Files
AtreidesAssetsFree/tests/repo/change-log.test.mjs
T

191 lines
8.0 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// 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()), []);
});
});