From 66f9fa2821decf30bec4468a33a499d8ea459510 Mon Sep 17 00:00:00 2001 From: mptyl Date: Tue, 25 Aug 2026 21:39:35 +0200 Subject: [PATCH] test(workspaces): make lock contention deterministic --- .../test/workspaces-git-repository.test.ts | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/backend/test/workspaces-git-repository.test.ts b/backend/test/workspaces-git-repository.test.ts index 22004a00..659b921a 100644 --- a/backend/test/workspaces-git-repository.test.ts +++ b/backend/test/workspaces-git-repository.test.ts @@ -323,16 +323,40 @@ test("parallel contenders recover a stale lock file without overlapping critical const second = new WorkspaceRepositoryLock(locks); let active = 0; let maximum = 0; - const critical = async () => { + let firstEntered!: () => void; + const entered = new Promise((resolve) => { firstEntered = resolve; }); + let releaseFirst!: () => void; + const held = new Promise((resolve) => { releaseFirst = resolve; }); + const firstRun = first.run(async () => { + active += 1; + maximum = Math.max(maximum, active); + firstEntered(); + await held; + active -= 1; + }); + await entered; + const secondRun = second.run(async () => { active += 1; maximum = Math.max(maximum, active); await new Promise((resolve) => setTimeout(resolve, 25)); active -= 1; - }; + }); - const results = await Promise.allSettled([first.run(critical), second.run(critical)]); + let timeout!: ReturnType; + const contender = await Promise.race([ + secondRun.then( + () => ({ status: "fulfilled" as const }), + () => ({ status: "rejected" as const }), + ), + new Promise<{ status: "timed-out" }>((resolve) => { + timeout = setTimeout(() => resolve({ status: "timed-out" }), 2_000); + }), + ]); + clearTimeout(timeout); + releaseFirst(); + await firstRun; + await secondRun.catch(() => undefined); - expect(results.filter((result) => result.status === "fulfilled")).toHaveLength(1); - expect(results.filter((result) => result.status === "rejected")).toHaveLength(1); + expect(contender.status).toBe("rejected"); expect(maximum).toBe(1); });