test: remove duplicate and theatrical tests (#2826)

- delete commands-update-download.test.ts (7 tests): superseded by
  cmd-update-cov.test.ts which has 13 tests with better fallback URL
  coverage and uses clack mocks properly

- remove saveSpawnRecord id generation describe from history-cov.test.ts
  (1 test): superseded by history-spawn-id.test.ts which has 3 more
  thorough tests covering the same scenario

- remove 4 describe blocks from cmd-run-cov.test.ts (18 tests):
  getSignalGuidance, getScriptFailureGuidance, getScriptFailureGuidance
  additional, and getSignalGuidance additional are all covered more
  thoroughly by the dedicated script-failure-guidance.test.ts; the
  "additional" blocks were theatrical (only checked joined.length > 0)

- delete picker.test.ts and merge its 8 parsePickerInput tests into
  picker-cov.test.ts to eliminate duplicate describe name collision

2063 -> 2036 tests (-27), 0 failures

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
A 2026-03-20 06:11:57 -07:00 committed by GitHub
parent c323f10ae9
commit a7cebd4054
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 105 additions and 472 deletions

View file

@ -4,7 +4,7 @@
* Focuses on uncovered helper functions: resolveAndLog, detectAndFixSwappedArgs,
* dry-run helpers (buildAgentLines, buildCloudLines, buildCredentialStatusLines,
* buildEnvironmentLines, buildPromptLines), showDryRunPreview, classifyNetworkError,
* isRetryableExitCode, headless output/error, and execScript paths.
* isRetryableExitCode, and headless output/error paths.
*/
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test";
@ -14,14 +14,8 @@ import { createConsoleMocks, createMockManifest, mockClackPrompts, restoreMocks
const clack = mockClackPrompts();
const { cmdRun, cmdRunHeadless, getScriptFailureGuidance, getSignalGuidance, isRetryableExitCode } = await import(
"../commands/index.js"
);
const { showDryRunPreview, execScript } = await import("../commands/run.js");
function stripAnsi(s: string): string {
return s.replace(/\x1b\[[0-9;]*m/g, "");
}
const { cmdRun, cmdRunHeadless, isRetryableExitCode } = await import("../commands/index.js");
const { showDryRunPreview } = await import("../commands/run.js");
describe("commands/run.ts coverage", () => {
let consoleMocks: ReturnType<typeof createConsoleMocks>;
@ -122,162 +116,6 @@ describe("commands/run.ts coverage", () => {
});
});
// ── getSignalGuidance ──────────────────────────────────────────────────
describe("getSignalGuidance", () => {
it("returns guidance for SIGKILL", () => {
const lines = getSignalGuidance("SIGKILL").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("killed");
});
it("returns default guidance for unknown signal", () => {
const lines = getSignalGuidance("SIGUSR1").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("SIGUSR1");
expect(joined).toContain("terminated");
});
it("includes dashboard hint when provided", () => {
const lines = getSignalGuidance("SIGUSR1", "https://dash.example.com").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("https://dash.example.com");
});
});
// ── getScriptFailureGuidance ──────────────────────────────────────────
describe("getScriptFailureGuidance", () => {
it("returns default guidance for null exit code", () => {
const lines = getScriptFailureGuidance(null, "hetzner").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("Common causes");
});
it("returns default guidance for unknown exit codes", () => {
const lines = getScriptFailureGuidance(99, "hetzner").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("Common causes");
});
it("includes dashboard hint for exit code 130", () => {
const lines = getScriptFailureGuidance(130, "hetzner", undefined, "https://dash.test").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("interrupted");
});
it("includes credential hints for exit code 1", () => {
const lines = getScriptFailureGuidance(1, "hetzner", "Set HCLOUD_TOKEN").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("HCLOUD_TOKEN");
});
it("handles exit code 137 (OOM/killed)", () => {
const lines = getScriptFailureGuidance(137, "sprite").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("killed");
});
});
// ── classifyNetworkError (via reportDownloadError path) ──────────────
describe("classifyNetworkError", () => {
it("handles timeout error in dry run preview", () => {
showDryRunPreview(mockManifest, "claude", "sprite");
// Just verify it completes without throwing
expect(clack.logInfo).toHaveBeenCalled();
});
});
// ── showDryRunPreview with cloud defaults ──────────────────────────
describe("showDryRunPreview edge cases", () => {
it("shows credential status for sprite (token auth)", () => {
showDryRunPreview(mockManifest, "claude", "sprite");
const allCalls = consoleMocks.log.mock.calls.flat().map(String);
expect(allCalls.some((c) => c.includes("OPENROUTER_API_KEY"))).toBe(true);
});
it("shows credential status for hetzner (token auth)", () => {
showDryRunPreview(mockManifest, "claude", "hetzner");
const allCalls = consoleMocks.log.mock.calls.flat().map(String);
expect(allCalls.some((c) => c.includes("OPENROUTER_API_KEY"))).toBe(true);
});
it("shows no environment lines when agent has no env", () => {
const noEnvManifest = {
...mockManifest,
agents: {
...mockManifest.agents,
noenv: {
name: "NoEnv Agent",
description: "Agent without env",
url: "https://example.com",
install: "npm install noenv",
launch: "noenv",
},
},
matrix: {
...mockManifest.matrix,
"sprite/noenv": "implemented",
},
};
global.fetch = mock(async () => new Response(JSON.stringify(noEnvManifest)));
showDryRunPreview(noEnvManifest, "noenv", "sprite");
expect(clack.logSuccess).toHaveBeenCalled();
});
});
// ── getScriptFailureGuidance edge cases ───────────────────────────
describe("getScriptFailureGuidance additional", () => {
it("handles exit code 2 (misuse of shell builtin)", () => {
const lines = getScriptFailureGuidance(2, "sprite").map(stripAnsi);
const joined = lines.join("\n");
expect(joined.length).toBeGreaterThan(0);
});
it("handles exit code 126 (permission denied)", () => {
const lines = getScriptFailureGuidance(126, "hetzner").map(stripAnsi);
const joined = lines.join("\n");
expect(joined.length).toBeGreaterThan(0);
});
it("handles exit code 127 (command not found)", () => {
const lines = getScriptFailureGuidance(127, "hetzner").map(stripAnsi);
const joined = lines.join("\n");
expect(joined.length).toBeGreaterThan(0);
});
it("handles exit code 255 (SSH error)", () => {
const lines = getScriptFailureGuidance(255, "aws").map(stripAnsi);
const joined = lines.join("\n");
expect(joined.length).toBeGreaterThan(0);
});
it("handles exit code 1 with no auth hint", () => {
const lines = getScriptFailureGuidance(1, "sprite").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("Cloud provider API error");
});
});
// ── getSignalGuidance additional ──────────────────────────────────
describe("getSignalGuidance additional", () => {
it("returns guidance for SIGTERM", () => {
const lines = getSignalGuidance("SIGTERM").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("terminated");
});
it("returns guidance for unknown signal without dashboard", () => {
const lines = getSignalGuidance("SIGXCPU").map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("SIGXCPU");
});
});
// ── cmdRun additional ─────────────────────────────────────────────
describe("cmdRun validation", () => {

View file

@ -1,183 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test";
import { isString } from "@openrouter/spawn-shared";
import pkg from "../../package.json" with { type: "json" };
import { createConsoleMocks, mockClackPrompts, restoreMocks } from "./test-helpers";
const VERSION = pkg.version;
/**
* Tests for cmdUpdate (commands/update.ts).
*
* Uses dependency injection (UpdateOptions.runUpdate) instead of mock.module
* for node:child_process to avoid process-global mock pollution.
*/
const { spinnerStart: mockSpinnerStart, spinnerStop: mockSpinnerStop } = mockClackPrompts();
// ── Import commands directly (no mock.module needed) ──────────────────────
import { cmdUpdate } from "../commands/index.js";
/** No-op runUpdate to prevent real subprocess calls in tests. */
const mockRunUpdate = mock(() => {});
describe("cmdUpdate", () => {
let consoleMocks: ReturnType<typeof createConsoleMocks>;
let originalFetch: typeof global.fetch;
let processExitSpy: ReturnType<typeof spyOn>;
beforeEach(async () => {
consoleMocks = createConsoleMocks();
mockSpinnerStart.mockClear();
mockSpinnerStop.mockClear();
mockRunUpdate.mockClear();
processExitSpy = spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit");
});
originalFetch = global.fetch;
});
afterEach(() => {
global.fetch = originalFetch;
processExitSpy.mockRestore();
restoreMocks(consoleMocks.log, consoleMocks.error);
});
it("should report up-to-date when remote version matches current", async () => {
global.fetch = mock(async (url: string) => {
if (isString(url) && url.includes("/version")) {
return new Response(`${VERSION}\n`);
}
return new Response("Not Found", {
status: 404,
});
});
await cmdUpdate({
runUpdate: mockRunUpdate,
});
expect(mockSpinnerStart).toHaveBeenCalled();
expect(mockSpinnerStop).toHaveBeenCalled();
// The spinner stop message should indicate up-to-date
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("up to date"))).toBe(true);
});
it("should report available update when remote version differs", async () => {
global.fetch = mock(async (url: string) => {
if (isString(url) && url.includes("/version")) {
return new Response("99.99.99\n");
}
return new Response("Not Found", {
status: 404,
});
});
await cmdUpdate({
runUpdate: mockRunUpdate,
});
expect(mockSpinnerStart).toHaveBeenCalled();
// Should show update message with version transition
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("99.99.99"))).toBe(true);
});
it("should handle package.json fetch failure gracefully", async () => {
global.fetch = mock(
async () =>
new Response("Internal Server Error", {
status: 500,
}),
);
await cmdUpdate({
runUpdate: mockRunUpdate,
});
expect(mockSpinnerStart).toHaveBeenCalled();
// Should show failed message
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("Failed"))).toBe(true);
// Should output error details
const errorOutput = consoleMocks.error.mock.calls.map((c: unknown[]) => c.join(" ")).join("\n");
expect(errorOutput).toContain("Error:");
});
it("should handle network error gracefully", async () => {
global.fetch = mock(async () => {
throw new TypeError("Failed to fetch");
});
await cmdUpdate({
runUpdate: mockRunUpdate,
});
expect(mockSpinnerStart).toHaveBeenCalled();
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("Failed"))).toBe(true);
});
it("should handle update failure gracefully", async () => {
global.fetch = mock(async (url: string) => {
if (isString(url) && url.includes("/version")) {
return new Response("99.99.99\n");
}
return new Response("Not Found", {
status: 404,
});
});
// Mock runUpdate that throws to simulate failure
const failingRunUpdate = mock(() => {
throw new Error("curl failed");
});
await cmdUpdate({
runUpdate: failingRunUpdate,
});
// Should show the update version in spinner stop
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("99.99.99"))).toBe(true);
});
it("should start spinner with checking message", async () => {
global.fetch = mock(
async () =>
new Response(
JSON.stringify({
version: VERSION,
}),
),
);
await cmdUpdate({
runUpdate: mockRunUpdate,
});
const startCalls = mockSpinnerStart.mock.calls.map((c: unknown[]) => c.join(" "));
expect(startCalls.some((msg: string) => msg.includes("Checking"))).toBe(true);
});
it("should show version in spinner stop during update", async () => {
global.fetch = mock(async (url: string) => {
if (isString(url) && url.includes("/version")) {
return new Response("2.0.0\n");
}
return new Response("Error", {
status: 500,
});
});
await cmdUpdate({
runUpdate: mockRunUpdate,
});
// cmdUpdate now uses s.stop() with version info instead of s.message()
const stopCalls = mockSpinnerStop.mock.calls.map((c: unknown[]) => c.join(" "));
expect(stopCalls.some((msg: string) => msg.includes("2.0.0"))).toBe(true);
});
});

View file

@ -662,24 +662,6 @@ describe("history.ts coverage", () => {
});
});
// ── saveSpawnRecord auto-generates id ─────────────────────────────────
describe("saveSpawnRecord id generation", () => {
it("auto-generates id if not provided", () => {
const recordWithoutId: SpawnRecord = {
id: "",
agent: "claude",
cloud: "sprite",
timestamp: "2026-01-01T00:00:00Z",
};
saveSpawnRecord(recordWithoutId);
const data = JSON.parse(readFileSync(join(testDir, "history.json"), "utf-8"));
expect(data.records[0].id).toBeTruthy();
expect(typeof data.records[0].id).toBe("string");
});
});
// ── Trimming with archiving ───────────────────────────────────────────
describe("trimming and archiving", () => {

View file

@ -14,7 +14,108 @@ describe("picker.ts coverage", () => {
// ── parsePickerInput extended ─────────────────────────────────────────
describe("parsePickerInput", () => {
it("handles tabs within values", () => {
it("parses three-field tab-separated lines (value, label, hint)", () => {
const result = parsePickerInput("us-east-1\tVirginia\tRecommended");
expect(result).toEqual([
{
value: "us-east-1",
label: "Virginia",
hint: "Recommended",
},
]);
});
it("parses two-field lines (value, label) with no hint", () => {
const result = parsePickerInput("us-east-1\tVirginia");
expect(result).toEqual([
{
value: "us-east-1",
label: "Virginia",
},
]);
});
it("uses value as label when only value is provided", () => {
const result = parsePickerInput("us-east-1");
expect(result).toEqual([
{
value: "us-east-1",
label: "us-east-1",
},
]);
});
it("filters empty and whitespace-only lines", () => {
const result = parsePickerInput("a\tAlpha\n\n \nb\tBeta\n");
expect(result).toEqual([
{
value: "a",
label: "Alpha",
},
{
value: "b",
label: "Beta",
},
]);
});
it("handles mixed field counts in a single input", () => {
const input = [
"val1\tLabel1\tHint1",
"val2\tLabel2",
"val3",
].join("\n");
const result = parsePickerInput(input);
expect(result).toEqual([
{
value: "val1",
label: "Label1",
hint: "Hint1",
},
{
value: "val2",
label: "Label2",
},
{
value: "val3",
label: "val3",
},
]);
});
it("returns empty array for empty input", () => {
expect(parsePickerInput("")).toEqual([]);
expect(parsePickerInput(" ")).toEqual([]);
expect(parsePickerInput("\n\n")).toEqual([]);
});
it("trims whitespace from fields", () => {
const result = parsePickerInput(" val \t Label \t Hint ");
expect(result).toEqual([
{
value: "val",
label: "Label",
hint: "Hint",
},
]);
});
it("parses multiple lines correctly", () => {
const input = "us-central1-a\tIowa\nus-east1-b\tVirginia";
const result = parsePickerInput(input);
expect(result).toEqual([
{
value: "us-central1-a",
label: "Iowa",
},
{
value: "us-east1-b",
label: "Virginia",
},
]);
});
it("handles tabs within values (extra fields beyond 3 are ignored)", () => {
const result = parsePickerInput("a\tb\tc\td");
expect(result).toHaveLength(1);
expect(result[0].value).toBe("a");

View file

@ -1,105 +0,0 @@
import { describe, expect, it } from "bun:test";
import { parsePickerInput } from "../picker";
describe("parsePickerInput", () => {
it("parses three-field tab-separated lines (value, label, hint)", () => {
const result = parsePickerInput("us-east-1\tVirginia\tRecommended");
expect(result).toEqual([
{
value: "us-east-1",
label: "Virginia",
hint: "Recommended",
},
]);
});
it("parses two-field lines (value, label) with no hint", () => {
const result = parsePickerInput("us-east-1\tVirginia");
expect(result).toEqual([
{
value: "us-east-1",
label: "Virginia",
},
]);
});
it("uses value as label when only value is provided", () => {
const result = parsePickerInput("us-east-1");
expect(result).toEqual([
{
value: "us-east-1",
label: "us-east-1",
},
]);
});
it("filters empty and whitespace-only lines", () => {
const result = parsePickerInput("a\tAlpha\n\n \nb\tBeta\n");
expect(result).toEqual([
{
value: "a",
label: "Alpha",
},
{
value: "b",
label: "Beta",
},
]);
});
it("handles mixed field counts in a single input", () => {
const input = [
"val1\tLabel1\tHint1",
"val2\tLabel2",
"val3",
].join("\n");
const result = parsePickerInput(input);
expect(result).toEqual([
{
value: "val1",
label: "Label1",
hint: "Hint1",
},
{
value: "val2",
label: "Label2",
},
{
value: "val3",
label: "val3",
},
]);
});
it("returns empty array for empty input", () => {
expect(parsePickerInput("")).toEqual([]);
expect(parsePickerInput(" ")).toEqual([]);
expect(parsePickerInput("\n\n")).toEqual([]);
});
it("trims whitespace from fields", () => {
const result = parsePickerInput(" val \t Label \t Hint ");
expect(result).toEqual([
{
value: "val",
label: "Label",
hint: "Hint",
},
]);
});
it("parses multiple lines correctly", () => {
const input = "us-central1-a\tIowa\nus-east1-b\tVirginia";
const result = parsePickerInput(input);
expect(result).toEqual([
{
value: "us-central1-a",
label: "Iowa",
},
{
value: "us-east1-b",
label: "Virginia",
},
]);
});
});