diff --git a/packages/cli/src/__tests__/commands-error-paths.test.ts b/packages/cli/src/__tests__/commands-error-paths.test.ts index b2a724c0..ab250ee8 100644 --- a/packages/cli/src/__tests__/commands-error-paths.test.ts +++ b/packages/cli/src/__tests__/commands-error-paths.test.ts @@ -330,51 +330,6 @@ describe("Commands Error Paths", () => { }); }); - // ── cmdRun: swapped arguments detection ────────────────────────────── - - describe("cmdRun - swapped arguments detection", () => { - it("should detect when cloud and agent arguments are swapped", async () => { - // "spawn sprite claude" should detect that sprite is a cloud and claude is an agent - await expect(cmdRun("sprite", "claude")).rejects.toThrow("process.exit"); - expect(processExitSpy).toHaveBeenCalledWith(1); - - const infoCalls = mockLogInfo.mock.calls.map((c: any[]) => c.join(" ")); - expect(infoCalls.some((msg: string) => msg.includes("swapped"))).toBe(true); - }); - - it("should suggest the correct argument order when swapped", async () => { - await expect(cmdRun("sprite", "claude")).rejects.toThrow("process.exit"); - - const infoCalls = mockLogInfo.mock.calls.map((c: any[]) => c.join(" ")); - expect(infoCalls.some((msg: string) => msg.includes("spawn claude sprite"))).toBe(true); - }); - - it("should suggest correct order for hetzner/codex swap", async () => { - await expect(cmdRun("hetzner", "codex")).rejects.toThrow("process.exit"); - - const infoCalls2 = mockLogInfo.mock.calls.map((c: any[]) => c.join(" ")); - expect(infoCalls2.some((msg: string) => msg.includes("swapped"))).toBe(true); - - const infoCalls = mockLogInfo.mock.calls.map((c: any[]) => c.join(" ")); - expect(infoCalls.some((msg: string) => msg.includes("spawn codex hetzner"))).toBe(true); - }); - - it("should NOT trigger swap detection when both args are unknown", async () => { - await expect(cmdRun("unknown1", "unknown2")).rejects.toThrow("process.exit"); - - const warnCalls = mockLogWarn.mock.calls.map((c: any[]) => c.join(" ")); - expect(warnCalls.some((msg: string) => msg.includes("swapped"))).toBe(false); - }); - - it("should NOT trigger swap detection when agent is valid", async () => { - // "spawn claude nonexistent" - agent is valid, cloud is not - await expect(cmdRun("claude", "nonexistent")).rejects.toThrow("process.exit"); - - const warnCalls = mockLogWarn.mock.calls.map((c: any[]) => c.join(" ")); - expect(warnCalls.some((msg: string) => msg.includes("swapped"))).toBe(false); - }); - }); - // ── cmdRun: batch validation (both errors at once) ────────────────── describe("cmdRun - batch validation shows all errors at once", () => { diff --git a/packages/cli/src/__tests__/manifest-integrity.test.ts b/packages/cli/src/__tests__/manifest-integrity.test.ts index 21bcbb65..343c2b8a 100644 --- a/packages/cli/src/__tests__/manifest-integrity.test.ts +++ b/packages/cli/src/__tests__/manifest-integrity.test.ts @@ -53,38 +53,16 @@ describe("Manifest Integrity", () => { }); // ── Agent definitions ─────────────────────────────────────────────── + // Field type precision is covered by manifest-type-contracts.test.ts. + // Only naming conventions and uniqueness constraints live here. describe("agent definitions", () => { - it("should have required fields for every agent", () => { - for (const [key, agent] of Object.entries(manifest.agents)) { - expect(agent.name).toBeTruthy(); - expect(agent.description).toBeTruthy(); - expect(agent.url).toBeTruthy(); - expect(agent.install).toBeTruthy(); - expect(agent.launch).toBeTruthy(); - expect(agent.env).toBeTruthy(); - } - }); - it("should use lowercase-hyphen-underscore keys for agents", () => { for (const key of agents) { expect(key).toMatch(/^[a-z0-9_-]+$/); } }); - it("should have valid URL format for agent urls", () => { - for (const [key, agent] of Object.entries(manifest.agents)) { - expect(agent.url).toMatch(/^https?:\/\//); - } - }); - - it("should have env as an object for every agent", () => { - for (const [key, agent] of Object.entries(manifest.agents)) { - expect(typeof agent.env).toBe("object"); - expect(agent.env).not.toBeNull(); - } - }); - it("should have unique agent display names", () => { const names = Object.values(manifest.agents).map((a) => a.name); const uniqueNames = new Set(names); @@ -93,33 +71,16 @@ describe("Manifest Integrity", () => { }); // ── Cloud definitions ─────────────────────────────────────────────── + // Field type precision is covered by manifest-type-contracts.test.ts. + // Only naming conventions and uniqueness constraints live here. describe("cloud definitions", () => { - it("should have required fields for every cloud", () => { - for (const [key, cloud] of Object.entries(manifest.clouds)) { - expect(cloud.name).toBeTruthy(); - expect(cloud.description).toBeTruthy(); - expect(cloud.url).toBeTruthy(); - expect(cloud.type).toBeTruthy(); - expect(cloud.auth).toBeTruthy(); - expect(cloud.provision_method).toBeTruthy(); - expect(cloud.exec_method).toBeTruthy(); - expect(cloud.interactive_method).toBeTruthy(); - } - }); - it("should use lowercase-hyphen-underscore keys for clouds", () => { for (const key of clouds) { expect(key).toMatch(/^[a-z0-9_-]+$/); } }); - it("should have valid URL format for cloud urls", () => { - for (const [key, cloud] of Object.entries(manifest.clouds)) { - expect(cloud.url).toMatch(/^https?:\/\//); - } - }); - it("should have unique cloud display names", () => { const names = Object.values(manifest.clouds).map((c) => c.name); const uniqueNames = new Set(names); diff --git a/packages/cli/src/__tests__/manifest-type-contracts.test.ts b/packages/cli/src/__tests__/manifest-type-contracts.test.ts index 5cd46c19..406a5538 100644 --- a/packages/cli/src/__tests__/manifest-type-contracts.test.ts +++ b/packages/cli/src/__tests__/manifest-type-contracts.test.ts @@ -109,16 +109,6 @@ describe("Agent optional field types (when present)", () => { }); } - if (agent.deps !== undefined) { - it(`agent "${key}" deps should be an array of strings`, () => { - expect(Array.isArray(agent.deps)).toBe(true); - for (const dep of agent.deps!) { - expect(typeof dep).toBe("string"); - expect(dep.length).toBeGreaterThan(0); - } - }); - } - if (agent.config_files !== undefined) { it(`agent "${key}" config_files should be an object with string keys`, () => { expect(typeof agent.config_files).toBe("object"); @@ -290,24 +280,6 @@ describe("Agent launch command consistency", () => { }); }); -// ── Dotenv path validation ──────────────────────────────────────────────── - -describe("Dotenv configuration", () => { - for (const [key, agent] of allAgents.filter(([, a]) => a.dotenv !== undefined)) { - it(`agent "${key}" dotenv path should look like a file path`, () => { - const path = agent.dotenv!.path; - // Should contain a / or ~ indicating a path - expect(path).toMatch(/[/~]/); - }); - - it(`agent "${key}" dotenv values should all be strings`, () => { - for (const [k, v] of Object.entries(agent.dotenv!.values)) { - expect(typeof v).toBe("string"); - } - }); - } -}); - // ── Interactive prompts structure ───────────────────────────────────────── describe("Interactive prompts structure", () => { diff --git a/packages/cli/src/__tests__/manifest.test.ts b/packages/cli/src/__tests__/manifest.test.ts index 840a096b..81b783c5 100644 --- a/packages/cli/src/__tests__/manifest.test.ts +++ b/packages/cli/src/__tests__/manifest.test.ts @@ -123,9 +123,6 @@ describe("manifest", () => { signal: expect.any(AbortSignal), }), ); - - // The manifest was fetched and returned successfully - expect(manifest.agents).toBeDefined(); }); it("should use disk cache when fresh", async () => { diff --git a/packages/cli/src/__tests__/run-path-credential-display.test.ts b/packages/cli/src/__tests__/run-path-credential-display.test.ts index 50733f2f..86e593c1 100644 --- a/packages/cli/src/__tests__/run-path-credential-display.test.ts +++ b/packages/cli/src/__tests__/run-path-credential-display.test.ts @@ -151,8 +151,6 @@ mock.module("@clack/prompts", () => ({ // Import after mocks are set up const { prioritizeCloudsByCredentials, - getImplementedClouds, - getImplementedAgents, checkEntity, resolveAgentKey, resolveCloudKey, @@ -463,47 +461,6 @@ describe("key resolution for run path", () => { }); }); -// ── getImplementedClouds / getImplementedAgents for run path ───────────── - -describe("implementation checks for run path", () => { - const manifest = makeManifest(); - - it("should return implemented clouds for claude", () => { - const clouds = getImplementedClouds(manifest, "claude"); - expect(clouds).toContain("hetzner"); - expect(clouds).toContain("sprite"); - expect(clouds).toContain("digitalocean"); - expect(clouds).toContain("upcloud"); - expect(clouds).toContain("localcloud"); - }); - - it("should return implemented clouds for codex (fewer)", () => { - const clouds = getImplementedClouds(manifest, "codex"); - expect(clouds).toContain("hetzner"); - expect(clouds).toContain("digitalocean"); - expect(clouds).toContain("localcloud"); - // sprite/codex and upcloud/codex are "missing" - expect(clouds).not.toContain("sprite"); - expect(clouds).not.toContain("upcloud"); - }); - - it("should return implemented agents for hetzner", () => { - const agents = getImplementedAgents(manifest, "hetzner"); - expect(agents).toContain("claude"); - expect(agents).toContain("codex"); - }); - - it("should return empty for nonexistent agent", () => { - const clouds = getImplementedClouds(manifest, "nonexistent"); - expect(clouds).toEqual([]); - }); - - it("should return empty for nonexistent cloud", () => { - const agents = getImplementedAgents(manifest, "nonexistent"); - expect(agents).toEqual([]); - }); -}); - // ── Integration: full run-path validation sequence ────────────────────── describe("run-path validation sequence integration", () => {