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

151 lines
8.0 KiB
JavaScript

// Generated, do not edit. sha256:52052b11142a17d483d54341c9f2ba38a354f77829e27fbdc3fddff2dd5437b6
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert/strict';
import { createDbHarness } from '../helpers/db-node.mjs';
const harness = createDbHarness();
mock.module('../../server/db.js', { namedExports: harness.exports });
const {
createEvent, eventAccountIdsByAsset, eventAssetIdsByAccount, eventDaysByAccount, eventsCountByAccount,
eventsNetByAccount, eventsWithoutImportRef, firstTaxEventDate, listEvents,
} = await import('../../server/data/events.js');
const {
bookReservation, createReservation, deleteReservation, listDueReservations, listReservations,
listReservationsForAccount, updateReservation,
} = await import('../../server/data/reservations.js');
let eur, bank, spaar;
beforeEach(() => {
harness.recreate();
harness.exports.run(
`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit) VALUES ('EUR','Euro','euro','fiat','EUR','cent',2,'unit')`);
eur = harness.exports.lastId();
harness.exports.run(`INSERT INTO Account (name, searchName, kind, isOwnCustody, scanEnabled, isActive, closedAt) VALUES ('Bank','bank','bank',0,0,1,NULL)`);
bank = harness.exports.lastId();
harness.exports.run(`INSERT INTO Account (name, searchName, kind, isOwnCustody, scanEnabled, isActive, closedAt) VALUES ('Spaar','spaar','bank',0,0,1,NULL)`);
spaar = harness.exports.lastId();
});
const rij = (id) => harness.exports.one(`SELECT * FROM Event WHERE id = ?`, [id]);
const reservering = (extra = {}) => createReservation({
accountId: bank, assetId: eur, direction: 'out', amount: 50_000,
occurredAt: '2026-12-01T00:00:00Z', note: 'aanslag', ...extra,
});
const isNietOpen = e => e.key === 'reservations.notOpen';
describe('vastleggen', () => {
it('zet een afschrijving op het van-account en een bijboeking op het naar-account', async () => {
const uit = await reservering();
const bij = await reservering({ direction: 'in', amount: 10_000 });
assert.deepEqual([rij(uit).type, rij(uit).fromAccountId, rij(uit).toAccountId], ['reservation', bank, null]);
assert.deepEqual([rij(bij).fromAccountId, rij(bij).toAccountId], [null, bank]);
assert.deepEqual(listReservationsForAccount(bank).map(r => r.id), [uit, bij]);
assert.deepEqual(listReservationsForAccount(spaar), []);
});
it('weigert een onbekende richting en een reservering zonder account', async () => {
await assert.rejects(() => reservering({ direction: 'opzij' }), /direction/);
await assert.rejects(() => reservering({ accountId: null }), /account/);
});
it('weigert in de datalaag én in de DDL een reservering met twee accounts of met een prijs', async () => {
const basis = { assetId: eur, type: 'reservation', occurredAt: '2026-12-01T00:00:00Z', amount: 1, source: 'manual' };
await assert.rejects(() => createEvent({ ...basis, fromAccountId: bank, toAccountId: spaar }), /exactly one account/);
await assert.rejects(() => createEvent({ ...basis, fromAccountId: bank, fiatPrice: 1 }), /no price/);
assert.throws(() => harness.exports.run(
`INSERT INTO Event (assetId, fromAccountId, toAccountId, amount, type, source, occurredAt, createdAt)
VALUES (?, ?, ?, 1, 'reservation', 'manual', '2026-12-01T00:00:00Z', '2026-10-23T00:00:00Z')`,
[eur, bank, spaar]), /CHECK/);
});
});
describe('telt nergens mee zolang hij niet geboekt is', () => {
it('blijft buiten saldo, lijsten, importpool en eerste boeking', async () => {
await reservering({ occurredAt: '2025-01-01T00:00:00Z' });
assert.equal(eventsNetByAccount(bank, eur, null), 0);
assert.equal(eventsCountByAccount(bank, eur, null), 0);
assert.deepEqual(listEvents(), []);
assert.deepEqual(eventsWithoutImportRef(), []);
assert.deepEqual(eventAssetIdsByAccount(bank), []);
assert.deepEqual(eventAccountIdsByAsset(eur), []);
assert.deepEqual(eventDaysByAccount(bank, eur, '2024-01-01', '2026-01-01'), []);
assert.equal(firstTaxEventDate(), null);
});
});
describe('vervallen', () => {
it('geeft alleen de reserveringen waarvan de datum bereikt is', async () => {
const vroeg = await reservering({ occurredAt: '2026-01-01T00:00:00Z' });
await reservering({ occurredAt: '2027-01-01T00:00:00Z' });
assert.deepEqual(listDueReservations('2026-06-01T00:00:00Z').map(r => r.id), [vroeg]);
assert.equal(listReservations().length, 2);
});
});
describe('boeken', () => {
it('maakt van dezelfde rij een verplaatsing met de echte datum en het echte bedrag', async () => {
const id = await reservering();
await bookReservation(id, { occurredAt: '2026-12-03T10:00:00Z', amount: 48_000, otherAccountId: null });
const e = rij(id);
assert.deepEqual([e.type, e.occurredAt, e.amount, e.fromAccountId, e.toAccountId, e.note],
['transfer', '2026-12-03T10:00:00Z', 48_000, bank, null, 'aanslag']);
assert.equal(eventsNetByAccount(bank, eur, null), -48_000, 'nu telt hij wel');
assert.deepEqual(listReservations(), []);
});
it('zet een tegenaccount aan de andere kant, ook bij een bijboeking', async () => {
const uit = await reservering();
await bookReservation(uit, { occurredAt: '2026-12-01T00:00:00Z', amount: 50_000, otherAccountId: spaar });
assert.deepEqual([rij(uit).fromAccountId, rij(uit).toAccountId], [bank, spaar]);
const bij = await reservering({ direction: 'in' });
await bookReservation(bij, { occurredAt: '2026-12-01T00:00:00Z', amount: 50_000, otherAccountId: spaar });
assert.deepEqual([rij(bij).fromAccountId, rij(bij).toAccountId], [spaar, bank]);
});
it('weigert een tweede keer boeken, en laat de eerste boeking staan', async () => {
const id = await reservering();
await bookReservation(id, { occurredAt: '2026-12-01T00:00:00Z', amount: 50_000 });
await assert.rejects(() => bookReservation(id, { occurredAt: '2026-12-09T00:00:00Z', amount: 1 }), isNietOpen);
assert.equal(rij(id).amount, 50_000);
});
it('weigert het eigen account als tegenkant, een bedrag van nul en een ongeldige datum', async () => {
const id = await reservering();
await assert.rejects(() => bookReservation(id, { occurredAt: '2026-12-01T00:00:00Z', amount: 50_000, otherAccountId: bank }), /another account/);
await assert.rejects(() => bookReservation(id, { occurredAt: '2026-12-01T00:00:00Z', amount: 0 }), /amount > 0/);
await assert.rejects(() => bookReservation(id, { occurredAt: 'morgen', amount: 1 }), /occurredAt/);
assert.equal(rij(id).type, 'reservation', 'en hij staat nog open');
});
});
describe('een gewone boeking is via deze module niet aan te raken', () => {
it('weigert boeken, bewerken en verwijderen van een verplaatsing', async () => {
const t = await createEvent({ assetId: eur, type: 'transfer', occurredAt: '2026-01-01T00:00:00Z', amount: 100,
fromAccountId: bank, toAccountId: spaar, source: 'manual' });
await assert.rejects(() => bookReservation(t, { occurredAt: '2026-01-02T00:00:00Z', amount: 1 }), isNietOpen);
await assert.rejects(() => updateReservation(t, { accountId: bank, assetId: eur, direction: 'out', amount: 1,
occurredAt: '2026-01-02T00:00:00Z' }), isNietOpen);
await assert.rejects(() => deleteReservation(t), isNietOpen);
assert.deepEqual([rij(t).type, rij(t).amount], ['transfer', 100]);
});
});
describe('bewerken en verwijderen', () => {
it('bewerkt bedrag, datum, richting en omschrijving, en blijft een reservering', async () => {
const id = await reservering();
await updateReservation(id, { accountId: bank, assetId: eur, direction: 'in', amount: 7, occurredAt: '2027-02-01T00:00:00Z', note: 'teruggave' });
const e = rij(id);
assert.deepEqual([e.type, e.amount, e.occurredAt, e.fromAccountId, e.toAccountId, e.note],
['reservation', 7, '2027-02-01T00:00:00Z', null, bank, 'teruggave']);
});
it('verwijdert hem zonder boeking', async () => {
const id = await reservering();
await deleteReservation(id);
assert.ok(!rij(id));
await assert.rejects(() => deleteReservation(id), isNietOpen, 'een tweede keer is er niets meer');
});
});