Files
2026-10-29 11:57:29 +01:00

161 lines
5.9 KiB
JavaScript

// Generated, do not edit. sha256:db9b5ce5c6a3dba8e3c23b58d01a079d11c9184ff6a8915c824e00ac45006d20
import { describe, it, before, after, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
openDatabase, closeDatabase, database, pragmaStand,
run, all, one, lastId, BUSY_TIMEOUT_MS,
} from '../../server/db/conn.mjs';
const DDL = `
CREATE TABLE Ouder (id INTEGER PRIMARY KEY, naam TEXT NOT NULL) STRICT;
CREATE TABLE Kind (
id INTEGER PRIMARY KEY,
ouderId INTEGER NOT NULL REFERENCES Ouder(id) ON DELETE CASCADE,
tekst TEXT NOT NULL
) STRICT;
CREATE INDEX IxKindOuder ON Kind (ouderId);
`;
describe('server/db/conn: de verbinding en zijn PRAGMA\'s', () => {
beforeEach(() => { openDatabase(':memory:'); run(DDL); });
afterEach(() => closeDatabase());
it('dwingt foreign keys écht af, en niet alleen op papier', () => {
run(`INSERT INTO Ouder (naam) VALUES (?)`, ['een']);
const ouderId = lastId();
run(`INSERT INTO Kind (ouderId, tekst) VALUES (?,?)`, [ouderId, 'hoort erbij']);
assert.throws(
() => run(`INSERT INTO Kind (ouderId, tekst) VALUES (?,?)`, [9999, 'wees']),
/FOREIGN KEY constraint failed/);
run(`DELETE FROM Ouder WHERE id = ?`, [ouderId]);
assert.equal(all(`SELECT * FROM Kind`).length, 0);
});
it('TEGENPROEF: zonder handhaving slaagt exact dezelfde INSERT', () => {
const zonder = new DatabaseSync(':memory:', { enableForeignKeyConstraints: false });
zonder.exec(DDL);
zonder.prepare(`INSERT INTO Kind (ouderId, tekst) VALUES (?,?)`).run(9999n, 'wees');
assert.equal(zonder.prepare(`SELECT COUNT(*) AS n FROM Kind`).get().n, 1,
'zonder handhaving hoort de wees er gewoon in te staan; anders bewijst de test hierboven niets');
zonder.close();
});
it('zet de vier PRAGMA\'s op de verbinding', () => {
const stand = pragmaStand();
assert.equal(stand.foreignKeys, true);
assert.equal(stand.journalMode, 'memory');
assert.equal(stand.synchronous, 1);
assert.equal(stand.busyTimeout, BUSY_TIMEOUT_MS);
});
it('weigert een tweede verbinding zolang de eerste openstaat', () => {
assert.throws(() => openDatabase(':memory:'), /already open/);
});
it('geeft gewone objecten terug, geen rijen met null-prototype', () => {
run(`INSERT INTO Ouder (naam) VALUES (?)`, ['een']);
const rij = one(`SELECT * FROM Ouder`);
assert.equal(Object.getPrototypeOf(rij), Object.prototype);
assert.deepEqual(rij, { id: 1, naam: 'een' });
});
it('geeft null bij one() zonder treffer, en null uit lastId() zonder insert', () => {
assert.equal(one(`SELECT * FROM Ouder WHERE id = ?`, [404]), null);
});
});
describe('server/db/conn: bindwaarden gelijk aan wat de browser deed', () => {
beforeEach(() => {
openDatabase(':memory:');
run(`CREATE TABLE Waarde (id INTEGER PRIMARY KEY, tekst TEXT, getal INTEGER) STRICT;`);
});
afterEach(() => closeDatabase());
it('maakt van undefined een NULL in plaats van te gooien', () => {
run(`INSERT INTO Waarde (tekst, getal) VALUES (?,?)`, [undefined, 1]);
assert.equal(one(`SELECT tekst FROM Waarde`).tekst, null);
});
it('maakt van true/false een 1/0', () => {
run(`INSERT INTO Waarde (tekst, getal) VALUES (?,?)`, ['x', true]);
run(`INSERT INTO Waarde (tekst, getal) VALUES (?,?)`, ['y', false]);
assert.deepEqual(all(`SELECT getal FROM Waarde ORDER BY id`).map(r => r.getal), [1, 0]);
});
it('schrijft een heel getal in een TEXT-kolom als \'1\' en niet als \'1.0\'', () => {
run(`INSERT INTO Waarde (tekst) VALUES (?)`, [1]);
assert.equal(one(`SELECT tekst FROM Waarde`).tekst, '1');
});
});
describe('server/db/conn: run() zonder params voert meerdere statements uit', () => {
beforeEach(() => openDatabase(':memory:'));
afterEach(() => closeDatabase());
it('voert een meerdelige DDL-string volledig uit', () => {
run(DDL);
const tabellen = all(`SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name`).map(r => r.name);
assert.deepEqual(tabellen, ['Kind', 'Ouder']);
});
it('voert met params alleen het eerste statement uit', () => {
run(`CREATE TABLE A (id INTEGER PRIMARY KEY, t TEXT) STRICT;`);
run(`INSERT INTO A (t) VALUES (?); INSERT INTO A (t) VALUES ('tweede');`, ['eerste']);
assert.deepEqual(all(`SELECT t FROM A`).map(r => r.t), ['eerste']);
});
});
describe('server/db/conn: op een echt bestand', () => {
let dir, pad;
before(async () => {
dir = await mkdtemp(join(tmpdir(), 'assetapp-db-'));
pad = join(dir, 'test.sqlite');
});
after(async () => {
closeDatabase();
await rm(dir, { recursive: true, force: true });
});
it('zet WAL aan, en die blijft in het bestand staan', () => {
openDatabase(pad);
run(DDL);
assert.equal(pragmaStand().journalMode, 'wal');
closeDatabase();
const kaal = new DatabaseSync(pad);
assert.equal(kaal.prepare(`PRAGMA journal_mode`).get().journal_mode, 'wal');
kaal.close();
});
it('zet synchronous en busy_timeout élke keer opnieuw, want die zijn NIET persistent', () => {
const kaal = new DatabaseSync(pad);
assert.equal(kaal.prepare(`PRAGMA synchronous`).get().synchronous, 2);
assert.equal(kaal.prepare(`PRAGMA busy_timeout`).get().timeout, 0);
kaal.close();
openDatabase(pad);
const stand = pragmaStand();
assert.equal(stand.synchronous, 1);
assert.equal(stand.busyTimeout, BUSY_TIMEOUT_MS);
closeDatabase();
});
it('bewaart de data tussen twee sessies door, zonder persist()', () => {
openDatabase(pad);
run(`INSERT INTO Ouder (naam) VALUES (?)`, ['blijft staan']);
closeDatabase();
openDatabase(pad);
assert.equal(one(`SELECT naam FROM Ouder ORDER BY id DESC`).naam, 'blijft staan');
assert.equal(database().constructor.name, 'DatabaseSync');
closeDatabase();
});
});