Skip to content

Commit 1520fdd

Browse files
committed
Refactor test utilities and improve path handling in tests
- Introduced `useTempDir` utility for managing temporary directories in tests, replacing manual directory creation and cleanup. - Updated path handling in `path-validation.test.ts` and `sessions.test.ts` to utilize the new `useTempDir` utility. - Consolidated worker thread tests to use `writeWorkerScript` for creating worker scripts, improving readability and maintainability. - Enhanced error handling in worker thread tests to ensure clear error messages when accessing databases across threads. - Adjusted TypeScript configuration to set the root directory for better project structure.
1 parent 2ae6f2f commit 1520fdd

19 files changed

+1724
-309
lines changed

test/backup-restoration.test.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
11
import { expect } from "@jest/globals";
2-
import * as fs from "fs";
3-
import * as os from "os";
4-
import * as path from "path";
2+
import * as fs from "node:fs";
53
import { DatabaseSync } from "../src/index";
4+
import { useTempDir } from "./test-utils";
65

76
describe("Backup Restoration", () => {
8-
let tmpDir: string;
7+
const { getDbPath, closeDatabases } = useTempDir("sqlite-backup-test-");
8+
99
let sourceDb: InstanceType<typeof DatabaseSync>;
1010
let sourceFile: string;
1111
let backupFile: string;
1212

1313
beforeEach(() => {
14-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sqlite-backup-test-"));
15-
sourceFile = path.join(tmpDir, "source.db");
16-
backupFile = path.join(tmpDir, "backup.db");
14+
sourceFile = getDbPath("source.db");
15+
backupFile = getDbPath("backup.db");
1716
sourceDb = new DatabaseSync(sourceFile);
1817
});
1918

2019
afterEach(() => {
21-
try {
22-
if (sourceDb) sourceDb.close();
23-
} catch {
24-
// Ignore errors when closing
25-
}
26-
fs.rmSync(tmpDir, { recursive: true, force: true });
20+
closeDatabases(sourceDb);
2721
});
2822

2923
it("should restore data correctly from backup", async () => {
@@ -232,7 +226,7 @@ describe("Backup Restoration", () => {
232226
`);
233227

234228
// First backup
235-
const backup1 = path.join(tmpDir, "backup1.db");
229+
const backup1 = getDbPath("backup1.db");
236230
await sourceDb.backup(backup1);
237231

238232
// Add more data
@@ -241,7 +235,7 @@ describe("Backup Restoration", () => {
241235
);
242236

243237
// Second backup
244-
const backup2 = path.join(tmpDir, "backup2.db");
238+
const backup2 = getDbPath("backup2.db");
245239
await sourceDb.backup(backup2);
246240

247241
// Add final data

test/backup.test.ts

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
1+
import { describe, expect, it } from "@jest/globals";
22
import * as fs from "node:fs";
3-
import * as fsp from "node:fs/promises";
4-
import * as os from "node:os";
5-
import * as path from "node:path";
63
import { DatabaseSync } from "../src";
4+
import { createTestDb, useTempDir } from "./test-utils";
75

86
describe("Backup functionality", () => {
7+
const { getDbPath, closeDatabases } = useTempDir("sqlite-backup-test-", {
8+
waitForWindows: true,
9+
});
10+
911
let sourceDb: InstanceType<typeof DatabaseSync>;
10-
let tmpDir: string;
1112
let sourcePath: string;
1213
let destPath: string;
1314

1415
beforeEach(() => {
15-
// Create temporary directory for test databases
16-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sqlite-backup-test-"));
17-
sourcePath = path.join(tmpDir, "source.db");
18-
destPath = path.join(tmpDir, "destination.db");
16+
sourcePath = getDbPath("source.db");
17+
destPath = getDbPath("destination.db");
1918

2019
// Create and populate source database
21-
sourceDb = new DatabaseSync(sourcePath);
22-
sourceDb.exec(`
20+
sourceDb = createTestDb(
21+
sourcePath,
22+
`
2323
CREATE TABLE users (
2424
id INTEGER PRIMARY KEY,
2525
name TEXT NOT NULL,
@@ -39,30 +39,13 @@ describe("Backup functionality", () => {
3939
('Widget', 9.99),
4040
('Gadget', 19.99),
4141
('Doohickey', 29.99);
42-
`);
42+
`,
43+
);
4344
});
4445

45-
afterEach(async () => {
46-
try {
47-
sourceDb?.close();
48-
} catch {
49-
// Ignore close errors during cleanup
50-
}
51-
52-
// Wait a bit for Windows file handles to be released
53-
if (process.platform === "win32") {
54-
await new Promise((resolve) => setTimeout(resolve, 100));
55-
}
56-
57-
if (fs.existsSync(tmpDir)) {
58-
await fsp.rm(tmpDir, {
59-
recursive: true,
60-
force: true,
61-
maxRetries: 3,
62-
retryDelay: 500,
63-
});
64-
}
65-
}, 10000); // Increase timeout to 10 seconds
46+
afterEach(() => {
47+
closeDatabases(sourceDb);
48+
});
6649

6750
it("should create a backup of the database", async () => {
6851
// Perform backup
@@ -163,7 +146,7 @@ describe("Backup functionality", () => {
163146

164147
it("should handle backup with attached databases", async () => {
165148
// Attach another database
166-
const attachedPath = path.join(tmpDir, "attached.db");
149+
const attachedPath = getDbPath("attached.db");
167150
sourceDb.exec(`ATTACH DATABASE '${attachedPath}' AS attached`);
168151
sourceDb.exec(`
169152
CREATE TABLE attached.extra_data (
@@ -215,7 +198,7 @@ describe("Backup functionality", () => {
215198
});
216199

217200
it("should handle concurrent backups", async () => {
218-
const destPath2 = path.join(tmpDir, "destination2.db");
201+
const destPath2 = getDbPath("destination2.db");
219202

220203
// Start two backups concurrently
221204
const [pages1, pages2] = await Promise.all([
@@ -285,7 +268,7 @@ describe("Backup functionality", () => {
285268
`);
286269

287270
// Perform backup
288-
const backupPath = path.join(tmpDir, "full_backup.db");
271+
const backupPath = getDbPath("full_backup.db");
289272
const totalPages = await sourceDb.backup(backupPath);
290273
expect(totalPages).toBeGreaterThan(0);
291274

@@ -463,7 +446,7 @@ describe("Backup functionality", () => {
463446
};
464447

465448
// Perform backup
466-
const backupPath = path.join(tmpDir, "pragma_backup.db");
449+
const backupPath = getDbPath("pragma_backup.db");
467450
await sourceDb.backup(backupPath);
468451
sourceDb.close();
469452

@@ -536,7 +519,7 @@ describe("Backup functionality", () => {
536519
timestamp: number;
537520
}> = [];
538521

539-
const backupPath = path.join(tmpDir, "pages_test.db");
522+
const backupPath = getDbPath("pages_test.db");
540523
const startTime = Date.now();
541524

542525
// Use a small rate to ensure multiple iterations
@@ -614,7 +597,7 @@ describe("Backup functionality", () => {
614597

615598
it("should handle incremental backup simulation", async () => {
616599
// Create initial backup
617-
const backup1Path = path.join(tmpDir, "backup1.db");
600+
const backup1Path = getDbPath("backup1.db");
618601
await sourceDb.backup(backup1Path);
619602

620603
// Add more data
@@ -624,7 +607,7 @@ describe("Backup functionality", () => {
624607
`);
625608

626609
// Create second backup
627-
const backup2Path = path.join(tmpDir, "backup2.db");
610+
const backup2Path = getDbPath("backup2.db");
628611
await sourceDb.backup(backup2Path);
629612

630613
// Don't close sourceDb here - let afterEach handle it
@@ -679,7 +662,7 @@ describe("Backup functionality", () => {
679662
}
680663

681664
// Test 1: Backup with rate = -1 (all at once)
682-
const backup1Path = path.join(tmpDir, "all_at_once.db");
665+
const backup1Path = getDbPath("all_at_once.db");
683666
let callbackCount1 = 0;
684667

685668
await sourceDb.backup(backup1Path, {
@@ -690,7 +673,7 @@ describe("Backup functionality", () => {
690673
});
691674

692675
// Test 2: Backup with rate = 1 (one page at a time)
693-
const backup2Path = path.join(tmpDir, "one_page.db");
676+
const backup2Path = getDbPath("one_page.db");
694677
let callbackCount2 = 0;
695678
const progressInfo2: Array<{ remaining: number }> = [];
696679

@@ -703,7 +686,7 @@ describe("Backup functionality", () => {
703686
});
704687

705688
// Test 3: Backup with rate = 5
706-
const backup3Path = path.join(tmpDir, "five_pages.db");
689+
const backup3Path = getDbPath("five_pages.db");
707690
let callbackCount3 = 0;
708691
const progressInfo3: Array<{ remaining: number }> = [];
709692

test/concurrent-access.test.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
1-
import * as fs from "node:fs";
2-
import * as os from "node:os";
3-
import * as path from "node:path";
41
import { DatabaseSync } from "../src";
2+
import { useTempDir } from "./test-utils";
53

64
describe("Concurrent Access Patterns Tests", () => {
7-
let tempDir: string;
5+
const { getDbPath } = useTempDir("sqlite-concurrent-test-");
86
let dbPath: string;
97

108
beforeEach(() => {
11-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "sqlite-concurrent-test-"));
12-
dbPath = path.join(tempDir, "concurrent.db");
13-
});
14-
15-
afterEach(() => {
16-
try {
17-
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
18-
fs.rmdirSync(tempDir);
19-
} catch {
20-
// Ignore cleanup errors
21-
}
9+
dbPath = getDbPath("concurrent.db");
2210
});
2311

2412
describe("Multiple Reader Pattern", () => {

test/concurrent-simple.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { describe, expect, it } from "@jest/globals";
2+
import { DatabaseSync } from "../src";
3+
import { useTempDir } from "./test-utils";
4+
5+
describe("Simple Concurrent Access Tests", () => {
6+
const { getDbPath } = useTempDir("sqlite-concurrent-simple-");
7+
let dbPath: string;
8+
9+
beforeEach(() => {
10+
dbPath = getDbPath("test.db");
11+
});
12+
13+
it("should handle multiple database connections", () => {
14+
// Create database with first connection
15+
const db1 = new DatabaseSync(dbPath);
16+
db1.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
17+
db1.exec("INSERT INTO test (value) VALUES ('hello')");
18+
19+
// Open second connection for reading
20+
const db2 = new DatabaseSync(dbPath, { readOnly: true });
21+
const result = db2.prepare("SELECT * FROM test").get();
22+
expect(result.value).toBe("hello");
23+
24+
// Clean up
25+
db1.close();
26+
db2.close();
27+
});
28+
29+
it("should handle WAL mode for concurrent access", () => {
30+
// Create database and enable WAL
31+
const db1 = new DatabaseSync(dbPath);
32+
db1.exec("PRAGMA journal_mode = WAL");
33+
db1.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value INTEGER)");
34+
35+
// Insert data
36+
for (let i = 0; i < 10; i++) {
37+
db1.exec(`INSERT INTO test (value) VALUES (${i})`);
38+
}
39+
40+
// Open multiple readers
41+
const readers = [];
42+
for (let i = 0; i < 3; i++) {
43+
const reader = new DatabaseSync(dbPath, { readOnly: true });
44+
readers.push(reader);
45+
}
46+
47+
// All readers should see the same data
48+
for (const reader of readers) {
49+
const count = reader.prepare("SELECT COUNT(*) as count FROM test").get();
50+
expect(count.count).toBe(10);
51+
}
52+
53+
// Clean up
54+
db1.close();
55+
readers.forEach((r) => r.close());
56+
});
57+
58+
it("should handle rapid open/close cycles", () => {
59+
// Initialize database
60+
const initDb = new DatabaseSync(dbPath);
61+
initDb.exec("CREATE TABLE test (id INTEGER PRIMARY KEY)");
62+
initDb.close();
63+
64+
// Rapid open/close cycles
65+
for (let i = 0; i < 20; i++) {
66+
const db = new DatabaseSync(dbPath);
67+
const result = db.prepare("SELECT COUNT(*) as count FROM test").get();
68+
expect(result.count).toBe(0);
69+
db.close();
70+
}
71+
});
72+
73+
it("should handle concurrent prepared statements", () => {
74+
const db = new DatabaseSync(":memory:");
75+
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
76+
77+
// Create multiple prepared statements
78+
const insertStmt = db.prepare("INSERT INTO test (value) VALUES (?)");
79+
const selectStmt = db.prepare("SELECT * FROM test WHERE value = ?");
80+
const countStmt = db.prepare("SELECT COUNT(*) as count FROM test");
81+
82+
// Use them concurrently
83+
insertStmt.run("test1");
84+
insertStmt.run("test2");
85+
insertStmt.run("test3");
86+
87+
const count = countStmt.get();
88+
expect(count.count).toBe(3);
89+
90+
const result = selectStmt.get("test2");
91+
expect(result.value).toBe("test2");
92+
93+
// Clean up
94+
insertStmt.finalize();
95+
selectStmt.finalize();
96+
countStmt.finalize();
97+
db.close();
98+
});
99+
});

test/extension-loading.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { execSync } from "child_process";
22
import * as fs from "fs";
33
import * as path from "path";
44
import { DatabaseSync } from "../src";
5+
import { getDirname } from "./test-utils";
56

67
describe("Extension Loading Tests", () => {
78
// Build the test extension before running tests
89
let testExtensionPath: string;
910

1011
beforeAll(() => {
11-
const extensionDir = path.join(__dirname, "fixtures", "test-extension");
12+
const extensionDir = path.join(getDirname(), "fixtures", "test-extension");
1213

1314
// Build the extension
1415
try {
@@ -360,7 +361,7 @@ describe("Extension Loading Tests", () => {
360361
});
361362

362363
test("can load extension in file-based database", () => {
363-
const dbPath = path.join(__dirname, "test-extension.db");
364+
const dbPath = path.join(getDirname(), "test-extension.db");
364365

365366
// Clean up any existing file
366367
if (fs.existsSync(dbPath)) {

0 commit comments

Comments
 (0)