// Generated, do not edit. sha256:df7d96413775b40f968338398229b7ff06e570855f37d25616828524e6f0575d import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert/strict'; import { createDbHarness } from '../helpers/db-node.mjs'; const harness = createDbHarness({ seedPreferences: true }); mock.module('../../server/db.js', { namedExports: harness.exports }); const { trackUpdate, trackDelete, trackUpdateMany, withOrigin, currentOrigin, rowHistory } = await import('../../server/data/change-log.js'); const db = harness.exports; function account(naam = 'Kluis') { db.run(`INSERT INTO Account (name, searchName, kind, isOwnCustody, scanEnabled, isActive) VALUES (?, ?, 'vault', 1, 0, 1)`, [naam, naam.toLowerCase()]); return db.lastId(); } const logboek = () => db.all(`SELECT * FROM ChangeLog ORDER BY id`); beforeEach(() => harness.recreate()); describe('trackUpdate: wat er veranderde', () => { it('schrijft één regel met alleen de gewijzigde velden', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = ? WHERE id = ?`, ['Nieuwe naam', id])); const regels = logboek(); assert.equal(regels.length, 1); assert.equal(regels[0].tableName, 'Account'); assert.equal(regels[0].rowId, id); assert.equal(regels[0].operation, 'update'); assert.deepEqual(JSON.parse(regels[0].changes), { name: ['Kluis', 'Nieuwe naam'] }, 'alleen wat er veranderde, met oud én nieuw'); }); it('zet updatedAt, en meldt dat niet in zijn eigen regel', () => { const id = account(); assert.equal(db.one(`SELECT updatedAt AS u FROM Account WHERE id = ?`, [id]).u, null); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'Anders' WHERE id = ?`, [id])); assert.ok(db.one(`SELECT updatedAt AS u FROM Account WHERE id = ?`, [id]).u, 'updatedAt hoort gezet'); assert.deepEqual(Object.keys(JSON.parse(logboek()[0].changes)), ['name'], 'updatedAt is boekhouding en geen antwoord op een vraag'); }); it('meldt updatedAt ook niet als de aanroeper hem ZÉLF zet', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'X', updatedAt = '2020-01-01T00:00:00Z' WHERE id = ?`, [id])); assert.deepEqual(Object.keys(JSON.parse(logboek()[0].changes)), ['name'], 'het tijdstempel is boekhouding, ook als de aanroeper hem zelf zette'); }); it('schrijft NIETS als er niets veranderde', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'Kluis' WHERE id = ?`, [id])); assert.deepEqual(logboek(), []); assert.equal(db.one(`SELECT updatedAt AS u FROM Account WHERE id = ?`, [id]).u, null, 'en dan hoort updatedAt ook niet op te schuiven'); }); it('meldt meerdere velden in één regel', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'X', kind = 'bank' WHERE id = ?`, [id])); const regels = logboek(); assert.equal(regels.length, 1, 'één handeling is één regel, ook als er twee velden veranderen'); assert.deepEqual(JSON.parse(regels[0].changes), { name: ['Kluis', 'X'], kind: ['vault', 'bank'] }); }); it('laat een tabel die niet meedoet met rust', () => { trackUpdate('Preference', 1, () => db.run(`UPDATE Preference SET value = '"light"' WHERE key = 'theme'`)); assert.deepEqual(logboek(), []); }); }); describe('trackDelete: wat er verdween', () => { it('legt de hele rij vast en niet een verschil', () => { const id = account('Weg hiermee'); trackDelete('Account', id, () => db.run(`DELETE FROM Account WHERE id = ?`, [id])); const regels = logboek(); assert.equal(regels.length, 1); assert.equal(regels[0].operation, 'delete'); const rij = JSON.parse(regels[0].changes); assert.equal(rij.name, 'Weg hiermee', 'bij een delete bestaat er geen nieuw: wat er verdween ÍS de rij'); assert.equal(rij.kind, 'vault'); }); it('doet niets als de rij niet bestond', () => { trackDelete('Account', 999, () => db.run(`DELETE FROM Account WHERE id = 999`)); assert.deepEqual(logboek(), []); }); }); describe('trackUpdateMany: één regel per geraakte rij', () => { it('schrijft per rij en niet per ronde', () => { const a = account('Een'), b = account('Twee'); trackUpdateMany('Account', [a, b], () => db.run(`UPDATE Account SET kind = 'bank' WHERE id IN (?, ?)`, [a, b])); const regels = logboek(); assert.equal(regels.length, 2, 'anders is "wat is er met DEZE rij gebeurd" onbeantwoordbaar'); assert.deepEqual(regels.map(r => r.rowId), [a, b]); for (const r of regels) assert.deepEqual(JSON.parse(r.changes).kind, ['vault', 'bank']); }); it('slaat de rijen over die niet veranderden', () => { const a = account('Een'), b = account('Twee'); trackUpdateMany('Account', [a, b], () => db.run(`UPDATE Account SET kind = 'bank' WHERE id = ?`, [a])); assert.deepEqual(logboek().map(r => r.rowId), [a]); }); }); describe('withOrigin: door wie', () => { it('staat standaard op handmatig', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'X' WHERE id = ?`, [id])); assert.equal(logboek()[0].origin, 'manual'); assert.equal(logboek()[0].originId, null, 'een handmatige wijziging heeft geen rij die hem aanwijst'); }); it('legt de herkomst en het voorstel vast', () => { const id = account(); withOrigin('ai_proposal', 42, () => trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'X' WHERE id = ?`, [id]))); assert.equal(logboek()[0].origin, 'ai_proposal'); assert.equal(logboek()[0].originId, 42); }); it('herstelt de vorige herkomst, ook als het misgaat', () => { assert.throws(() => withOrigin('import', 7, () => { throw new Error('mislukt'); }), /mislukt/); assert.deepEqual(currentOrigin(), { origin: 'manual', originId: null }); }); it('weigert een herkomst die niet in de enum staat', () => { assert.throws(() => withOrigin('zomaar', null, () => {}), /Unknown origin/); }); }); describe('de datalaag schrijft echt mee', () => { it('legt een gewijzigde rekening vast, via het echte domeinpad', async () => { const { createAccount, updateAccount } = await import('../../server/data/accounts.js'); const id = await createAccount({ name: 'Beurs', kind: 'exchange', isOwnCustody: 0, scanEnabled: 0 }); assert.deepEqual(logboek(), [], 'aanmaken levert geen regel op: dat staat in createdAt'); await updateAccount(id, { name: 'Andere beurs', kind: 'exchange', isOwnCustody: 0, scanEnabled: 0 }); const regels = logboek(); assert.equal(regels.length, 1); assert.equal(regels[0].tableName, 'Account'); assert.equal(regels[0].rowId, id); assert.deepEqual(JSON.parse(regels[0].changes).name, ['Beurs', 'Andere beurs']); }); it('legt een verwijderde rekening vast met zijn hele rij', async () => { const { createAccount, deleteAccount } = await import('../../server/data/accounts.js'); const id = await createAccount({ name: 'Weg', kind: 'vault', isOwnCustody: 1, scanEnabled: 0 }); await deleteAccount(id); const regels = logboek(); assert.equal(regels.length, 1); assert.equal(regels[0].operation, 'delete'); assert.equal(JSON.parse(regels[0].changes).name, 'Weg'); }); }); describe('rowHistory: de leeskant', () => { it('geeft de wijzigingen van één rij, nieuwste eerst', () => { const id = account(); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'Tweede' WHERE id = ?`, [id])); trackUpdate('Account', id, () => db.run(`UPDATE Account SET name = 'Derde' WHERE id = ?`, [id])); const h = rowHistory('Account', id); assert.equal(h.length, 2); assert.deepEqual(h[0].changes.name, ['Tweede', 'Derde'], 'nieuwste eerst'); assert.deepEqual(h[1].changes.name, ['Kluis', 'Tweede']); }); it('geeft niets terug voor een rij zonder geschiedenis', () => { assert.deepEqual(rowHistory('Account', account()), []); }); });