mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
test: remove duplicate and theatrical tests (#3057)
* test: remove duplicate in-memory cache tests and fix missing cache reset Two tests verifying in-memory cache returns the same instance without re-fetching were duplicated across manifest.test.ts and manifest-cache-lifecycle.test.ts. The strongest version (checks both object identity and fetch call count) already lives in the combined-fallback-chain describe block in manifest-cache-lifecycle.test.ts, so the two weaker duplicates are removed. Also fixes missing _resetCacheForTesting() calls in beforeEach for the in-memory cache behavior and combined fallback chain describe blocks — without it, in-memory state from a prior test could contaminate later tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: remove duplicate and theatrical tests Consolidate 5 near-identical manifest rejection tests into a single data-driven loop, and collapse 4 identical logging-function smoke tests into a data-driven loop. Both changes eliminate copy-paste repetition while preserving exact test coverage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: spawn-qa-bot <qa@openrouter.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0bca96af58
commit
e8cf33daad
3 changed files with 69 additions and 110 deletions
|
|
@ -4,7 +4,7 @@ import type { TestEnvironment } from "./test-helpers";
|
|||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
|
||||
import { existsSync, mkdirSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { agentKeys, countImplemented, loadManifest } from "../manifest";
|
||||
import { _resetCacheForTesting, agentKeys, countImplemented, loadManifest } from "../manifest";
|
||||
import { createMockManifest, setupTestEnvironment, teardownTestEnvironment } from "./test-helpers";
|
||||
|
||||
/**
|
||||
|
|
@ -239,6 +239,7 @@ describe("Manifest Cache Lifecycle", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
env = setupTestEnvironment();
|
||||
_resetCacheForTesting();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -255,15 +256,6 @@ describe("Manifest Cache Lifecycle", () => {
|
|||
// fetch should have been called at least twice (once per forceRefresh)
|
||||
expect(fetchMock.mock.calls.length).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it("should return same instance without forceRefresh", async () => {
|
||||
global.fetch = mock(() => Promise.resolve(new Response(JSON.stringify(mockManifest))));
|
||||
|
||||
const manifest1 = await loadManifest(true);
|
||||
const manifest2 = await loadManifest(false);
|
||||
|
||||
expect(manifest1).toBe(manifest2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("combined fallback chain: invalid fetch + stale cache", () => {
|
||||
|
|
@ -271,6 +263,7 @@ describe("Manifest Cache Lifecycle", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
env = setupTestEnvironment();
|
||||
_resetCacheForTesting();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -171,15 +171,6 @@ describe("manifest", () => {
|
|||
expect(global.fetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns in-memory cache on second call without fetching", async () => {
|
||||
const fetchMock = mock(async () => new Response(JSON.stringify(mockManifest)));
|
||||
global.fetch = fetchMock;
|
||||
await loadManifest();
|
||||
const fetchCount = fetchMock.mock.calls.length;
|
||||
await loadManifest();
|
||||
expect(fetchMock.mock.calls.length).toBe(fetchCount);
|
||||
});
|
||||
|
||||
it("falls back to stale cache when fetch fails", async () => {
|
||||
const cacheDir = join(env.testDir, "spawn");
|
||||
mkdirSync(cacheDir, {
|
||||
|
|
@ -217,30 +208,22 @@ describe("manifest", () => {
|
|||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
});
|
||||
|
||||
it("throws when manifest from GitHub is invalid", async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(
|
||||
async () =>
|
||||
const invalidManifestCases: Array<{
|
||||
label: string;
|
||||
fetchImpl: () => Promise<Response>;
|
||||
}> = [
|
||||
{
|
||||
label: "non-manifest shape",
|
||||
fetchImpl: async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
not: "a manifest",
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("rejects manifest with string agents field", async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(
|
||||
async () =>
|
||||
},
|
||||
{
|
||||
label: "string agents field",
|
||||
fetchImpl: async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
agents: "claude",
|
||||
|
|
@ -248,21 +231,10 @@ describe("manifest", () => {
|
|||
matrix: {},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("rejects manifest with array clouds field", async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(
|
||||
async () =>
|
||||
},
|
||||
{
|
||||
label: "array clouds field",
|
||||
fetchImpl: async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
agents: {},
|
||||
|
|
@ -273,21 +245,10 @@ describe("manifest", () => {
|
|||
matrix: {},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("rejects manifest with numeric matrix field", async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(
|
||||
async () =>
|
||||
},
|
||||
{
|
||||
label: "numeric matrix field",
|
||||
fetchImpl: async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
agents: {},
|
||||
|
|
@ -295,31 +256,27 @@ describe("manifest", () => {
|
|||
matrix: 42,
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
{
|
||||
label: "network error",
|
||||
fetchImpl: async () => {
|
||||
throw new Error("Network timeout");
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("throws when network errors occur and no cache exists", async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(async () => {
|
||||
throw new Error("Network timeout");
|
||||
for (const { label, fetchImpl } of invalidManifestCases) {
|
||||
it(`rejects invalid manifest (${label})`, async () => {
|
||||
const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
|
||||
global.fetch = mock(fetchImpl);
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
const cacheFile = join(env.testDir, "spawn", "manifest.json");
|
||||
if (existsSync(cacheFile)) {
|
||||
rmSync(cacheFile);
|
||||
}
|
||||
|
||||
await expect(loadManifest(true)).rejects.toThrow("Cannot load manifest");
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -62,25 +62,34 @@ afterEach(() => {
|
|||
// ── Logging functions ──────────────────────────────────────────────
|
||||
|
||||
describe("logging functions", () => {
|
||||
it("logInfo writes green text to stderr", () => {
|
||||
logInfo("test info");
|
||||
expect(stderrOutput.join("")).toContain("test info");
|
||||
});
|
||||
|
||||
it("logWarn writes yellow text to stderr", () => {
|
||||
logWarn("test warn");
|
||||
expect(stderrOutput.join("")).toContain("test warn");
|
||||
});
|
||||
|
||||
it("logError writes red text to stderr", () => {
|
||||
logError("test error");
|
||||
expect(stderrOutput.join("")).toContain("test error");
|
||||
});
|
||||
|
||||
it("logStep writes cyan text to stderr", () => {
|
||||
logStep("test step");
|
||||
expect(stderrOutput.join("")).toContain("test step");
|
||||
});
|
||||
for (const [fn, msg] of [
|
||||
[
|
||||
logInfo,
|
||||
"test info",
|
||||
],
|
||||
[
|
||||
logWarn,
|
||||
"test warn",
|
||||
],
|
||||
[
|
||||
logError,
|
||||
"test error",
|
||||
],
|
||||
[
|
||||
logStep,
|
||||
"test step",
|
||||
],
|
||||
] satisfies Array<
|
||||
[
|
||||
(msg: string) => void,
|
||||
string,
|
||||
]
|
||||
>) {
|
||||
it(`${fn.name} writes message to stderr`, () => {
|
||||
fn(msg);
|
||||
expect(stderrOutput.join("")).toContain(msg);
|
||||
});
|
||||
}
|
||||
|
||||
it("logStepInline writes message (newline-terminated in non-TTY)", () => {
|
||||
logStepInline("inline msg");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue