|
1 | 1 | // Test project script tests cover fixture project discovery and validation. |
| 2 | +import { spawnSync } from "node:child_process"; |
2 | 3 | import fs from "node:fs"; |
3 | 4 | import os from "node:os"; |
4 | 5 | import path from "node:path"; |
@@ -854,167 +855,68 @@ describe("test-projects args", () => { |
854 | 855 | }); |
855 | 856 |
|
856 | 857 | it("routes top-level test helpers to importing repo tests", () => { |
857 | | - expect(buildVitestRunPlans(["test/helpers/temp-dir.ts"])).toEqual([ |
858 | | - { |
859 | | - config: "test/vitest/vitest.unit-fast.config.ts", |
860 | | - forwardedArgs: [], |
861 | | - includePatterns: [ |
862 | | - "src/agents/command/attempt-execution.shared.test.ts", |
863 | | - "src/auto-reply/reply/session-entry-persistence.test.ts", |
864 | | - "src/crestodian/operations.test.ts", |
865 | | - "src/install-sh-version.test.ts", |
866 | | - "src/proxy-capture/store.sqlite.test.ts", |
867 | | - "test/scripts/android-version.test.ts", |
868 | | - "test/scripts/resolve-openclaw-ref.test.ts", |
869 | | - ], |
870 | | - watchMode: false, |
871 | | - }, |
872 | | - { |
873 | | - config: "test/vitest/vitest.unit-fast-fake-timers.config.ts", |
874 | | - forwardedArgs: [], |
875 | | - includePatterns: ["src/entry.compile-cache.test.ts"], |
876 | | - watchMode: false, |
877 | | - }, |
878 | | - { |
879 | | - config: "test/vitest/vitest.unit.config.ts", |
880 | | - forwardedArgs: [ |
881 | | - "src/state/openclaw-agent-db.test.ts", |
882 | | - "src/state/openclaw-state-db.test.ts", |
883 | | - "src/state/sqlite-query-plan.test.ts", |
884 | | - "src/transcripts/store.test.ts", |
885 | | - ], |
886 | | - includePatterns: null, |
887 | | - watchMode: false, |
888 | | - }, |
889 | | - { |
890 | | - config: "test/vitest/vitest.tooling.config.ts", |
891 | | - forwardedArgs: [], |
892 | | - includePatterns: [ |
893 | | - "src/scripts/docs-link-audit.test.ts", |
894 | | - "src/scripts/sync-plugin-versions.test.ts", |
895 | | - "test/e2e/qa-lab/runtime/gateway-mcp-real-transports.test.ts", |
896 | | - "test/helpers/temp-dir.test.ts", |
897 | | - "test/scripts/android-pin-version.test.ts", |
898 | | - "test/scripts/bench-cli-startup.test.ts", |
899 | | - "test/scripts/check-package-dist-imports.test.ts", |
900 | | - "test/scripts/check-workflows.test.ts", |
901 | | - "test/scripts/ci-hydrate-testbox-env.test.ts", |
902 | | - "test/scripts/clawhub-fixture-server.test.ts", |
903 | | - "test/scripts/codex-install-assertions.test.ts", |
904 | | - "test/scripts/config-reload-mutate-metadata.test.ts", |
905 | | - "test/scripts/control-ui-i18n.test.ts", |
906 | | - "test/scripts/docs-list.test.ts", |
907 | | - "test/scripts/doctor-install-switch-wrapper.test.ts", |
908 | | - "test/scripts/e2e-shell-tempfiles.test.ts", |
909 | | - "test/scripts/e2e-text-file-utils.test.ts", |
910 | | - "test/scripts/fixture-common.test.ts", |
911 | | - "test/scripts/fixture-plugin-commands.test.ts", |
912 | | - "test/scripts/incremental-line-reader.test.ts", |
913 | | - "test/scripts/ios-configure-signing.test.ts", |
914 | | - "test/scripts/ios-team-id.test.ts", |
915 | | - "test/scripts/ios-version.test.ts", |
916 | | - "test/scripts/kitchen-sink-rpc-walk.test.ts", |
917 | | - "test/scripts/native-app-i18n.test.ts", |
918 | | - "test/scripts/onboard-config-fixtures.test.ts", |
919 | | - "test/scripts/package-git-fixture.test.ts", |
920 | | - "test/scripts/parallels-lib-helpers.test.ts", |
921 | | - "test/scripts/parallels-package-log-progress-extract.test.ts", |
922 | | - "test/scripts/parallels-smoke-model.test.ts", |
923 | | - "test/scripts/plugin-package-dependencies.test.ts", |
924 | | - "test/scripts/plugins-assertions.test.ts", |
925 | | - "test/scripts/prepare-extension-package-boundary-artifacts.test.ts", |
926 | | - "test/scripts/proxy-install-ca.test.ts", |
927 | | - "test/scripts/release-preflight.test.ts", |
928 | | - "test/scripts/render-maturity-docs.test.ts", |
929 | | - "test/scripts/report-test-temp-creations.test.ts", |
930 | | - "test/scripts/runtime-postbuild-stamp.test.ts", |
931 | | - "test/scripts/test-install-sh-docker.test.ts", |
932 | | - "test/scripts/test-projects.test.ts", |
933 | | - "test/test-env.test.ts", |
934 | | - "test/vitest-scoped-config.test.ts", |
935 | | - ], |
936 | | - watchMode: false, |
937 | | - }, |
938 | | - { |
939 | | - config: "test/vitest/vitest.gateway.config.ts", |
940 | | - forwardedArgs: [], |
941 | | - includePatterns: [ |
942 | | - "src/gateway/hooks-mapping.test.ts", |
943 | | - "src/gateway/server-methods/chat.abort-persistence.test.ts", |
944 | | - "src/gateway/server.agent.gateway-server-agent-b.test.ts", |
945 | | - "src/gateway/server.chat.gateway-server-chat-b.test.ts", |
946 | | - "src/gateway/server.sessions.permissions-hooks.test.ts", |
947 | | - "src/gateway/terminal/launch.test.ts", |
948 | | - ], |
949 | | - watchMode: false, |
950 | | - }, |
951 | | - { |
952 | | - config: "test/vitest/vitest.runtime-config.config.ts", |
953 | | - forwardedArgs: [], |
954 | | - includePatterns: ["src/config/sessions/entry-freshness.test.ts"], |
955 | | - watchMode: false, |
956 | | - }, |
957 | | - { |
958 | | - config: "test/vitest/vitest.cron.config.ts", |
959 | | - forwardedArgs: [], |
960 | | - includePatterns: [ |
961 | | - "src/cron/isolated-agent/run-session-state.test.ts", |
962 | | - "src/cron/run-log.error-reason.test.ts", |
963 | | - ], |
964 | | - watchMode: false, |
965 | | - }, |
966 | | - { |
967 | | - config: "test/vitest/vitest.commands.config.ts", |
968 | | - forwardedArgs: [], |
969 | | - includePatterns: [ |
970 | | - "src/commands/doctor-completion.test.ts", |
971 | | - "src/commands/status.scan.shared.test.ts", |
972 | | - ], |
973 | | - watchMode: false, |
974 | | - }, |
975 | | - { |
976 | | - config: "test/vitest/vitest.auto-reply.config.ts", |
977 | | - forwardedArgs: [], |
978 | | - includePatterns: [ |
979 | | - "src/auto-reply/reply/get-reply-native-slash-fast-path.test.ts", |
980 | | - "src/auto-reply/reply/get-reply.auto-fallback.test.ts", |
981 | | - "src/auto-reply/reply/reply-turn-admission.test.ts", |
982 | | - "src/auto-reply/reply/session-updates.test.ts", |
983 | | - ], |
984 | | - watchMode: false, |
985 | | - }, |
986 | | - { |
987 | | - config: "test/vitest/vitest.agents.config.ts", |
988 | | - forwardedArgs: [], |
989 | | - includePatterns: [ |
990 | | - "src/agents/agent-bundle-mcp-runtime.test.ts", |
991 | | - "src/agents/agent-tools-agent-config.exec.test.ts", |
992 | | - "src/agents/bash-tools.exec-foreground-failures.test.ts", |
993 | | - "src/agents/cli-runner.reliability.test.ts", |
994 | | - "src/agents/models-config.file-mode.test.ts", |
995 | | - "src/agents/sandbox/ssh.test.ts", |
996 | | - "src/agents/sessions/tools/find.fd.test.ts", |
997 | | - "src/agents/sessions/tools/read.test.ts", |
998 | | - ], |
999 | | - watchMode: false, |
1000 | | - }, |
1001 | | - { |
1002 | | - config: "test/vitest/vitest.plugins.config.ts", |
1003 | | - forwardedArgs: [], |
1004 | | - includePatterns: ["src/plugins/git-install.test.ts"], |
1005 | | - watchMode: false, |
1006 | | - }, |
1007 | | - { |
1008 | | - config: "test/vitest/vitest.e2e.config.ts", |
1009 | | - forwardedArgs: [ |
1010 | | - "test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts", |
1011 | | - "test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts", |
1012 | | - "test/openclaw-launcher.e2e.test.ts", |
1013 | | - ], |
1014 | | - includePatterns: null, |
1015 | | - watchMode: false, |
1016 | | - }, |
1017 | | - ]); |
| 858 | + // The importer inventory of test/helpers/temp-dir.ts churns with every new |
| 859 | + // test using the helper; frozen full lists broke main on unrelated test |
| 860 | + // additions. Assert the routing structure instead of the inventory. |
| 861 | + const plans = buildVitestRunPlans(["test/helpers/temp-dir.ts"]); |
| 862 | + const planFiles = plans.map((plan) => plan.includePatterns ?? plan.forwardedArgs); |
| 863 | + const expandedFiles = planFiles.flat(); |
| 864 | + |
| 865 | + // Helper targets expand to importing test files; the helper itself never |
| 866 | + // reaches Vitest as a raw target. |
| 867 | + expect(expandedFiles).toContain("test/helpers/temp-dir.test.ts"); |
| 868 | + expect(expandedFiles).not.toContain("test/helpers/temp-dir.ts"); |
| 869 | + expect(expandedFiles.filter((file) => !file.endsWith(".test.ts"))).toEqual([]); |
| 870 | + |
| 871 | + // Lower bound derived from the repo itself: every tracked test file that |
| 872 | + // directly imports the helper must be picked up by the expansion scan, so |
| 873 | + // dropped importers still fail without freezing the full inventory. |
| 874 | + const scanRoots = ["src", "test", "ui", "extensions", "packages"]; |
| 875 | + const grep = spawnSync( |
| 876 | + "git", |
| 877 | + ["grep", "-l", "--fixed-strings", "helpers/temp-dir", "--", ...scanRoots], |
| 878 | + { encoding: "utf8" }, |
| 879 | + ); |
| 880 | + expect(grep.status).toBe(0); |
| 881 | + const directImporterTests = grep.stdout |
| 882 | + .split("\n") |
| 883 | + .map((line) => line.trim()) |
| 884 | + .filter((file) => file.endsWith(".test.ts") && !file.endsWith(".live.test.ts")) |
| 885 | + .filter((file) => { |
| 886 | + const source = fs.readFileSync(file, "utf8"); |
| 887 | + return [...source.matchAll(/from\s+["'](\.[^"']+)["']/gu)].some((match) => { |
| 888 | + const importerDir = path.posix.dirname(file); |
| 889 | + const resolved = path.posix.normalize(path.posix.join(importerDir, match[1])); |
| 890 | + return resolved.replace(/\.(?:js|ts)$/u, "") === "test/helpers/temp-dir"; |
| 891 | + }); |
| 892 | + }); |
| 893 | + expect(directImporterTests.length).toBeGreaterThan(0); |
| 894 | + expect(directImporterTests.filter((file) => !expandedFiles.includes(file))).toEqual([]); |
| 895 | + |
| 896 | + // Importers partition across configs: each file lands in exactly one plan, |
| 897 | + // in deterministic sorted order. |
| 898 | + expect(plans.length).toBeGreaterThan(1); |
| 899 | + expect(new Set(expandedFiles).size).toBe(expandedFiles.length); |
| 900 | + for (const files of planFiles) { |
| 901 | + expect(files).toEqual([...files].toSorted((left, right) => left.localeCompare(right))); |
| 902 | + } |
| 903 | + |
| 904 | + // Each importer must route to the same config and include-vs-forwarded |
| 905 | + // shape as targeting it directly, so this test fails on real routing |
| 906 | + // regressions but not on new importers of the helper. |
| 907 | + for (const plan of plans) { |
| 908 | + expect(plan.watchMode).toBe(false); |
| 909 | + for (const file of plan.includePatterns ?? plan.forwardedArgs) { |
| 910 | + expect(buildVitestRunPlans([file])).toEqual([ |
| 911 | + { |
| 912 | + config: plan.config, |
| 913 | + forwardedArgs: plan.includePatterns ? [] : [file], |
| 914 | + includePatterns: plan.includePatterns ? [file] : null, |
| 915 | + watchMode: false, |
| 916 | + }, |
| 917 | + ]); |
| 918 | + } |
| 919 | + } |
1018 | 920 | }); |
1019 | 921 |
|
1020 | 922 | it("routes e2e targets straight to the e2e config", () => { |
|
0 commit comments