Skip to content

Commit 82d29f4

Browse files
committed
Add comprehensive error handling tests and Node.js compatibility tests
- Introduced a new test suite for error handling in the DatabaseSync class, covering various constraint violations (PRIMARY KEY, UNIQUE, NOT NULL, FOREIGN KEY, CHECK), SQL syntax errors, operations on non-existent tables and columns, invalid operations, and type conversion errors. - Added tests to ensure the database remains usable after constraint errors and that statements remain functional after parameter binding errors. - Implemented tests for user-defined functions and resource management, including handling of moderate-sized text and BLOB data. - Created a new test suite for Node.js compatibility, verifying API surface, behavior comparison with Node.js built-in SQLite, and extended features. - Ensured that constants match between our implementation and Node.js, and tested performance characteristics for basic operations and large result sets.
1 parent 3343dea commit 82d29f4

File tree

4 files changed

+1833
-17
lines changed

4 files changed

+1833
-17
lines changed

test/backup.test.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ describe("Backup functionality", () => {
7878
// Check that tables exist
7979
const tables = destDb
8080
.prepare(
81-
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name"
81+
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name",
8282
)
8383
.all() as { name: string }[];
8484
expect(tables).toEqual([{ name: "products" }, { name: "users" }]);
@@ -183,7 +183,7 @@ describe("Backup functionality", () => {
183183
// Verify main database tables
184184
const tables = destDb
185185
.prepare(
186-
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name"
186+
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name",
187187
)
188188
.all() as { name: string }[];
189189
expect(tables).toEqual([{ name: "products" }, { name: "users" }]);
@@ -196,21 +196,21 @@ describe("Backup functionality", () => {
196196

197197
it("should handle errors for invalid destination", async () => {
198198
await expect(
199-
sourceDb.backup("/invalid/path/that/does/not/exist/backup.db")
199+
sourceDb.backup("/invalid/path/that/does/not/exist/backup.db"),
200200
).rejects.toThrow();
201201
});
202202

203203
it("should handle errors for closed database", async () => {
204204
sourceDb.close();
205205
await expect(sourceDb.backup(destPath)).rejects.toThrow(
206-
"database is not open"
206+
"database is not open",
207207
);
208208
});
209209

210210
it("should reject invalid options", async () => {
211211
// Invalid progress callback
212212
await expect(
213-
sourceDb.backup(destPath, { progress: "not a function" as any })
213+
sourceDb.backup(destPath, { progress: "not a function" as any }),
214214
).rejects.toThrow("must be a function");
215215
});
216216

@@ -302,7 +302,7 @@ describe("Backup functionality", () => {
302302
// Verify all tables exist
303303
const tables = restoredDb
304304
.prepare(
305-
"SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view') ORDER BY type, name"
305+
"SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view') ORDER BY type, name",
306306
)
307307
.all() as { name: string; type: string }[];
308308
expect(tables).toEqual([
@@ -316,7 +316,7 @@ describe("Backup functionality", () => {
316316
// Verify ALL indexes exist
317317
const indexes = restoredDb
318318
.prepare(
319-
"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND name LIKE 'idx_%' ORDER BY name"
319+
"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND name LIKE 'idx_%' ORDER BY name",
320320
)
321321
.all() as { name: string; sql: string }[];
322322
expect(indexes.length).toBe(3);
@@ -328,7 +328,7 @@ describe("Backup functionality", () => {
328328
// Verify trigger exists and its definition
329329
const triggers = restoredDb
330330
.prepare(
331-
"SELECT name, sql FROM sqlite_master WHERE type = 'trigger' ORDER BY name"
331+
"SELECT name, sql FROM sqlite_master WHERE type = 'trigger' ORDER BY name",
332332
)
333333
.all() as { name: string; sql: string }[];
334334
expect(triggers.length).toBe(1);
@@ -338,7 +338,7 @@ describe("Backup functionality", () => {
338338
// Verify CHECK constraints by examining table schema
339339
const ordersSql = restoredDb
340340
.prepare(
341-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'orders'"
341+
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'orders'",
342342
)
343343
.get() as { sql: string };
344344
expect(ordersSql.sql).toContain("CHECK (quantity > 0)");
@@ -397,15 +397,15 @@ describe("Backup functionality", () => {
397397
expect(() => {
398398
restoredDb
399399
.prepare(
400-
"INSERT INTO orders (product_id, quantity, total) VALUES (1, 0, 10)"
400+
"INSERT INTO orders (product_id, quantity, total) VALUES (1, 0, 10)",
401401
)
402402
.run();
403403
}).toThrow(); // Should fail due to CHECK (quantity > 0)
404404

405405
expect(() => {
406406
restoredDb
407407
.prepare(
408-
"INSERT INTO orders (product_id, quantity, total) VALUES (1, 1, -10)"
408+
"INSERT INTO orders (product_id, quantity, total) VALUES (1, 1, -10)",
409409
)
410410
.run();
411411
}).toThrow(); // Should fail due to CHECK (total >= 0)
@@ -418,7 +418,7 @@ describe("Backup functionality", () => {
418418
expect(() => {
419419
restoredDb
420420
.prepare(
421-
"INSERT INTO orders (product_id, quantity, total) VALUES (999, 1, 10)"
421+
"INSERT INTO orders (product_id, quantity, total) VALUES (999, 1, 10)",
422422
)
423423
.run();
424424
}).toThrow(); // Should fail due to foreign key constraint
@@ -515,7 +515,7 @@ describe("Backup functionality", () => {
515515
// Insert enough data to create multiple pages (SQLite default page size is usually 4096 bytes)
516516
const largeText = "x".repeat(1000); // 1KB of text per row
517517
const insertStmt = sourceDb.prepare(
518-
"INSERT INTO large_data (data, padding) VALUES (?, ?)"
518+
"INSERT INTO large_data (data, padding) VALUES (?, ?)",
519519
);
520520

521521
for (let i = 0; i < 100; i++) {
@@ -556,7 +556,7 @@ describe("Backup functionality", () => {
556556
// Verify we had multiple progress callbacks
557557
expect(progressCalls.length).toBeGreaterThan(0);
558558
expect(progressCalls.length).toBeGreaterThanOrEqual(
559-
Math.floor(totalPages / pagesPerStep) - 1
559+
Math.floor(totalPages / pagesPerStep) - 1,
560560
);
561561

562562
// Verify progress is incremental
@@ -581,14 +581,14 @@ describe("Backup functionality", () => {
581581
const firstCall = progressCalls[0];
582582
expect(firstCall.totalPages).toBe(totalPages);
583583
expect(firstCall.remainingPages).toBeGreaterThan(
584-
totalPages - pagesPerStep - 1
584+
totalPages - pagesPerStep - 1,
585585
);
586586
}
587587

588588
// Verify timing - with small page sizes, the backup should take some measurable time
589589
// due to multiple iterations (though this is environment-dependent)
590590
console.log(
591-
`Backup took ${duration}ms with ${progressCalls.length} progress callbacks`
591+
`Backup took ${duration}ms with ${progressCalls.length} progress callbacks`,
592592
);
593593

594594
// Verify the backup is complete and valid
@@ -717,7 +717,7 @@ describe("Backup functionality", () => {
717717

718718
// Verify different behaviors
719719
console.log(
720-
`Callbacks - All at once: ${callbackCount1}, One page: ${callbackCount2}, Five pages: ${callbackCount3}`
720+
`Callbacks - All at once: ${callbackCount1}, One page: ${callbackCount2}, Five pages: ${callbackCount3}`,
721721
);
722722

723723
// With rate=-1, we should get 0 or very few callbacks (maybe just 1)

0 commit comments

Comments
 (0)