























@@ -6,12 +6,15 @@ import { DatabaseSync } from "node:sqlite";
66import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
77import {
88cleanupAgedMemoryReindexTempFiles,
9+closeMemoryDatabase,
910openMemoryDatabaseAtPath,
1011openMemoryReindexTempDatabaseAtPath,
1112} from "./manager-db.js";
1213import {
14+acquireMemoryReindexSwapLock,
1315acquireMemoryReindexLock,
1416resolveMemoryReindexLockPath,
17+tryAcquireMemoryReindexSwapLock,
1518tryAcquireMemoryReindexLock,
1619} from "./manager-reindex-lock.js";
1720@@ -56,15 +59,15 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
56595760const db = openMemoryDatabaseAtPath(dbPath, false);
5861expect(db).toBeDefined();
59-db.close();
62+closeMemoryDatabase(db);
6063});
61646265it("allows creating a new database when allowCreate is true", async () => {
6366const dbPath = path.join(fixtureRoot, `case-${caseId++}`, "new-index.sqlite");
64676568const db = openMemoryDatabaseAtPath(dbPath, false, true);
6669expect(db).toBeDefined();
67-db.close();
70+closeMemoryDatabase(db);
68716972const stat = await fs.stat(dbPath);
7073expect(stat.size).toBeGreaterThan(0);
@@ -95,7 +98,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
95989699reindexLock.release();
97100const db = openMemoryDatabaseAtPath(dbPath, false, true);
98-db.close();
101+closeMemoryDatabase(db);
99102});
100103101104it("refuses to auto-create an empty database when allowCreate is false", async () => {
@@ -118,7 +121,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
118121119122const reopen = openMemoryDatabaseAtPath(dbPath, false, false);
120123expect(reopen).toBeDefined();
121-reopen.close();
124+closeMemoryDatabase(reopen);
122125});
123126124127it("removes aged orphan reindex temp files before opening the live database", async () => {
@@ -138,7 +141,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
138141}
139142140143const db = openMemoryDatabaseAtPath(dbPath, false);
141-db.close();
144+closeMemoryDatabase(db);
142145143146await expectPathMissing(orphanBase);
144147await expectPathMissing(`${orphanBase}-wal`);
@@ -165,7 +168,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
165168}
166169167170const db = openMemoryDatabaseAtPath(dbPath, false);
168-db.close();
171+closeMemoryDatabase(db);
169172170173await expectPathMissing(orphanBase);
171174await expectPathMissing(`${orphanBase}-journal`);
@@ -188,7 +191,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
188191await fs.utimes(strandedJournal, old, old);
189192190193const db = openMemoryDatabaseAtPath(dbPath, false);
191-db.close();
194+closeMemoryDatabase(db);
192195193196await expectPathMissing(strandedJournal);
194197});
@@ -207,7 +210,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
207210}
208211209212const db = openMemoryDatabaseAtPath(dbPath, false);
210-db.close();
213+closeMemoryDatabase(db);
211214212215await expect(fs.access(activeBase)).resolves.toBeUndefined();
213216await expect(fs.access(`${activeBase}-wal`)).resolves.toBeUndefined();
@@ -233,7 +236,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
233236234237cleanupAgedMemoryReindexTempFiles(dbPath);
235238const db = openMemoryDatabaseAtPath(dbPath, false);
236-db.close();
239+closeMemoryDatabase(db);
237240238241await expect(fs.access(activeBase)).resolves.toBeUndefined();
239242await expect(fs.access(`${activeBase}-wal`)).resolves.toBeUndefined();
@@ -254,7 +257,7 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
254257await fs.utimes(orphanBase, old, old);
255258256259const db = openMemoryDatabaseAtPath(dbPath, false, true);
257-db.close();
260+closeMemoryDatabase(db);
258261259262await expect(fs.access(orphanBase)).resolves.toBeUndefined();
260263});
@@ -275,6 +278,36 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
275278await expect(fs.access(resolveMemoryReindexLockPath(dbPath))).resolves.toBeUndefined();
276279});
277280281+it("blocks an atomic swap while a live memory database is open", async () => {
282+const dbPath = path.join(fixtureRoot, `case-${caseId++}`, "index.sqlite");
283+await fs.mkdir(path.dirname(dbPath), { recursive: true });
284+const seed = new DatabaseSync(dbPath);
285+seed.close();
286+287+const db = openMemoryDatabaseAtPath(dbPath, false);
288+expect(tryAcquireMemoryReindexSwapLock(dbPath)).toBeUndefined();
289+290+closeMemoryDatabase(db);
291+const swapLock = acquireMemoryReindexSwapLock(dbPath);
292+swapLock.release();
293+});
294+295+it("blocks a live database open while an atomic swap is active", async () => {
296+const dbPath = path.join(fixtureRoot, `case-${caseId++}`, "index.sqlite");
297+await fs.mkdir(path.dirname(dbPath), { recursive: true });
298+const seed = new DatabaseSync(dbPath);
299+seed.close();
300+301+const swapLock = acquireMemoryReindexSwapLock(dbPath);
302+expect(() => openMemoryDatabaseAtPath(dbPath, false)).toThrow(
303+/unavailable during a safe reindex swap/,
304+);
305+swapLock.release();
306+307+const db = openMemoryDatabaseAtPath(dbPath, false);
308+closeMemoryDatabase(db);
309+});
310+278311it("does not block database startup when orphan discovery fails", async () => {
279312const dbPath = path.join(fixtureRoot, `case-${caseId++}`, "index.sqlite");
280313await fs.mkdir(path.dirname(dbPath), { recursive: true });
@@ -285,6 +318,6 @@ describe("openMemoryDatabaseAtPath readOnly probe", () => {
285318});
286319287320const db = openMemoryDatabaseAtPath(dbPath, false);
288-db.close();
321+closeMemoryDatabase(db);
289322});
290323});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。