test: cover beta fallback update logic

This commit is contained in:
Peter Steinberger
2026-01-20 16:28:28 +00:00
parent 3d5ffee07f
commit cb5d76ed3d
4 changed files with 170 additions and 41 deletions

View File

@@ -27,6 +27,7 @@ vi.mock("../infra/update-check.js", async () => {
...actual,
checkUpdateStatus: vi.fn(),
fetchNpmTagVersion: vi.fn(),
resolveNpmChannelTag: vi.fn(),
};
});
@@ -38,11 +39,6 @@ vi.mock("../commands/doctor.js", () => ({
vi.mock("./daemon-cli.js", () => ({
runDaemonRestart: vi.fn(),
}));
// Mock plugin update helpers
vi.mock("../plugins/update.js", () => ({
syncPluginsForUpdateChannel: vi.fn(),
updateNpmInstalledPlugins: vi.fn(),
}));
// Mock the runtime
vi.mock("../runtime.js", () => ({
@@ -78,15 +74,18 @@ describe("update-cli", () => {
vi.clearAllMocks();
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { readConfigFileSnapshot } = await import("../config/config.js");
const { checkUpdateStatus, fetchNpmTagVersion } = await import("../infra/update-check.js");
const { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } =
await import("../plugins/update.js");
const { checkUpdateStatus, fetchNpmTagVersion, resolveNpmChannelTag } =
await import("../infra/update-check.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(process.cwd());
vi.mocked(readConfigFileSnapshot).mockResolvedValue(baseSnapshot);
vi.mocked(fetchNpmTagVersion).mockResolvedValue({
tag: "latest",
version: "9999.0.0",
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "9999.0.0",
});
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: "/test/path",
installKind: "git",
@@ -112,16 +111,6 @@ describe("update-cli", () => {
latestVersion: "1.2.3",
},
});
vi.mocked(syncPluginsForUpdateChannel).mockResolvedValue({
config: baseSnapshot.config,
changed: false,
summary: { switchedToBundled: [], switchedToNpm: [], warnings: [], errors: [] },
});
vi.mocked(updateNpmInstalledPlugins).mockResolvedValue({
config: baseSnapshot.config,
changed: false,
outcomes: [],
});
setTty(false);
setStdoutTty(false);
});
@@ -163,25 +152,6 @@ describe("update-cli", () => {
expect(defaultRuntime.log).toHaveBeenCalled();
});
it("updateCommand syncs plugins after a successful update", async () => {
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } =
await import("../plugins/update.js");
const { updateCommand } = await import("./update-cli.js");
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "git",
steps: [],
durationMs: 100,
});
await updateCommand({});
expect(syncPluginsForUpdateChannel).toHaveBeenCalled();
expect(updateNpmInstalledPlugins).toHaveBeenCalled();
});
it("updateStatusCommand prints table output", async () => {
const { defaultRuntime } = await import("../runtime.js");
const { updateStatusCommand } = await import("./update-cli.js");
@@ -274,6 +244,47 @@ describe("update-cli", () => {
expect(call?.channel).toBe("beta");
});
it("falls back to latest when beta tag is older than release", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2026.1.18-1" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { readConfigFileSnapshot } = await import("../config/config.js");
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { updateCommand } = await import("./update-cli.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(readConfigFileSnapshot).mockResolvedValue({
...baseSnapshot,
config: { update: { channel: "beta" } },
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.1.20-1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
await updateCommand({});
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe("beta");
expect(call?.tag).toBe("latest");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
it("honors --tag override", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
try {
@@ -443,13 +454,13 @@ describe("update-cli", () => {
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { fetchNpmTagVersion } = await import("../infra/update-check.js");
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { defaultRuntime } = await import("../runtime.js");
const { updateCommand } = await import("./update-cli.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(fetchNpmTagVersion).mockResolvedValue({
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});