Files

277 lines
13 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:60e26a3323cff9f6656d123b1222be3bdc643c67ea3cef033160914cd9a62cad
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
dailyProvenance, provenanceCounts, provenanceRuns, provenanceOn, provenanceOfRow, originFrom, liveOrigin, PROVENANCE_KINDS,
} from '../../shared/price-provenance.js';
const MANUAL = 99;
let seq = 0;
const dag = (d, fiatPrice, extra = {}) => ({
id: ++seq, observedAt: `${d}T00:00:00Z`, resolution: 'day', fiatPrice, providerId: 1,
createdAt: `${d}T00:00:00Z`.replace(d, volgendeDag(d)), updatedAt: null, ...extra,
});
const spot = (d, tijd, fiatPrice, extra = {}) => ({
id: ++seq, observedAt: `${d}T${tijd}:00.000Z`, resolution: 'timestamp', fiatPrice, providerId: 1,
createdAt: `${d}T${tijd}:00.000Z`, updatedAt: null, ...extra,
});
function volgendeDag(d) { return new Date(Date.parse(`${d}T00:00:00Z`) + 86400000).toISOString().slice(0, 10); }
const opts = extra => ({ manualProviderId: MANUAL, today: '2026-09-25', ...extra });
const op = (uit, d) => uit.find(x => x.day === d);
describe('dailyProvenance: de soort per dag', () => {
it('een dagrij van een bron is een slotkoers, een rij op manual is met de hand', () => {
const uit = dailyProvenance([dag('2026-09-01', 100), dag('2026-09-02', 101, { providerId: MANUAL })],
opts({ until: '2026-09-02' }));
assert.deepEqual(uit.map(d => d.kind), ['close', 'manual']);
});
it('een spot van een voorbije dag is afgeleid, met de afwijking naar de slotkoersen eromheen', () => {
const uit = dailyProvenance([dag('2026-09-01', 100), spot('2026-09-02', '09:00', 110), dag('2026-09-03', 120)],
opts({ until: '2026-09-03' }));
const d = op(uit, '2026-09-02');
assert.equal(d.kind, 'spot');
assert.equal(d.fiatPrice, 110);
assert.deepEqual(d.deviation, { before: 10, after: -8.33 });
});
it('een afwijking zonder slotkoers aan een kant is null aan die kant, en een spot meet niet tegen een spot', () => {
const uit = dailyProvenance([spot('2026-09-01', '09:00', 100), spot('2026-09-02', '09:00', 110), dag('2026-09-03', 100)],
opts({ until: '2026-09-03' }));
assert.deepEqual(op(uit, '2026-09-02').deviation, { before: null, after: 10 });
});
it('vandaag is live, ook als er een dagrij staat', () => {
const uit = dailyProvenance([dag('2026-09-24', 100), dag('2026-09-25', 101)], opts());
assert.equal(op(uit, '2026-09-25').kind, 'live');
});
it('een dagrij die op zijn eigen dag geschreven is, is een voorlopige koers en dus afgeleid', () => {
const voorlopig = dag('2026-09-02', 105, { createdAt: '2026-09-02T14:00:00.000Z' });
const uit = dailyProvenance([dag('2026-09-01', 100), voorlopig], opts({ until: '2026-09-02' }));
assert.equal(op(uit, '2026-09-02').kind, 'spot');
});
it('een dagrij van vóór migratie 86 (geen tijdstempels) telt als slotkoers', () => {
const oud = dag('2026-09-01', 100, { createdAt: null, updatedAt: null });
assert.equal(dailyProvenance([oud], opts({ until: '2026-09-01' }))[0].kind, 'close');
});
});
describe('dailyProvenance: dezelfde keuzes als de leeskant', () => {
it('een dagrij wint van een spot op dezelfde dag, in welke volgorde ze ook binnenkomen', () => {
const rijen = [spot('2026-09-01', '15:00', 200), dag('2026-09-01', 100)];
for (const volgorde of [rijen, [...rijen].reverse()]) {
const d = dailyProvenance(volgorde, opts({ until: '2026-09-01' }))[0];
assert.equal(d.kind, 'close');
assert.equal(d.fiatPrice, 100);
}
});
it('van twee dagrijen wint de nieuwste (hoogste id), zoals getPricePoint', () => {
const oud = dag('2026-09-01', 100);
const nieuw = dag('2026-09-01', 102);
assert.equal(dailyProvenance([nieuw, oud], opts({ until: '2026-09-01' }))[0].fiatPrice, 102);
assert.equal(dailyProvenance([oud, nieuw], opts({ until: '2026-09-01' }))[0].fiatPrice, 102);
});
it('van twee spots op één dag wint de laatste', () => {
const d = dailyProvenance([spot('2026-09-01', '18:00', 120), spot('2026-09-01', '09:00', 100)],
opts({ until: '2026-09-01' }))[0];
assert.equal(d.fiatPrice, 120);
});
it('een koers van nul of lager telt niet, zoals in toDailyGrid', () => {
const uit = dailyProvenance([dag('2026-09-01', 100), dag('2026-09-02', 0), dag('2026-09-03', -5)],
opts({ until: '2026-09-03' }));
assert.deepEqual(uit.map(d => d.kind), ['close', 'carried', 'carried']);
assert.equal(op(uit, '2026-09-03').fiatPrice, 100);
});
});
describe('dailyProvenance: vandaag volgt de laatst geziene koers', () => {
const VANDAAG = '2026-09-25';
const voorlopig = (fiatPrice, tijd) => dag(VANDAAG, fiatPrice, { createdAt: `${VANDAAG}T06:00:00.000Z`, updatedAt: `${VANDAAG}T${tijd}:00.000Z` });
it('een spot van later vandaag wint van een eerder geschreven dagrij, in welke volgorde ook', () => {
const rijen = [voorlopig(100, '08:00'), spot(VANDAAG, '14:00', 110, { providerId: 2 })];
for (const volgorde of [rijen, [...rijen].reverse()]) {
const d = op(dailyProvenance(volgorde, opts()), VANDAAG);
assert.equal(d.fiatPrice, 110);
assert.equal(d.kind, 'live');
}
});
it('een dagrij die later vandaag is bijgewerkt, wint van een eerdere spot', () => {
const rijen = [spot(VANDAAG, '07:00', 110, { providerId: 2 }), voorlopig(100, '09:00')];
for (const volgorde of [rijen, [...rijen].reverse()]) {
assert.equal(op(dailyProvenance(volgorde, opts()), VANDAAG).fiatPrice, 100);
}
});
it('een voorbije dag houdt de regel van de leeskant: de dagrij wint, ook van een latere spot', () => {
const gisteren = '2026-09-24';
const rijen = [dag(gisteren, 100, { createdAt: `${gisteren}T08:00:00.000Z` }), spot(gisteren, '14:00', 120, { providerId: 2 })];
for (const volgorde of [rijen, [...rijen].reverse()]) {
assert.equal(op(dailyProvenance(volgorde, opts()), gisteren).fiatPrice, 100);
}
});
});
describe('dailyProvenance: doortrekken en de grens', () => {
it('crypto trekt zeven dagen door en heeft daarna geen koers', () => {
const uit = dailyProvenance([dag('2026-09-01', 100)], opts({ until: '2026-09-09' }));
assert.equal(op(uit, '2026-09-08').kind, 'carried');
assert.equal(op(uit, '2026-09-08').ageDays, 7);
assert.equal(op(uit, '2026-09-09').kind, 'none');
assert.equal(op(uit, '2026-09-09').fiatPrice, null);
});
it('metaal trekt veertien dagen door, fiat zonder grens', () => {
const metaal = dailyProvenance([dag('2026-08-01', 50)], opts({ assetKind: 'metal', until: '2026-08-16' }));
assert.equal(op(metaal, '2026-08-15').kind, 'carried');
assert.equal(op(metaal, '2026-08-16').kind, 'none');
const fiat = dailyProvenance([dag('2025-01-01', 0.9)], opts({ assetKind: 'fiat', until: '2026-09-01' }));
assert.equal(fiat.at(-1).kind, 'carried');
});
it('een bereik dat vóór de eerste koers begint, heeft daar geen koers', () => {
const uit = dailyProvenance([dag('2026-09-03', 100)], opts({ from: '2026-09-01', until: '2026-09-03' }));
assert.deepEqual(uit.map(d => d.kind), ['none', 'none', 'close']);
});
it('een bereik dat ná de eerste koers begint, trekt de koers van daarvoor door', () => {
const uit = dailyProvenance([dag('2026-09-01', 100)], opts({ from: '2026-09-03', until: '2026-09-03' }));
assert.equal(uit[0].kind, 'carried');
assert.equal(uit[0].ageDays, 2);
});
it('lege invoer geeft een lege lijst en geen fout', () => {
assert.deepEqual(dailyProvenance([], opts()), []);
assert.deepEqual(dailyProvenance(null, opts()), []);
});
});
describe('provenanceOn: de herkomst van één dag, voor één bedrag', () => {
it('geeft hetzelfde als het dagoverzicht voor die dag', () => {
const rijen = [dag('2026-09-01', 100), dag('2026-09-03', 102)];
assert.deepEqual(provenanceOn(rijen, '2026-09-02', opts()),
dailyProvenance(rijen, opts({ until: '2026-09-03' })).find(d => d.day === '2026-09-02'));
});
it('meet de afwijking van een afgeleide koers tegen slotkoersen BUITEN die ene dag', () => {
const rijen = [dag('2026-09-01', 100), spot('2026-09-02', '09:00', 110), dag('2026-09-03', 100)];
assert.deepEqual(provenanceOn(rijen, '2026-09-02', opts()).deviation, { before: 10, after: 10 });
});
it('beschrijft de rij met de gebruikte koers als twee bronnen dezelfde dag hebben', () => {
const eerste = dag('2026-09-01', 100, { providerId: 1 });
const tweede = dag('2026-09-01', 105, { providerId: 2 });
const d = provenanceOn([eerste, tweede], '2026-09-01', opts({ usedPrice: 100 }));
assert.equal(d.fiatPrice, 100);
assert.equal(d.row.providerId, 1);
assert.equal(d.kind, 'close');
});
it('doet dat ook bij een doorgetrokken koers, op de dag waar hij vandaan komt', () => {
const rijen = [dag('2026-09-01', 100, { providerId: 1 }), dag('2026-09-01', 105, { providerId: 2 })];
const d = provenanceOn(rijen, '2026-09-03', opts({ usedPrice: 100 }));
assert.equal(d.kind, 'carried');
assert.equal(d.ageDays, 2);
assert.equal(d.row.providerId, 1);
});
it('noemt een gebruikte koers voorbij de ouderdomsgrens doorgetrokken, met zijn echte leeftijd', () => {
const d = provenanceOn([dag('2026-08-01', 100)], '2026-09-01', opts({ usedPrice: 100 }));
assert.equal(d.kind, 'carried');
assert.equal(d.ageDays, 31);
assert.equal(provenanceOn([dag('2026-08-01', 100)], '2026-09-01', opts()).kind, 'none', 'zonder gebruikte koers blijft het geen koers');
});
it('houdt zijn eigen keuze als geen rij de gebruikte koers heeft, en geeft null zonder koers', () => {
const rijen = [dag('2026-09-01', 100)];
assert.equal(provenanceOn(rijen, '2026-09-01', opts({ usedPrice: 999 })).fiatPrice, 100);
assert.equal(provenanceOn([], '2026-09-01', opts()), null);
});
});
describe('provenanceOfRow: de laatste opgeslagen koers als koers van vandaag', () => {
it('een rij van een eerdere dag is doorgetrokken, met zijn leeftijd, ook voorbij de grens', () => {
const d = provenanceOfRow(dag('2026-08-01', 100), opts());
assert.equal(d.kind, 'carried');
assert.equal(d.ageDays, 55);
assert.equal(d.day, '2026-09-25');
});
it('een rij van vandaag is live, een met de hand blijft met de hand', () => {
assert.equal(provenanceOfRow(spot('2026-09-25', '09:00', 100), opts()).kind, 'live');
assert.equal(provenanceOfRow(dag('2026-09-25', 100, { providerId: MANUAL }), opts()).kind, 'manual');
});
it('beschrijft een rij als koers van een dag in het verleden, voor een verplaatsing op die dag', () => {
assert.equal(provenanceOfRow(dag('2026-03-03', 100), opts({ day: '2026-03-03' })).kind, 'close');
const afgeleid = provenanceOfRow({ ...spot('2026-03-03', '09:00', 100), derivedFrom: 'spot' }, opts({ day: '2026-03-03' }));
assert.equal(afgeleid.kind, 'spot', 'de laatste spot van die dag is afgeleid en niet live');
assert.equal(provenanceOfRow(dag('2026-03-01', 100), opts({ day: '2026-03-03' })).ageDays, 2);
});
it('geeft null zonder rij of zonder koers', () => {
assert.equal(provenanceOfRow(null, opts()), null);
assert.equal(provenanceOfRow(dag('2026-09-25', 0), opts()), null);
});
});
describe('originFrom en liveOrigin: de platte vorm voor de hover', () => {
it('noemt de bron bij naam, de brondag en bij een afgeleide koers het moment', () => {
const rij = spot('2026-09-02', '09:12', 110, { providerId: 7 });
const d = provenanceOn([dag('2026-09-01', 100), rij], '2026-09-02', opts());
const o = originFrom(d, new Map([[7, 'Bitvavo']]));
assert.equal(o.kind, 'spot');
assert.equal(o.source, 'Bitvavo');
assert.equal(o.sourceDay, '2026-09-02');
assert.equal(o.moment, rij.observedAt);
});
it('geeft bij een slotkoers geen moment, want die is van de hele dag', () => {
const o = originFrom(provenanceOn([dag('2026-09-01', 100)], '2026-09-01', opts()), new Map());
assert.equal(o.moment, null);
assert.equal(o.source, null, 'een onbekende bron is null en geen verzonnen naam');
});
it('geeft null zonder koers, en een live spot zegt alleen dat hij live is', () => {
assert.equal(originFrom(null), null);
assert.equal(originFrom({ day: '2026-09-01', fiatPrice:null, kind: 'none', row: null }), null);
assert.deepEqual(liveOrigin(100, null, new Date('2026-09-25T10:00:00Z')),
{ day: '2026-09-25', kind: 'live', fiatPrice:100, ageDays: 0, deviation: null, source: null, sourceDay: null, moment: '2026-09-25T10:00:00.000Z' });
assert.equal(liveOrigin(null), null);
});
});
describe('provenanceRuns', () => {
it('voegt opeenvolgende dagen van dezelfde soort samen en breekt bij elke wissel', () => {
const uit = dailyProvenance([dag('2026-09-01', 100), dag('2026-09-02', 100), spot('2026-09-03', '09:00', 101)],
opts({ until: '2026-09-05' }));
assert.deepEqual(provenanceRuns(uit), [
{ kind: 'close', from: '2026-09-01', until: '2026-09-02', days: 2 },
{ kind: 'spot', from: '2026-09-03', until: '2026-09-03', days: 1 },
{ kind: 'carried', from: '2026-09-04', until: '2026-09-05', days: 2 },
]);
});
it('geeft een lege lijst bij lege invoer', () => {
assert.deepEqual(provenanceRuns([]), []);
});
});
describe('provenanceCounts', () => {
it('telt per soort en noemt elke soort, ook met nul', () => {
const uit = dailyProvenance([dag('2026-09-01', 100), spot('2026-09-02', '09:00', 101)], opts({ until: '2026-09-03' }));
const n = provenanceCounts(uit);
assert.deepEqual(Object.keys(n), [...PROVENANCE_KINDS]);
assert.equal(n.close, 1);
assert.equal(n.spot, 1);
assert.equal(n.carried, 1);
assert.equal(n.none, 0);
});
});