From 844f1dd87a646df55887b06176615c8b5ab616bf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Apr 2026 18:32:17 +0100 Subject: [PATCH] test: dedupe session lock symlink fixtures --- src/agents/session-write-lock.test.ts | 117 +++++++++++++------------- 1 file changed, 60 insertions(+), 57 deletions(-) diff --git a/src/agents/session-write-lock.test.ts b/src/agents/session-write-lock.test.ts index 637aa4dfdf6..66e585e917b 100644 --- a/src/agents/session-write-lock.test.ts +++ b/src/agents/session-write-lock.test.ts @@ -70,6 +70,38 @@ async function writeCurrentProcessLock(lockPath: string, extra?: Record Promise, +) { + if (process.platform === "win32") { + return; + } + + const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lock-")); + try { + const realDir = path.join(root, "real"); + const linkDir = path.join(root, "link"); + await fs.mkdir(realDir, { recursive: true }); + await fs.symlink(realDir, linkDir); + + const sessionReal = path.join(realDir, "sessions.json"); + const sessionLink = path.join(linkDir, "sessions.json"); + await run({ + sessionReal, + sessionLink, + realLockPath: `${sessionReal}.lock`, + linkLockPath: `${sessionLink}.lock`, + }); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } +} + async function expectActiveInProcessLockIsNotReclaimed(params?: { legacyStarttime?: unknown; }): Promise { @@ -109,48 +141,33 @@ describe("acquireSessionWriteLock", () => { vi.restoreAllMocks(); }); it("reuses locks across symlinked session paths", async () => { - if (process.platform === "win32") { - return; - } + await withSymlinkedSessionPaths( + async ({ sessionReal, sessionLink, realLockPath, linkLockPath }) => { + const lockA = await acquireSessionWriteLock({ + sessionFile: sessionReal, + timeoutMs: 500, + allowReentrant: true, + }); + const lockB = await acquireSessionWriteLock({ + sessionFile: sessionLink, + timeoutMs: 500, + allowReentrant: true, + }); - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lock-")); - try { - const realDir = path.join(root, "real"); - const linkDir = path.join(root, "link"); - await fs.mkdir(realDir, { recursive: true }); - await fs.symlink(realDir, linkDir); - - const sessionReal = path.join(realDir, "sessions.json"); - const sessionLink = path.join(linkDir, "sessions.json"); - const realLockPath = `${sessionReal}.lock`; - const linkLockPath = `${sessionLink}.lock`; - - const lockA = await acquireSessionWriteLock({ - sessionFile: sessionReal, - timeoutMs: 500, - allowReentrant: true, - }); - const lockB = await acquireSessionWriteLock({ - sessionFile: sessionLink, - timeoutMs: 500, - allowReentrant: true, - }); - - await expect(fs.access(realLockPath)).resolves.toBeUndefined(); - await expect(fs.access(linkLockPath)).resolves.toBeUndefined(); - const [realCanonicalLockPath, linkCanonicalLockPath] = await Promise.all([ - fs.realpath(realLockPath), - fs.realpath(linkLockPath), - ]); - expect(linkCanonicalLockPath).toBe(realCanonicalLockPath); - await expectLockRemovedOnlyAfterFinalRelease({ - lockPath: realLockPath, - firstLock: lockA, - secondLock: lockB, - }); - } finally { - await fs.rm(root, { recursive: true, force: true }); - } + await expect(fs.access(realLockPath)).resolves.toBeUndefined(); + await expect(fs.access(linkLockPath)).resolves.toBeUndefined(); + const [realCanonicalLockPath, linkCanonicalLockPath] = await Promise.all([ + fs.realpath(realLockPath), + fs.realpath(linkLockPath), + ]); + expect(linkCanonicalLockPath).toBe(realCanonicalLockPath); + await expectLockRemovedOnlyAfterFinalRelease({ + lockPath: realLockPath, + firstLock: lockA, + secondLock: lockB, + }); + }, + ); }); it("keeps the lock file until the last release", async () => { @@ -185,19 +202,7 @@ describe("acquireSessionWriteLock", () => { }); it("does not reenter locks by default through symlinked session paths", async () => { - if (process.platform === "win32") { - return; - } - - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lock-")); - try { - const realDir = path.join(root, "real"); - const linkDir = path.join(root, "link"); - await fs.mkdir(realDir, { recursive: true }); - await fs.symlink(realDir, linkDir); - - const sessionReal = path.join(realDir, "sessions.json"); - const sessionLink = path.join(linkDir, "sessions.json"); + await withSymlinkedSessionPaths(async ({ sessionReal, sessionLink }) => { const lock = await acquireSessionWriteLock({ sessionFile: sessionReal, timeoutMs: 500 }); await expect( @@ -205,9 +210,7 @@ describe("acquireSessionWriteLock", () => { ).rejects.toThrow(/session file locked/); await lock.release(); - } finally { - await fs.rm(root, { recursive: true, force: true }); - } + }); }); it("allows a new default lock acquisition after the held lock is released", async () => {