test(db): release gated transactions in finally so a red assertion holds no lock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
le king fu 2026-10-12 19:31:28 -04:00
parent 08946e6d08
commit 8efe99aa20

View file

@ -130,11 +130,15 @@ describe("db serialization (database-locked fix)", () => {
await tx.execute("COMMIT"); await tx.execute("COMMIT");
await gate; await gate;
}); });
try {
await vi.waitFor(() => expect(events).toContain("end:COMMIT")); await vi.waitFor(() => expect(events).toContain("end:COMMIT"));
expect(events).not.toContain("start:NESTED"); expect(events).not.toContain("start:NESTED");
} finally {
// Released even on failure, so a red assertion leaves no held lock.
release(); release();
await txn; await txn;
await nested; await nested;
}
expect(events).toEqual(["start:BEGIN", "end:BEGIN", "start:COMMIT", "end:COMMIT", "start:NESTED", "end:NESTED"]); expect(events).toEqual(["start:BEGIN", "end:BEGIN", "start:COMMIT", "end:COMMIT", "start:NESTED", "end:NESTED"]);
}); });
@ -358,6 +362,7 @@ describe("blocking alert (#408)", () => {
nested = (await getDb()).execute("NESTED"); nested = (await getDb()).execute("NESTED");
await gate; await gate;
}); });
try {
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS - 1); await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS - 1);
expect(logWarn).not.toHaveBeenCalled(); expect(logWarn).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1); await vi.advanceTimersByTimeAsync(1);
@ -368,10 +373,12 @@ describe("blocking alert (#408)", () => {
// Once per operation: still one warning much later. // Once per operation: still one warning much later.
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS * 3); await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS * 3);
expect(logWarn).toHaveBeenCalledTimes(1); expect(logWarn).toHaveBeenCalledTimes(1);
} finally {
release(); release();
await vi.advanceTimersByTimeAsync(10); await vi.advanceTimersByTimeAsync(10);
await txn; await txn;
await nested; await nested;
}
}); });
it("says nothing for an operation that got the lock in time", async () => { it("says nothing for an operation that got the lock in time", async () => {
@ -393,12 +400,17 @@ describe("blocking alert (#408)", () => {
const switching = connectToProfile("next.db"); const switching = connectToProfile("next.db");
// Settled outcome caught at once: the rejection lands while timers advance. // Settled outcome caught at once: the rejection lands while timers advance.
const waiting = withTransaction(async () => "late").catch((e: unknown) => e); const waiting = withTransaction(async () => "late").catch((e: unknown) => e);
try {
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS); await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS);
expect(logWarn).toHaveBeenCalledTimes(1); expect(logWarn).toHaveBeenCalledTimes(1);
expect(String(vi.mocked(logWarn).mock.calls[0][0])).toContain("a transaction has waited 15 s, the lock is held by a profile opening"); expect(String(vi.mocked(logWarn).mock.calls[0][0])).toContain(
"a transaction has waited 15 s, the lock is held by a profile opening"
);
} finally {
finish(); finish();
await vi.advanceTimersByTimeAsync(10); await vi.advanceTimersByTimeAsync(10);
await switching; await switching;
}
expect(await waiting).toBeInstanceOf(DbConnectionClosedError); expect(await waiting).toBeInstanceOf(DbConnectionClosedError);
}); });
}); });