Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
"test:system-messages:vitest": "pnpm exec vitest run test/systemMessages.test.ts",
"test:tool-routing-semantic:vitest": "pnpm exec vitest run test/toolRoutingSemantic.test.ts",
"test:tool-routing-semantic": "pnpm run test:tool-routing-semantic:vitest && npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
"test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop && pnpm run test:model-capabilities:vitest && pnpm run test:agent-runtime:vitest && pnpm run test:retry-after:vitest",
"test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop && pnpm run test:model-capabilities:vitest && pnpm run test:agent-runtime:vitest && pnpm run test:retry-after:vitest && pnpm run test:proxy:vitest && pnpm run test:knowledge-grounding:vitest && pnpm run test:mcp-result-cache:vitest && pnpm run test:prompt-redaction:vitest",
"// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit)": "",
"test:live": "pnpm run test:providers && pnpm run test:mcp:http && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:observability && pnpm run test:context && pnpm run test:memory && pnpm run test:tool-reliability && pnpm run test:evaluation && pnpm run test:autoresearch && pnpm run test:credentials && pnpm run test:dynamic",
"// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",
Expand Down Expand Up @@ -218,7 +218,11 @@
"pre-push": "pnpm run validate:commit && pnpm run validate:env && pnpm run validate && pnpm run test:ci",
"check:all": "pnpm run lint && pnpm run format --check && pnpm run validate && pnpm run validate:commit",
"test:litellm-context:vitest": "pnpm exec vitest run test/litellmContextWindows.test.ts",
"test:step-budget-guard:vitest": "pnpm exec vitest run test/stepBudgetGuard.test.ts"
"test:step-budget-guard:vitest": "pnpm exec vitest run test/stepBudgetGuard.test.ts",
"test:proxy:vitest": "pnpm exec vitest run test/proxyAnalysis.test.ts test/proxyConfigHotReload.test.ts test/proxyObservabilityFoundation.test.ts test/proxyReliabilityHardening.test.ts test/proxyReplay.test.ts test/proxyRollingWorkerHandoff.test.ts test/proxyTerminalErrorAccounting.test.ts test/proxyUpdaterFallback.test.ts test/proxyUsageStatsPersistence.test.ts",
"test:knowledge-grounding:vitest": "pnpm exec vitest run test/knowledgeGrounding.test.ts",
"test:mcp-result-cache:vitest": "pnpm exec vitest run test/mcpResultCacheErrorSkip.test.ts",
"test:prompt-redaction:vitest": "pnpm exec vitest run test/promptRedaction.test.ts"
},
"files": [
"dist",
Expand Down
46 changes: 46 additions & 0 deletions scripts/build-validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,51 @@ class NeuroLinkBuildValidator {
this.log("Project structure validation completed");
}

// Every test/*.test.ts must be reachable from a package.json script.
//
// A Vitest file that no script names is never executed by anything: not
// `pnpm test`, not `test:unit`, not a developer following the docs. It looks
// like coverage in review and silently rots — twelve files had drifted into
// that state, one of them holding a genuine failing assertion nobody had seen.
checkOrphanedTestFiles(): void {
this.log("Validating that every test file is reachable from a script...");

const testDir = path.join(this.rootDir, "test");
if (!fs.existsSync(testDir)) {
this.warnings.push("No test/ directory found; skipping orphan check");
return;
}

const packageJson = this.readFileWithCache(
path.join(this.rootDir, "package.json"),
);
if (!packageJson) {
this.errors.push("Could not read package.json to check test wiring");
return;
}
// Match against the scripts block alone: a filename appearing in some other
// field would otherwise count as "wired" without running anything.
const scripts = JSON.stringify(
(JSON.parse(packageJson) as { scripts?: Record<string, string> })
.scripts ?? {},
);

const orphans = fs
.readdirSync(testDir)
.filter((name) => name.endsWith(".test.ts"))
.filter((name) => !scripts.includes(name));
Comment on lines +530 to +540

if (orphans.length > 0) {
this.errors.push(
`${orphans.length} test file(s) are not referenced by any package.json script, ` +
`so nothing runs them: ${orphans.join(", ")}. ` +
`Add each to a "test:<name>:vitest" script and chain it into "test:unit".`,
);
}

this.log(`Test wiring check completed (${orphans.length} orphaned)`);
}

// Main validation runner
run(): void {
console.log("Running NeuroLink Build Validations...\n");
Expand All @@ -520,6 +565,7 @@ class NeuroLinkBuildValidator {
this.checkErrorHandling();
this.checkTodoReferences();
this.checkEnvironmentConfig();
this.checkOrphanedTestFiles();

const endTime = Date.now();
const duration = ((endTime - startTime) / 1000).toFixed(2);
Expand Down
52 changes: 52 additions & 0 deletions test/proxyTerminalErrorAccounting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ describe("translated terminal accounting", () => {
const modelRouter = {
resolve: () => ({ provider: "openai", model: "translated-model" }),
getFallbackChain: () => [],
// Auto-fallback became opt-in in "fix(proxy): bound account admission and
// fallback routing" (33bdd141). This test is about the SECOND attempt not
// finalizing the terminal error a second time, so it has to opt in — a
// router without this reports one attempt and never reaches the path
// under test. Default-off behaviour is pinned by the test below.
isAutoFallbackEnabled: () => true,
};
const messagesRoute = createClaudeProxyRoutes(
modelRouter as never,
Expand All @@ -278,6 +284,7 @@ describe("translated terminal accounting", () => {
type: "error",
});

// Two attempts, but the terminal error is finalized once.
expect(getStats()).toMatchObject({
totalAttempts: 2,
totalAttemptErrors: 2,
Expand All @@ -298,6 +305,51 @@ describe("translated terminal accounting", () => {
});
});

it("makes no second attempt when auto-fallback is left disabled", async () => {
// The default since 33bdd141: without an explicit opt-in, the translation
// layer must not pick a fallback provider on its own. This is the behaviour
// that silently changed the test above from passing to failing while the
// file was orphaned, so it gets pinned rather than left implicit.
await startRequestLogCapture();
const ctx = createContext("translated-route-no-autofallback", async () => ({
stream: (async function* () {
yield* [];
})(),
toolCalls: [],
usage: {},
model: "translated-model",
})) as ServerContext & { body: Record<string, unknown> };
ctx.body = {
model: "claude-routed-model",
messages: [{ role: "user", content: "hello" }],
};
const modelRouter = {
resolve: () => ({ provider: "openai", model: "translated-model" }),
getFallbackChain: () => [],
isAutoFallbackEnabled: () => false,
};
const messagesRoute = createClaudeProxyRoutes(
modelRouter as never,
).routes.find(
(route) => route.method === "POST" && route.path === "/v1/messages",
);
if (!messagesRoute) {
throw new Error("messages route not found");
}

await expect(messagesRoute.handler(ctx)).resolves.toMatchObject({
type: "error",
});

// One attempt, not two — and still exactly one finalized terminal error.
expect(getStats()).toMatchObject({
totalAttempts: 1,
totalAttemptErrors: 1,
totalRequests: 1,
totalErrors: 1,
});
});

it("records client cancellation once without marking the stream successful", async () => {
const logDir = await startRequestLogCapture();
let resolvePendingRead!: (value: IteratorResult<unknown>) => void;
Expand Down
Loading