fix(test): bound shared git inventory lookup (#111171)

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
tzy-17
2026-08-03 23:36:49 +08:00
committed by GitHub
parent 7fafaf50f4
commit 53d442eb51
6 changed files with 84 additions and 15 deletions
+5 -3
View File
@@ -57,9 +57,11 @@ function expectNonEmptyStringList(values: readonly string[], label: string) {
}
function listTrackedSourceFiles(): string[] {
return (listGitTrackedFiles({ pathspecs: sourceRootsForDeprecatedCallGuard }) ?? []).filter(
(file) => /\.(?:ts|tsx|mts|cts)$/u.test(file),
);
const files = listGitTrackedFiles({ pathspecs: sourceRootsForDeprecatedCallGuard });
if (!files) {
throw new Error("unable to list tracked source files for the deprecated-call guard");
}
return files.filter((file) => /\.(?:ts|tsx|mts|cts)$/u.test(file));
}
describe("plugin compatibility registry", () => {
@@ -257,7 +257,10 @@ describe("config footprint guardrails", () => {
]);
const facadeImportPattern =
/\bfrom\s*["'][^"']*(?:channel-config-primitives|bundled-channel-config-schema)(?:\.js)?["']/u;
const files = listGitTrackedFiles({ repoRoot: REPO_ROOT, pathspecs: "src" }) ?? [];
const files = listGitTrackedFiles({ repoRoot: REPO_ROOT, pathspecs: "src" });
if (!files) {
throw new Error("unable to list tracked source files for the config facade guard");
}
const offenders = files.filter((file) => {
if (!(file.endsWith(".ts") || file.endsWith(".tsx")) || allowedShellImporters.has(file)) {
@@ -34,12 +34,14 @@ const LEGACY_MODEL_CATALOG_BRIDGES = new Map([
]);
function listSourceFiles(): string[] {
return (
listGitTrackedFiles({
repoRoot: REPO_ROOT,
pathspecs: ["src", "extensions", "packages", "test"],
}) ?? []
)
const files = listGitTrackedFiles({
repoRoot: REPO_ROOT,
pathspecs: ["src", "extensions", "packages", "test"],
});
if (!files) {
throw new Error("unable to list tracked source files for the model-catalog import guard");
}
return files
.filter((file) => /\.(?:[cm]?ts|tsx|mts|cts)$/u.test(file))
.filter((file) => fs.existsSync(path.join(REPO_ROOT, file)));
}
@@ -465,11 +465,13 @@ function collectNewDeprecatedMemoryEmbeddingProviderApiFiles(): string[] {
function collectNewDeprecatedMemoryEmbeddingProviderManifestFiles(): string[] {
const files: string[] = [];
const manifestFiles =
listGitTrackedFiles({
repoRoot: REPO_ROOT,
pathspecs: "extensions/**/openclaw.plugin.json",
}) ?? [];
const manifestFiles = listGitTrackedFiles({
repoRoot: REPO_ROOT,
pathspecs: "extensions/**/openclaw.plugin.json",
});
if (!manifestFiles) {
throw new Error("unable to list plugin manifests for the deprecated manifest guard");
}
for (const repoRelativePath of manifestFiles) {
const source = fs.readFileSync(resolve(REPO_ROOT, repoRelativePath), "utf8");
if (
+56
View File
@@ -0,0 +1,56 @@
import fs from "node:fs/promises";
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { withTempDir } from "./temp-dir.js";
const spawnSyncMock = vi.hoisted(() => vi.fn());
vi.mock("node:child_process", () => ({
spawnSync: spawnSyncMock,
}));
import { listGitTrackedFiles } from "./repo-files.js";
describe("listGitTrackedFiles", () => {
beforeEach(() => {
spawnSyncMock.mockReset();
});
it("bounds Git inventory lookup and returns sorted existing files", async () => {
await withTempDir("openclaw-repo-files-", async (repoRoot) => {
await fs.writeFile(path.join(repoRoot, "z.ts"), "");
await fs.writeFile(path.join(repoRoot, "a.ts"), "");
spawnSyncMock.mockReturnValue({
status: 0,
stdout: "z.ts\nmissing.ts\na.ts\n",
});
expect(listGitTrackedFiles({ repoRoot, pathspecs: "src" })).toEqual(["a.ts", "z.ts"]);
expect(spawnSyncMock).toHaveBeenCalledWith(
"git",
["ls-files", "--", "src"],
expect.objectContaining({
cwd: repoRoot,
killSignal: "SIGKILL",
timeout: 5_000,
}),
);
});
});
it("caches a timed-out inventory lookup as unavailable", async () => {
await withTempDir("openclaw-repo-files-timeout-", async (repoRoot) => {
spawnSyncMock.mockReturnValue({
error: Object.assign(new Error("spawnSync git ETIMEDOUT"), { code: "ETIMEDOUT" }),
signal: "SIGKILL",
status: null,
stdout: "",
});
const params = { repoRoot, pathspecs: ["src", "extensions"] };
expect(listGitTrackedFiles(params)).toBeNull();
expect(listGitTrackedFiles(params)).toBeNull();
expect(spawnSyncMock).toHaveBeenCalledOnce();
});
});
});
+4
View File
@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
const GIT_LS_FILES_TIMEOUT_MS = 5_000;
const gitTrackedFilesCache = new Map<string, string[] | null>();
function filterExistingRepoFiles(repoRoot: string, files: readonly string[]): string[] {
@@ -36,8 +37,11 @@ export function listGitTrackedFiles(params: {
const result = spawnSync("git", ["ls-files", "--", ...pathspecs], {
cwd: repoRoot,
encoding: "utf8",
// Bound repository scans; SIGKILL avoids waiting on a hung Git process after timeout.
killSignal: "SIGKILL",
maxBuffer: 16 * 1024 * 1024,
stdio: ["ignore", "pipe", "ignore"],
timeout: GIT_LS_FILES_TIMEOUT_MS,
});
if (result.status !== 0) {
gitTrackedFilesCache.set(cacheKey, null);