test(scripts): replace brittle temp-dir importer inventory with structural routing assertions

The 'routes top-level test helpers to importing repo tests' test froze the
full list of tests importing test/helpers/temp-dir.ts; any new importer broke
main's checks shard (twice on 2026-07-05). Assert routing structure instead:
scan-derived direct-importer lower bound, partition invariants, and per-file
consistency with single-target routing.
This commit is contained in:
Peter Steinberger
2026-07-05 17:41:37 +01:00
parent 2b7003d535
commit b4e0c5306c
+63 -161
View File
@@ -1,4 +1,5 @@
// Test project script tests cover fixture project discovery and validation.
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
@@ -854,167 +855,68 @@ describe("test-projects args", () => {
});
it("routes top-level test helpers to importing repo tests", () => {
expect(buildVitestRunPlans(["test/helpers/temp-dir.ts"])).toEqual([
{
config: "test/vitest/vitest.unit-fast.config.ts",
forwardedArgs: [],
includePatterns: [
"src/agents/command/attempt-execution.shared.test.ts",
"src/auto-reply/reply/session-entry-persistence.test.ts",
"src/crestodian/operations.test.ts",
"src/install-sh-version.test.ts",
"src/proxy-capture/store.sqlite.test.ts",
"test/scripts/android-version.test.ts",
"test/scripts/resolve-openclaw-ref.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.unit-fast-fake-timers.config.ts",
forwardedArgs: [],
includePatterns: ["src/entry.compile-cache.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.unit.config.ts",
forwardedArgs: [
"src/state/openclaw-agent-db.test.ts",
"src/state/openclaw-state-db.test.ts",
"src/state/sqlite-query-plan.test.ts",
"src/transcripts/store.test.ts",
],
includePatterns: null,
watchMode: false,
},
{
config: "test/vitest/vitest.tooling.config.ts",
forwardedArgs: [],
includePatterns: [
"src/scripts/docs-link-audit.test.ts",
"src/scripts/sync-plugin-versions.test.ts",
"test/e2e/qa-lab/runtime/gateway-mcp-real-transports.test.ts",
"test/helpers/temp-dir.test.ts",
"test/scripts/android-pin-version.test.ts",
"test/scripts/bench-cli-startup.test.ts",
"test/scripts/check-package-dist-imports.test.ts",
"test/scripts/check-workflows.test.ts",
"test/scripts/ci-hydrate-testbox-env.test.ts",
"test/scripts/clawhub-fixture-server.test.ts",
"test/scripts/codex-install-assertions.test.ts",
"test/scripts/config-reload-mutate-metadata.test.ts",
"test/scripts/control-ui-i18n.test.ts",
"test/scripts/docs-list.test.ts",
"test/scripts/doctor-install-switch-wrapper.test.ts",
"test/scripts/e2e-shell-tempfiles.test.ts",
"test/scripts/e2e-text-file-utils.test.ts",
"test/scripts/fixture-common.test.ts",
"test/scripts/fixture-plugin-commands.test.ts",
"test/scripts/incremental-line-reader.test.ts",
"test/scripts/ios-configure-signing.test.ts",
"test/scripts/ios-team-id.test.ts",
"test/scripts/ios-version.test.ts",
"test/scripts/kitchen-sink-rpc-walk.test.ts",
"test/scripts/native-app-i18n.test.ts",
"test/scripts/onboard-config-fixtures.test.ts",
"test/scripts/package-git-fixture.test.ts",
"test/scripts/parallels-lib-helpers.test.ts",
"test/scripts/parallels-package-log-progress-extract.test.ts",
"test/scripts/parallels-smoke-model.test.ts",
"test/scripts/plugin-package-dependencies.test.ts",
"test/scripts/plugins-assertions.test.ts",
"test/scripts/prepare-extension-package-boundary-artifacts.test.ts",
"test/scripts/proxy-install-ca.test.ts",
"test/scripts/release-preflight.test.ts",
"test/scripts/render-maturity-docs.test.ts",
"test/scripts/report-test-temp-creations.test.ts",
"test/scripts/runtime-postbuild-stamp.test.ts",
"test/scripts/test-install-sh-docker.test.ts",
"test/scripts/test-projects.test.ts",
"test/test-env.test.ts",
"test/vitest-scoped-config.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.gateway.config.ts",
forwardedArgs: [],
includePatterns: [
"src/gateway/hooks-mapping.test.ts",
"src/gateway/server-methods/chat.abort-persistence.test.ts",
"src/gateway/server.agent.gateway-server-agent-b.test.ts",
"src/gateway/server.chat.gateway-server-chat-b.test.ts",
"src/gateway/server.sessions.permissions-hooks.test.ts",
"src/gateway/terminal/launch.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.runtime-config.config.ts",
forwardedArgs: [],
includePatterns: ["src/config/sessions/entry-freshness.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.cron.config.ts",
forwardedArgs: [],
includePatterns: [
"src/cron/isolated-agent/run-session-state.test.ts",
"src/cron/run-log.error-reason.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.commands.config.ts",
forwardedArgs: [],
includePatterns: [
"src/commands/doctor-completion.test.ts",
"src/commands/status.scan.shared.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.auto-reply.config.ts",
forwardedArgs: [],
includePatterns: [
"src/auto-reply/reply/get-reply-native-slash-fast-path.test.ts",
"src/auto-reply/reply/get-reply.auto-fallback.test.ts",
"src/auto-reply/reply/reply-turn-admission.test.ts",
"src/auto-reply/reply/session-updates.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.agents.config.ts",
forwardedArgs: [],
includePatterns: [
"src/agents/agent-bundle-mcp-runtime.test.ts",
"src/agents/agent-tools-agent-config.exec.test.ts",
"src/agents/bash-tools.exec-foreground-failures.test.ts",
"src/agents/cli-runner.reliability.test.ts",
"src/agents/models-config.file-mode.test.ts",
"src/agents/sandbox/ssh.test.ts",
"src/agents/sessions/tools/find.fd.test.ts",
"src/agents/sessions/tools/read.test.ts",
],
watchMode: false,
},
{
config: "test/vitest/vitest.plugins.config.ts",
forwardedArgs: [],
includePatterns: ["src/plugins/git-install.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.e2e.config.ts",
forwardedArgs: [
"test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts",
"test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts",
"test/openclaw-launcher.e2e.test.ts",
],
includePatterns: null,
watchMode: false,
},
]);
// The importer inventory of test/helpers/temp-dir.ts churns with every new
// test using the helper; frozen full lists broke main on unrelated test
// additions. Assert the routing structure instead of the inventory.
const plans = buildVitestRunPlans(["test/helpers/temp-dir.ts"]);
const planFiles = plans.map((plan) => plan.includePatterns ?? plan.forwardedArgs);
const expandedFiles = planFiles.flat();
// Helper targets expand to importing test files; the helper itself never
// reaches Vitest as a raw target.
expect(expandedFiles).toContain("test/helpers/temp-dir.test.ts");
expect(expandedFiles).not.toContain("test/helpers/temp-dir.ts");
expect(expandedFiles.filter((file) => !file.endsWith(".test.ts"))).toEqual([]);
// Lower bound derived from the repo itself: every tracked test file that
// directly imports the helper must be picked up by the expansion scan, so
// dropped importers still fail without freezing the full inventory.
const scanRoots = ["src", "test", "ui", "extensions", "packages"];
const grep = spawnSync(
"git",
["grep", "-l", "--fixed-strings", "helpers/temp-dir", "--", ...scanRoots],
{ encoding: "utf8" },
);
expect(grep.status).toBe(0);
const directImporterTests = grep.stdout
.split("\n")
.map((line) => line.trim())
.filter((file) => file.endsWith(".test.ts") && !file.endsWith(".live.test.ts"))
.filter((file) => {
const source = fs.readFileSync(file, "utf8");
return [...source.matchAll(/from\s+["'](\.[^"']+)["']/gu)].some((match) => {
const importerDir = path.posix.dirname(file);
const resolved = path.posix.normalize(path.posix.join(importerDir, match[1]));
return resolved.replace(/\.(?:js|ts)$/u, "") === "test/helpers/temp-dir";
});
});
expect(directImporterTests.length).toBeGreaterThan(0);
expect(directImporterTests.filter((file) => !expandedFiles.includes(file))).toEqual([]);
// Importers partition across configs: each file lands in exactly one plan,
// in deterministic sorted order.
expect(plans.length).toBeGreaterThan(1);
expect(new Set(expandedFiles).size).toBe(expandedFiles.length);
for (const files of planFiles) {
expect(files).toEqual([...files].toSorted((left, right) => left.localeCompare(right)));
}
// Each importer must route to the same config and include-vs-forwarded
// shape as targeting it directly, so this test fails on real routing
// regressions but not on new importers of the helper.
for (const plan of plans) {
expect(plan.watchMode).toBe(false);
for (const file of plan.includePatterns ?? plan.forwardedArgs) {
expect(buildVitestRunPlans([file])).toEqual([
{
config: plan.config,
forwardedArgs: plan.includePatterns ? [] : [file],
includePatterns: plan.includePatterns ? [file] : null,
watchMode: false,
},
]);
}
}
});
it("routes e2e targets straight to the e2e config", () => {