mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
fix(test): keep extension batches inside requested directories (#162009)
* fix(test): keep extension batches inside requested directories Preserve canonical plugin roots as directory filters and intersect plain directory test globs with their owning scope. Prevent policy batches from selecting sibling policy filenames or nested policy directories while retaining watch discovery, explicit files, exclusions, and complex owner patterns. Add real runner-to-selector regression cases, proven failing before the fix, and keep existing aggregation and scoped-config assertions aligned. * fix(test): preserve explicit extension file filters
This commit is contained in:
parent
8eadb661cd
commit
a58e5babbe
4 changed files with 78 additions and 11 deletions
|
|
@ -33,7 +33,7 @@ import { isDirectScriptRun, runVitestBatch } from "./lib/vitest-batch-runner.mts
|
|||
import type { VitestBatchRunParams } from "./lib/vitest-batch-runner.mts";
|
||||
import { prepareVitestRuntime } from "./lib/vitest-build-prerequisites.mts";
|
||||
import { resolveVitestCacheRoot, resolveVitestCacheSlotPath } from "./lib/vitest-cache-slots.mts";
|
||||
import { resolveExplicitVitestMode } from "./lib/vitest-cli-mode.mts";
|
||||
import { collectVitestFileFilters, resolveExplicitVitestMode } from "./lib/vitest-cli-mode.mts";
|
||||
import { resolveVitestHomeSelection } from "./lib/vitest-home-selection.mts";
|
||||
import { createVitestReportOwner, type VitestReportOutcome } from "./lib/vitest-report-owner.mts";
|
||||
import { resolveVitestRuntimeCliSelections } from "./lib/vitest-runtime-selection.mts";
|
||||
|
|
@ -178,6 +178,7 @@ function preparePlanGroup(
|
|||
exactExcludePaths: Set<string>,
|
||||
) {
|
||||
const targets = resolveGroupTargets(group, exactExcludePaths);
|
||||
const hasFileFilters = collectVitestFileFilters(vitestArgs).length > 0;
|
||||
const targetChunks =
|
||||
targets.length === 0
|
||||
? []
|
||||
|
|
@ -195,7 +196,11 @@ function preparePlanGroup(
|
|||
group,
|
||||
watchMode: resolveExplicitVitestMode(["run", ...vitestArgs]) === "watch",
|
||||
}),
|
||||
targets: chunk.map((target) => relativizeExtensionVitestPath(target)),
|
||||
targets: chunk.map((target) => {
|
||||
const relative = relativizeExtensionVitestPath(target);
|
||||
// Bound default discovery without widening an explicit CLI file selection.
|
||||
return !hasFileFilters && group.extensionIds.includes(relative) ? `${relative}/` : relative;
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const bunInvocations: typeof invocations = [];
|
||||
|
|
@ -238,7 +243,12 @@ function combineSinglePluginGroups(
|
|||
config,
|
||||
args: relativizeExtensionVitestArgs(vitestArgs),
|
||||
targets: targets.filter(
|
||||
(target) => !targets.some((root) => target.startsWith(`${root}/`)),
|
||||
(target) =>
|
||||
!targets.some(
|
||||
(root) =>
|
||||
root !== target &&
|
||||
target.startsWith(root.endsWith("/") ? root : `${root}/`),
|
||||
),
|
||||
),
|
||||
env: {
|
||||
...createGroupEnv({
|
||||
|
|
|
|||
|
|
@ -62,7 +62,10 @@ import { providerExtensionTestRoots } from "../vitest/vitest.extension-provider-
|
|||
import { qaExtensionTestRoots } from "../vitest/vitest.extension-qa-paths.mjs";
|
||||
import { zaloExtensionTestRoots } from "../vitest/vitest.extension-zalo-paths.mjs";
|
||||
import { extensionCatchAllExcludedTestRoots } from "../vitest/vitest.extensions.config.ts";
|
||||
import { isSharedVitestExcludedPath } from "../vitest/vitest.pattern-file.ts";
|
||||
import {
|
||||
isSharedVitestExcludedPath,
|
||||
matchesVitestCliSelection,
|
||||
} from "../vitest/vitest.pattern-file.ts";
|
||||
|
||||
vi.mock("../../scripts/lib/vitest-build-prerequisites.mts", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("../../scripts/lib/vitest-build-prerequisites.mts")>()),
|
||||
|
|
@ -689,7 +692,7 @@ describe("scripts/test-extension.mts", () => {
|
|||
env: {
|
||||
OPENCLAW_EXTENSION_BATCH_PARALLEL: "2",
|
||||
},
|
||||
targets: ["two"],
|
||||
targets: ["two/"],
|
||||
});
|
||||
const cachePaths = runGroup.mock.calls.map(([params]) =>
|
||||
params.env?.OPENCLAW_VITEST_FS_MODULE_CACHE_PATH?.replaceAll("\\", "/"),
|
||||
|
|
@ -1009,6 +1012,53 @@ await new Promise(()=>{});export default {};`,
|
|||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ ids: ["policy"], args: [], selected: ["extensions/policy/src/example.test.ts"] },
|
||||
{
|
||||
ids: ["policy", "file-transfer"],
|
||||
args: [],
|
||||
selected: [
|
||||
"extensions/policy/src/example.test.ts",
|
||||
"extensions/file-transfer/src/shared/policy.test.ts",
|
||||
],
|
||||
},
|
||||
{
|
||||
ids: ["policy"],
|
||||
args: ["--watch"],
|
||||
selected: ["extensions/policy/src/example.test.ts"],
|
||||
},
|
||||
])("confines extension roots $ids with $args", async ({ ids, args, selected }) => {
|
||||
const runGroup = vi.fn<(params: RunGroupParams) => Promise<number>>().mockResolvedValue(0);
|
||||
await expect(
|
||||
runExtensionBatchPlan(resolveExtensionBatchPlan({ extensionIds: ids }), {
|
||||
env: {},
|
||||
runGroup,
|
||||
vitestArgs: args,
|
||||
}),
|
||||
).resolves.toBe(0);
|
||||
|
||||
expect(runGroup).toHaveBeenCalledOnce();
|
||||
const invocation = requireFirstMockArg<RunGroupParams>(runGroup);
|
||||
const candidates = [
|
||||
"extensions/policy/src/example.test.ts",
|
||||
"extensions/file-transfer/src/shared/policy.test.ts",
|
||||
"extensions/other/src/policy/example.test.ts",
|
||||
"extensions/policy-extra/src/example.test.ts",
|
||||
"extensions/policy/src/example.test.tsx",
|
||||
];
|
||||
expect(
|
||||
candidates.filter((file) =>
|
||||
matchesVitestCliSelection(
|
||||
file,
|
||||
["extensions/**/*.test.ts"],
|
||||
["run", "--config", invocation.config, ...invocation.args, ...invocation.targets],
|
||||
"extensions",
|
||||
invocation.env ?? {},
|
||||
),
|
||||
),
|
||||
).toEqual(selected);
|
||||
});
|
||||
|
||||
it("expands extension batch roots before applying exact Vitest excludes", async () => {
|
||||
const runGroup = vi.fn<() => Promise<number>>().mockResolvedValue(0);
|
||||
await runExtensionBatchPlan(
|
||||
|
|
@ -1222,7 +1272,7 @@ await new Promise(()=>{});export default {};`,
|
|||
expect(result).toBe(0);
|
||||
expect(runGroup).toHaveBeenCalledOnce();
|
||||
const invocation = requireFirstMockArg<RunGroupParams>(runGroup);
|
||||
expect(invocation.targets).toEqual(["matrix"]);
|
||||
expect(invocation.targets).toEqual(["matrix/"]);
|
||||
expect(invocation.config).toBe("test/vitest/vitest.database-worker-watch.config.ts");
|
||||
expect(invocation.homeMode).toBe("live-aware");
|
||||
expect(invocation.env?.OPENCLAW_VITEST_DATABASE_WORKER_WATCH_OWNER).toBe(
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ function expectForkedNonIsolatedRunner(
|
|||
}
|
||||
|
||||
describe("createScopedVitestConfig", () => {
|
||||
it("keeps broad package scoped cli directory filters aligned with repo-root include patterns", () => {
|
||||
it("narrows package CLI directories within their owner", () => {
|
||||
const config = createScopedVitestConfig(["packages/**/*.test.ts"], {
|
||||
argv: ["vitest", "run", "packages/normalization-core"],
|
||||
dir: "packages",
|
||||
|
|
@ -77,7 +77,7 @@ describe("createScopedVitestConfig", () => {
|
|||
passWithNoTests: true,
|
||||
});
|
||||
|
||||
expect(requireTestConfig(config).include).toEqual(["**/*.test.ts"]);
|
||||
expect(requireTestConfig(config).include).toEqual(["normalization-core/**/*.test.ts"]);
|
||||
});
|
||||
|
||||
it("relativizes scoped include and exclude patterns to the configured dir", () => {
|
||||
|
|
|
|||
|
|
@ -62,18 +62,25 @@ export function narrowIncludePatterns(
|
|||
return null;
|
||||
}
|
||||
|
||||
// Vitest applies CLI filters after discovery. Prefix overlap cannot prove glob
|
||||
// containment, so retain the owner's patterns unless selecting an owned literal file.
|
||||
// Prefix overlap alone cannot prove containment. Only narrow literal files
|
||||
// and plain directory test patterns; preserve more complex owner globs.
|
||||
const narrowed = new Set<string>();
|
||||
for (const candidate of candidatePatterns) {
|
||||
const isLiteral = !/[?*[\]{}]/u.test(candidate);
|
||||
const directoryCandidate = candidate.replace(/\.test\.\*$/u, ".test.ts");
|
||||
const candidateRoot = directoryTestPatternRoot(directoryCandidate);
|
||||
for (const laneScope of includePatterns) {
|
||||
if (isLiteral) {
|
||||
if (matchesGlob(candidate, laneScope)) {
|
||||
narrowed.add(candidate);
|
||||
}
|
||||
} else if (patternsCouldOverlap(candidate, laneScope, matchesGlob)) {
|
||||
narrowed.add(laneScope);
|
||||
const ownerRoot = directoryTestPatternRoot(laneScope);
|
||||
narrowed.add(
|
||||
candidateRoot !== null && ownerRoot !== null && isAtOrUnder(candidateRoot, ownerRoot)
|
||||
? directoryCandidate
|
||||
: laneScope,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue