diff --git a/extensions/memory-core/src/memory/manager.read-file.test.ts b/extensions/memory-core/src/memory/manager.read-file.test.ts index 37d1f0ccb4d..917cb7ffc11 100644 --- a/extensions/memory-core/src/memory/manager.read-file.test.ts +++ b/extensions/memory-core/src/memory/manager.read-file.test.ts @@ -246,19 +246,19 @@ describe("MemoryIndexManager.readFile", () => { await fs.mkdir(path.dirname(absPath), { recursive: true }); await fs.writeFile(absPath, "first\nsecond", "utf-8"); - const realReadFile = fs.readFile; + const realOpen = fs.open; let injected = false; - const readSpy = vi - .spyOn(fs, "readFile") - .mockImplementation(async (...args: Parameters) => { - const [target, options] = args; + const openSpy = vi + .spyOn(fs, "open") + .mockImplementation(async (...args: Parameters) => { + const [target, flags, mode] = args; if (!injected && typeof target === "string" && path.resolve(target) === absPath) { injected = true; const err = new Error("missing") as NodeJS.ErrnoException; err.code = "ENOENT"; throw err; } - return realReadFile(target, options); + return realOpen(target, flags, mode); }); try { @@ -269,7 +269,7 @@ describe("MemoryIndexManager.readFile", () => { }); expect(result).toEqual({ text: "", path: relPath }); } finally { - readSpy.mockRestore(); + openSpy.mockRestore(); } }); diff --git a/extensions/zalouser/src/zalo-js.credentials.test.ts b/extensions/zalouser/src/zalo-js.credentials.test.ts index 62a9f78608e..b10eaeb5315 100644 --- a/extensions/zalouser/src/zalo-js.credentials.test.ts +++ b/extensions/zalouser/src/zalo-js.credentials.test.ts @@ -450,8 +450,8 @@ describe("zalouser credential persistence", () => { await withEnvAsync({ OPENCLAW_STATE_DIR: stateDir }, async () => { const started = await startZaloQrLogin({ profile, timeoutMs: 1000 }); const waited = await waitForZaloQrLogin({ profile, timeoutMs: 1000 }); - expect(`${started.message} ${waited.message}`).toContain( - "Refusing to write Zalo credentials to symlinked path", + expect(`${started.message} ${waited.message}`).toMatch( + /Refusing to write Zalo credentials to symlinked path|private store target must be a regular file/, ); }); diff --git a/src/agents/apply-patch.test.ts b/src/agents/apply-patch.test.ts index 5cb2fa9bcf3..d35fc74402b 100644 --- a/src/agents/apply-patch.test.ts +++ b/src/agents/apply-patch.test.ts @@ -451,7 +451,9 @@ describe("applyPatch", () => { symlinkTarget: outside, timing: "before-realpath", run: async () => { - await expect(applyPatch(patch, { cwd: dir })).rejects.toThrow(/under root/i); + await expect(applyPatch(patch, { cwd: dir })).rejects.toThrow( + /under root|unable to resolve opened file path/i, + ); }, }); await expect(fs.stat(path.join(outside, "nested"))).rejects.toMatchObject({ diff --git a/src/agents/subagent-registry.persistence.test.ts b/src/agents/subagent-registry.persistence.test.ts index fcb04a3b6cf..8b1f25b9b6f 100644 --- a/src/agents/subagent-registry.persistence.test.ts +++ b/src/agents/subagent-registry.persistence.test.ts @@ -328,7 +328,7 @@ describe("subagent registry persistence", () => { expect(after.version).toBe(2); }); - it("reuses unchanged persisted registry snapshots without reparsing runs.json", async () => { + it("returns isolated clones for unchanged persisted registry snapshots", async () => { const registryPath = await writePersistedRegistry( { version: 2, @@ -349,8 +349,6 @@ describe("subagent registry persistence", () => { }, { seedChildSessions: false }, ); - const readSpy = vi.spyOn(fsSync, "readFileSync"); - const first = loadSubagentRegistryFromDisk(); first.clear(); const cachedEntry = loadSubagentRegistryFromDisk().get("run-cached"); @@ -373,9 +371,6 @@ describe("subagent registry persistence", () => { }); expect(second.get("run-cached")?.endedAt).toBeUndefined(); expect(second.get("run-cached")?.cleanupHandled).toBeUndefined(); - expect( - readSpy.mock.calls.filter(([pathname]) => String(pathname) === registryPath), - ).toHaveLength(1); await fs.writeFile( registryPath, @@ -398,24 +393,17 @@ describe("subagent registry persistence", () => { ); expect(loadSubagentRegistryFromDisk().has("run-updated")).toBe(true); - expect( - readSpy.mock.calls.filter(([pathname]) => String(pathname) === registryPath), - ).toHaveLength(2); }); - it("reuses unchanged invalid persisted registry snapshots as empty", async () => { + it("returns empty maps for unchanged invalid persisted registry snapshots", async () => { tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-subagent-")); process.env.OPENCLAW_STATE_DIR = tempStateDir; const registryPath = path.join(tempStateDir, "subagents", "runs.json"); await fs.mkdir(path.dirname(registryPath), { recursive: true }); await fs.writeFile(registryPath, "{invalid", "utf8"); - const readSpy = vi.spyOn(fsSync, "readFileSync"); expect(loadSubagentRegistryFromDisk()).toEqual(new Map()); expect(loadSubagentRegistryFromDisk()).toEqual(new Map()); - expect( - readSpy.mock.calls.filter(([pathname]) => String(pathname) === registryPath), - ).toHaveLength(1); }); it("normalizes persisted and newly registered session keys to canonical trimmed values", async () => { diff --git a/src/infra/archive.test.ts b/src/infra/archive.test.ts index c47e310dbcc..5386478c415 100644 --- a/src/infra/archive.test.ts +++ b/src/infra/archive.test.ts @@ -228,19 +228,16 @@ describe("archive utils", () => { symlinkTarget: outsideDir, timing: "after-realpath", run: async () => { - await expect( - extractArchive({ - archivePath, - destDir: extractDir, - timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS, - }), - ).rejects.toMatchObject({ - code: "destination-symlink-traversal", - } satisfies Partial); + await extractArchive({ + archivePath, + destDir: extractDir, + timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS, + }); }, }); await expect(fs.readFile(outsideTarget, "utf8")).resolves.toBe("SAFE"); + await expect(fs.readFile(path.join(slotDir, "target.txt"), "utf8")).resolves.toBe("owned"); }); }); diff --git a/src/plugins/contracts/extension-package-project-boundaries.test.ts b/src/plugins/contracts/extension-package-project-boundaries.test.ts index 29fb48b886c..c525725a666 100644 --- a/src/plugins/contracts/extension-package-project-boundaries.test.ts +++ b/src/plugins/contracts/extension-package-project-boundaries.test.ts @@ -56,6 +56,7 @@ const MEMORY_HOST_SDK_EXPORTS = [ "./status", ] as const; const MEMORY_HOST_SDK_ALLOWED_CORE_BRIDGE_FILES = [ + "packages/memory-host-sdk/src/host/fs-utils.ts", "packages/memory-host-sdk/src/host/openclaw-runtime.ts", ] as const; const MEMORY_HOST_SDK_RUNTIME_ADAPTER_FILES = [