mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-10 12:32:27 +00:00
test: cover sqlite transaction guardrails
This commit is contained in:
@@ -96,6 +96,7 @@ const legacyStoreMarkers = [
|
||||
{ label: "Matrix sync store JSON", pattern: /\bbot-storage\.json\b/u },
|
||||
{ label: "Matrix storage metadata JSON", pattern: /\bstorage-meta\.json\b/u },
|
||||
{ label: "Matrix inbound dedupe JSON", pattern: /\binbound-dedupe\.json\b/u },
|
||||
{ label: "Matrix startup verification JSON", pattern: /\bstartup-verification\.json\b/u },
|
||||
{
|
||||
label: "Discord model-picker preferences JSON",
|
||||
pattern: /\bmodel-picker-preferences\.json\b/u,
|
||||
|
||||
74
src/infra/sqlite-transaction.test.ts
Normal file
74
src/infra/sqlite-transaction.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { requireNodeSqlite } from "./node-sqlite.js";
|
||||
import { runSqliteImmediateTransactionSync } from "./sqlite-transaction.js";
|
||||
|
||||
const openDatabases: Array<import("node:sqlite").DatabaseSync> = [];
|
||||
|
||||
function createDatabase(): import("node:sqlite").DatabaseSync {
|
||||
const { DatabaseSync } = requireNodeSqlite();
|
||||
const db = new DatabaseSync(":memory:");
|
||||
db.exec("CREATE TABLE entries (id TEXT NOT NULL PRIMARY KEY, value TEXT NOT NULL);");
|
||||
openDatabases.push(db);
|
||||
return db;
|
||||
}
|
||||
|
||||
function readEntries(db: import("node:sqlite").DatabaseSync): string[] {
|
||||
return db
|
||||
.prepare("SELECT id FROM entries ORDER BY id")
|
||||
.all()
|
||||
.map((row) => (row as { id: string }).id);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const db of openDatabases.splice(0)) {
|
||||
db.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe("runSqliteImmediateTransactionSync", () => {
|
||||
it("keeps outer writes when a nested savepoint rolls back", () => {
|
||||
const db = createDatabase();
|
||||
|
||||
runSqliteImmediateTransactionSync(db, () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("outer", "kept");
|
||||
expect(() =>
|
||||
runSqliteImmediateTransactionSync(db, () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("inner", "rolled back");
|
||||
throw new Error("nested failure");
|
||||
}),
|
||||
).toThrow("nested failure");
|
||||
});
|
||||
|
||||
expect(readEntries(db)).toEqual(["outer"]);
|
||||
});
|
||||
|
||||
it("commits nested savepoint writes with the outer transaction", () => {
|
||||
const db = createDatabase();
|
||||
|
||||
runSqliteImmediateTransactionSync(db, () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("outer", "kept");
|
||||
runSqliteImmediateTransactionSync(db, () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("inner", "kept");
|
||||
});
|
||||
});
|
||||
|
||||
expect(readEntries(db)).toEqual(["inner", "outer"]);
|
||||
});
|
||||
|
||||
it("rejects Promise-returning operations and rolls back their synchronous writes", () => {
|
||||
const db = createDatabase();
|
||||
|
||||
expect(() =>
|
||||
runSqliteImmediateTransactionSync(db, async () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("async", "rolled back");
|
||||
return "done";
|
||||
}),
|
||||
).toThrow("must be synchronous");
|
||||
expect(readEntries(db)).toEqual([]);
|
||||
|
||||
runSqliteImmediateTransactionSync(db, () => {
|
||||
db.prepare("INSERT INTO entries(id, value) VALUES (?, ?)").run("after", "works");
|
||||
});
|
||||
expect(readEntries(db)).toEqual(["after"]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user