// Generated, do not edit. sha256:b1e7db08e72ca29ddd3b364141b2e4cd3fc040df65fe9c6f17ee2547ebc6452c import { describe, it, before, after, beforeEach } from 'node:test'; import assert from 'node:assert/strict'; import { DatabaseSync } from 'node:sqlite'; import { mkdtemp, mkdir, rm, readFile, writeFile, access } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; const DIR = await mkdtemp(join(tmpdir(), 'assetapp-restore-')); process.env.ASSETAPP_DATA_DIR = DIR; process.env.ASSETAPP_DB_FILE = join(DIR, 'db.sqlite'); const { initDatabase, dbFile } = await import('../../server/db.js'); const DB_FILE = dbFile(); const { closeDatabase, run, one } = await import('../../server/db/conn.mjs'); const { restoreDatabase } = await import('../../server/db-restore.mjs'); const { SCHEMA } = await import('../../server/db/schema.js'); const { CONFIG } = await import('../../shared/config.js'); const bestaat = async p => { try { await access(p); return true; } catch { return false; } }; async function maakDatabase(pad, { versie = CONFIG.SCHEMA_VERSION, merk = 'nieuw' } = {}) { const db = new DatabaseSync(pad); db.exec(SCHEMA); db.prepare(`INSERT INTO Schema (version) VALUES (?)`).run(BigInt(versie)); db.prepare(`INSERT INTO Label (name, searchName) VALUES (?,?)`).run(merk, merk); db.close(); return readFile(pad); } const huidigMerk = () => { const r = one(`SELECT name FROM Label LIMIT 1`); return r ? r.name : null; }; before(() => initDatabase()); after(async () => { closeDatabase(); await rm(DIR, { recursive: true, force: true }); }); beforeEach(async () => { closeDatabase(); await rm(DB_FILE, { force: true }); for (const b of ['-wal', '-shm']) await rm(DB_FILE + b, { force: true }); await maakDatabase(DB_FILE, { merk: 'origineel' }); initDatabase(); }); describe('restoreDatabase: het gelukkige pad', () => { it('vervangt de database en opent de nieuwe', async () => { const bytes = await maakDatabase(join(DIR, 'bron.sqlite'), { merk: 'teruggezet' }); const r = await restoreDatabase(bytes); assert.equal(r.versie, CONFIG.SCHEMA_VERSION); assert.equal(huidigMerk(), 'teruggezet'); }); it('bewaart de vorige database als .previous', async () => { const bytes = await maakDatabase(join(DIR, 'bron2.sqlite'), { merk: 'teruggezet' }); const r = await restoreDatabase(bytes); assert.ok(await bestaat(r.previous)); const oud = new DatabaseSync(r.previous, { readOnly: true }); assert.equal(oud.prepare(`SELECT name FROM Label LIMIT 1`).get().name, 'origineel'); oud.close(); }); it('laat geen WAL-buren van de vorige database naast de nieuwe staan', async () => { run(`INSERT INTO Label (name, searchName) VALUES ('los','los')`); assert.ok(await bestaat(DB_FILE + '-wal'), 'voorwaarde: er staat een write-ahead log'); const bytes = await maakDatabase(join(DIR, 'bron3.sqlite'), { merk: 'teruggezet' }); await restoreDatabase(bytes); assert.equal(huidigMerk(), 'teruggezet'); assert.equal(one(`SELECT COUNT(*) AS n FROM Label WHERE name = 'los'`).n, 0, 'de teruggezette database bevat niets uit het log van zijn voorganger'); }); it('meldt verweesde verwijzingen in plaats van ze te verzwijgen', async () => { const pad = join(DIR, 'wees.sqlite'); const db = new DatabaseSync(pad, { enableForeignKeyConstraints: false }); db.exec(SCHEMA); db.prepare(`INSERT INTO Schema (version) VALUES (?)`).run(BigInt(CONFIG.SCHEMA_VERSION)); db.prepare(`INSERT INTO WalletSource (accountId, kind, value, createdAt) VALUES (?,?,?,?)`) .run(999n, 'address', 'bc1q', '2026-01-01T00:00:00Z'); db.close(); const r = await restoreDatabase(await readFile(pad)); assert.ok(r.wezen > 0, 'de wees hoort geteld te worden'); }); it('noemt de geheimen, want die reizen niet mee', async () => { const bytes = await maakDatabase(join(DIR, 'bron4.sqlite')); const r = await restoreDatabase(bytes); assert.match(r.geheimenWaarschuwing, /secrets\.json/); }); }); describe('restoreDatabase: een afgekeurd bestand raakt de live database niet aan', () => { async function weigert(bytes, patroon) { await assert.rejects(() => restoreDatabase(bytes), patroon); assert.equal(huidigMerk(), 'origineel', 'de live database is niet aangeraakt'); assert.equal(await bestaat(DB_FILE + '.new'), false, 'en er blijft geen half bestand achter'); } it('weigert een leeg bestand', async () => { await weigert(Buffer.alloc(0), (e) => e.key === 'dbfile.emptyFile'); }); it('weigert iets dat geen SQLite-bestand is', async () => { await weigert(Buffer.from('dit is gewoon tekst, geen database'), (e) => e.key === 'dbfile.notSqlite'); }); it('weigert een SQLite-bestand zonder Schema-tabel', async () => { const pad = join(DIR, 'leeg.sqlite'); const db = new DatabaseSync(pad); db.exec(`CREATE TABLE Iets (id INTEGER PRIMARY KEY) STRICT;`); db.close(); await weigert(await readFile(pad), (e) => e.key === 'dbfile.noSchemaTable'); }); it('weigert een database van vóór de baseline, en verwijst naar het conversiescript', async () => { const bytes = await maakDatabase(join(DIR, 'oud.sqlite'), { versie: 59 }); await weigert(bytes, /baseline-convert/); }); it('weigert een database die nieuwer is dan de app', async () => { const bytes = await maakDatabase(join(DIR, 'toekomst.sqlite'), { versie: CONFIG.SCHEMA_VERSION + 1 }); await weigert(bytes, (e) => e.key === 'dbfile.versionNewerThanApp'); }); it('laat de database ook na een afkeuring gewoon bruikbaar', async () => { await assert.rejects(() => restoreDatabase(Buffer.from('rommel'))); run(`INSERT INTO Label (name, searchName) VALUES ('na','na')`); assert.equal(one(`SELECT COUNT(*) AS n FROM Label WHERE name = 'na'`).n, 1); }); }); describe('restoreDatabase: de wissel loopt stuk', () => { it('laat de app niet zonder database achter, en verandert niets', async () => { const bytes = await maakDatabase(join(DIR, 'nieuw-bij-stuk.sqlite'), { merk: 'nieuw' }); await rm(`${DB_FILE}.previous`, { recursive: true, force: true }); await mkdir(`${DB_FILE}.previous`, { recursive: true }); await writeFile(join(`${DB_FILE}.previous`, 'bezet.txt'), 'x'); try { await assert.rejects(() => restoreDatabase(bytes), 'de wissel hoort te falen'); assert.equal(huidigMerk(), 'origineel', 'de oude database staat er nog en is open'); run(`INSERT INTO Label (name, searchName) VALUES ('na','na')`); assert.equal(one(`SELECT COUNT(*) AS n FROM Label WHERE name = 'na'`).n, 1); assert.equal(await bestaat(`${DB_FILE}.new`), false, 'het tijdelijke bestand is weg'); } finally { await rm(`${DB_FILE}.previous`, { recursive: true, force: true }); } }); });