refactor: dedupe openclaw root traversal and add coverage

This commit is contained in:
Peter Steinberger
2026-02-19 13:43:24 +00:00
parent cf6edc6d57
commit 722a898f20
2 changed files with 39 additions and 11 deletions

View File

@@ -10,6 +10,7 @@ const FIXTURE_BASE = path.join(VITEST_FS_BASE, "openclaw-root");
const state = vi.hoisted(() => ({
entries: new Map<string, FakeFsEntry>(),
realpaths: new Map<string, string>(),
realpathErrors: new Set<string>(),
}));
const abs = (p: string) => path.resolve(p);
@@ -56,7 +57,15 @@ vi.mock("node:fs", async (importOriginal) => {
};
},
realpathSync: (p: string) =>
isFixturePath(p) ? (state.realpaths.get(abs(p)) ?? abs(p)) : actual.realpathSync(p),
isFixturePath(p)
? (() => {
const resolved = abs(p);
if (state.realpathErrors.has(resolved)) {
throw new Error(`ENOENT: no such file or directory, realpath '${p}'`);
}
return state.realpaths.get(resolved) ?? resolved;
})()
: actual.realpathSync(p),
};
return { ...wrapped, default: wrapped };
});
@@ -84,6 +93,7 @@ describe("resolveOpenClawPackageRoot", () => {
beforeEach(() => {
state.entries.clear();
state.realpaths.clear();
state.realpathErrors.clear();
});
it("resolves package root from .bin argv1", async () => {
@@ -109,6 +119,18 @@ describe("resolveOpenClawPackageRoot", () => {
expect(resolveOpenClawPackageRootSync({ argv1: bin })).toBe(realPkg);
});
it("falls back when argv1 realpath throws", async () => {
const { resolveOpenClawPackageRootSync } = await import("./openclaw-root.js");
const project = fx("realpath-throw-scenario");
const argv1 = path.join(project, "node_modules", ".bin", "openclaw");
const pkgRoot = path.join(project, "node_modules", "openclaw");
state.realpathErrors.add(abs(argv1));
setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" }));
expect(resolveOpenClawPackageRootSync({ argv1 })).toBe(pkgRoot);
});
it("prefers moduleUrl candidates", async () => {
const { resolveOpenClawPackageRootSync } = await import("./openclaw-root.js");
@@ -136,4 +158,10 @@ describe("resolveOpenClawPackageRoot", () => {
await expect(resolveOpenClawPackageRoot({ cwd: pkgRoot })).resolves.toBe(pkgRoot);
});
it("async resolver returns null when no package roots exist", async () => {
const { resolveOpenClawPackageRoot } = await import("./openclaw-root.js");
await expect(resolveOpenClawPackageRoot({ cwd: fx("missing") })).resolves.toBeNull();
});
});