From 65320abf05032d64962a9be27eef14dbf41bdce7 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Tue, 24 Mar 2026 08:32:53 -0700 Subject: [PATCH] refactor(test): extract shouldSkipCloudInit helper and add unit tests (#2958) Extracts the inline docker-mode condition from hetzner/main.ts and gcp/main.ts into a testable exported function in shared/cloud-init.ts, then adds real unit tests that import from the source. Fixes #2952. Agent: test-engineer Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- packages/cli/package.json | 2 +- packages/cli/src/__tests__/cloud-init.test.ts | 75 ++++++++++++++++++- packages/cli/src/gcp/main.ts | 8 +- packages/cli/src/hetzner/main.ts | 9 ++- packages/cli/src/shared/cloud-init.ts | 12 +++ 5 files changed, 102 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 146ae6c9..0052dd59 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "0.25.27", + "version": "0.25.28", "type": "module", "bin": { "spawn": "cli.js" diff --git a/packages/cli/src/__tests__/cloud-init.test.ts b/packages/cli/src/__tests__/cloud-init.test.ts index 3d55c8ab..55aa73d1 100644 --- a/packages/cli/src/__tests__/cloud-init.test.ts +++ b/packages/cli/src/__tests__/cloud-init.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { getPackagesForTier, needsBun, needsNode } from "../shared/cloud-init.js"; +import { getPackagesForTier, needsBun, needsNode, shouldSkipCloudInit } from "../shared/cloud-init.js"; describe("getPackagesForTier", () => { const MINIMAL_PACKAGES = [ @@ -113,3 +113,76 @@ describe("needsBun", () => { expect(needsBun()).toBe(true); }); }); + +describe("shouldSkipCloudInit", () => { + it("returns true when useDocker is true", () => { + expect( + shouldSkipCloudInit({ + useDocker: true, + }), + ).toBe(true); + }); + + it("returns true when snapshotId is a non-null string", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + snapshotId: "snap-123", + }), + ).toBe(true); + }); + + it("returns true when skipCloudInit is true", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + skipCloudInit: true, + }), + ).toBe(true); + }); + + it("returns false when all flags are off", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + }), + ).toBe(false); + }); + + it("returns false when snapshotId is null", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + snapshotId: null, + }), + ).toBe(false); + }); + + it("returns false when snapshotId is undefined", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + snapshotId: undefined, + }), + ).toBe(false); + }); + + it("returns false when skipCloudInit is false", () => { + expect( + shouldSkipCloudInit({ + useDocker: false, + skipCloudInit: false, + }), + ).toBe(false); + }); + + it("returns true when multiple flags are set", () => { + expect( + shouldSkipCloudInit({ + useDocker: true, + snapshotId: "snap-1", + skipCloudInit: true, + }), + ).toBe(true); + }); +}); diff --git a/packages/cli/src/gcp/main.ts b/packages/cli/src/gcp/main.ts index 417fd60a..9c0fab7f 100644 --- a/packages/cli/src/gcp/main.ts +++ b/packages/cli/src/gcp/main.ts @@ -5,6 +5,7 @@ import type { CloudOrchestrator } from "../shared/orchestrate.js"; import { getErrorMessage } from "@openrouter/spawn-shared"; +import { shouldSkipCloudInit } from "../shared/cloud-init.js"; import { DOCKER_CONTAINER_NAME, DOCKER_REGISTRY, runOrchestration } from "../shared/orchestrate.js"; import { logInfo, logStep, shellQuote } from "../shared/ui.js"; import { agents, resolveAgent } from "./agents.js"; @@ -85,7 +86,12 @@ async function main() { }, getServerName, async waitForReady() { - if (useDocker || cloud.skipCloudInit) { + if ( + shouldSkipCloudInit({ + useDocker, + skipCloudInit: cloud.skipCloudInit, + }) + ) { await waitForSshOnly(); } else { await waitForCloudInit(); diff --git a/packages/cli/src/hetzner/main.ts b/packages/cli/src/hetzner/main.ts index a083ab53..5b9ba96e 100644 --- a/packages/cli/src/hetzner/main.ts +++ b/packages/cli/src/hetzner/main.ts @@ -5,6 +5,7 @@ import type { CloudOrchestrator } from "../shared/orchestrate.js"; import { getErrorMessage } from "@openrouter/spawn-shared"; +import { shouldSkipCloudInit } from "../shared/cloud-init.js"; import { DOCKER_CONTAINER_NAME, DOCKER_REGISTRY, runOrchestration } from "../shared/orchestrate.js"; import { logInfo, logStep, shellQuote } from "../shared/ui.js"; import { agents, resolveAgent } from "./agents.js"; @@ -98,7 +99,13 @@ async function main() { }, getServerName, async waitForReady() { - if (useDocker || snapshotId || cloud.skipCloudInit) { + if ( + shouldSkipCloudInit({ + useDocker, + snapshotId, + skipCloudInit: cloud.skipCloudInit, + }) + ) { await waitForSshOnly(); } else { await waitForCloudInit(); diff --git a/packages/cli/src/shared/cloud-init.ts b/packages/cli/src/shared/cloud-init.ts index 43b85017..51506f9b 100644 --- a/packages/cli/src/shared/cloud-init.ts +++ b/packages/cli/src/shared/cloud-init.ts @@ -46,3 +46,15 @@ export function needsNode(tier: CloudInitTier = "full"): boolean { export function needsBun(tier: CloudInitTier = "full"): boolean { return tier === "bun" || tier === "full"; } + +/** + * Determines whether cloud-init wait should be skipped in favor of SSH-only wait. + * Extracted from the inline condition in hetzner/main.ts and gcp/main.ts. + */ +export function shouldSkipCloudInit(opts: { + useDocker: boolean; + snapshotId?: string | null | undefined; + skipCloudInit?: boolean; +}): boolean { + return opts.useDocker || opts.snapshotId != null || (opts.skipCloudInit ?? false); +}