Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,151 @@
|
||||
// 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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user