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

144 lines
6.5 KiB
JavaScript

// Generated, do not edit. sha256:762784c61e8bb974ae7fcd7e502db20b12fd6098e6d5292355f3d348d4e202f7
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 });
for (const mod of ['providers', 'price-source', 'price-history', 'api-metrics', 'preferences']) {
mock.module(`../../client/data/${mod}.js`, { namedExports: await import(`../../server/data/${mod}.js`) });
}
const facade = await import('../../client/price/providers.js');
let fetchCalls = [];
globalThis.fetch = async (url, opties) => {
fetchCalls.push({ url, body: opties?.body });
throw new Error('geen netwerk in deze test');
};
const { run, all, one, lastId } = harness.exports;
let eur;
function vasteKoersProvider({ capabilities = ['spot', 'history_point'], koers = 1.0 } = {}) {
run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired,
isActive, isSeed, isSystem, createdAt)
VALUES ('fixed','Vaste koers','vaste koers','','none',0,1,1,1,'2026-08-20T10:00:00.000Z')`);
const id = lastId();
run(`INSERT INTO ProviderFixedRate (providerId, fiatPrice) VALUES (?, ?)`, [id, koers]);
run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?, 'EUR')`, [id]);
for (const capability of capabilities) {
run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate, queryTemplate)
VALUES (?, ?, 'GET', '', NULL)`, [id, capability]);
}
return id;
}
function bronkeuze(providerId, capabilities) {
for (const capability of capabilities) {
run(`INSERT OR IGNORE INTO PriceSource (assetId, capability, providerId, rank, status)
VALUES (?, ?, ?, 1, 'confirmed')`, [eur, capability === 'spot' ? 'spot' : 'history', providerId]);
}
}
const asset = () => one(`SELECT * FROM Asset WHERE id = ?`, [eur]);
const prijsrijen = () => all(
`SELECT Asset.symbol, Provider.key AS bron, PriceHistory.fiatPrice, PriceHistory.resolution
FROM PriceHistory
JOIN Asset ON Asset.id = PriceHistory.assetId
JOIN Provider ON Provider.id = PriceHistory.providerId
ORDER BY PriceHistory.resolution, PriceHistory.observedAt`);
beforeEach(() => {
harness.recreate();
facade.clearSpotCache();
fetchCalls = [];
run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit)
VALUES ('EUR','Euro','euro','fiat','EUR','cent',2,'unit')`);
eur = lastId();
});
describe('vaste koers: de spot komt uit de provider en niet van het netwerk', () => {
it('geeft 1,00 als spot, in het ene prijsveld', () => {
bronkeuze(vasteKoersProvider(), ['spot']);
return facade.liveSpotFor(asset()).then(spot => {
assert.deepEqual(spot, { price: 1 });
assert.deepEqual(fetchCalls, [], 'er is niets naar buiten gegaan');
});
});
it('meet geen api-call, want er is geen api-call', async () => {
bronkeuze(vasteKoersProvider(), ['spot']);
await facade.liveSpotFor(asset());
await facade.liveSpotFor(asset());
assert.deepEqual(all(`SELECT * FROM ApiCallMetric`), []);
});
it('legt de huidige waarde wél vast als tijdstip-punt', async () => {
bronkeuze(vasteKoersProvider(), ['spot']);
await facade.liveSpotFor(asset());
assert.deepEqual(prijsrijen(),
[{ symbol: 'EUR', bron: 'fixed', fiatPrice: 1, resolution: 'timestamp' }]);
});
it('laat een gewone provider wél naar buiten gaan', async () => {
run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired,
isActive, isSeed, isSystem, createdAt)
VALUES ('mempool','mempool','mempool','https://voorbeeld.test','none',0,1,1,0,'2026-08-20T10:00:00.000Z')`);
const id = lastId();
run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?, 'EUR')`, [id]);
run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate, queryTemplate)
VALUES (?, 'spot', 'GET', '/prijs', NULL)`, [id]);
bronkeuze(id, ['spot']);
assert.equal(await facade.liveSpotFor(asset()), null, 'de stub gooit, dus geen koers');
assert.equal(fetchCalls.length, 1, 'en er is wél een verzoek gedaan');
assert.equal(fetchCalls[0].url, '/api/price/call');
});
});
describe('vaste koers: de historie', () => {
it('vult price_history met een dagpunt van 1,00 via de spot-keten', async () => {
bronkeuze(vasteKoersProvider(), ['spot', 'history_point']);
await facade.ensurePriceHistoryFor(asset());
assert.deepEqual(prijsrijen().filter(r => r.resolution === 'day'),
[{ symbol: 'EUR', bron: 'fixed', fiatPrice: 1, resolution: 'day' }]);
assert.deepEqual(fetchCalls, []);
});
it('levert een punt voor élke datum uit het verleden, met het tijdstip van die dag erbij', async () => {
bronkeuze(vasteKoersProvider(), ['history_point']);
assert.deepEqual(await facade.historicalPointForVia(asset(), '2019-12-31'),
{ price: 1, time: Date.parse('2019-12-31T00:00:00Z') / 1000 });
assert.deepEqual(await facade.historicalPointForVia(asset(), '2026-01-01'),
{ price: 1, time: Date.parse('2026-01-01T00:00:00Z') / 1000 });
assert.deepEqual(fetchCalls, []);
});
it('slaat een punt op voor elke gevraagde peildatum', async () => {
bronkeuze(vasteKoersProvider(), ['history_point']);
await facade.ensurePointsAtFor(asset(), ['2025-01-01', '2026-01-01']);
assert.deepEqual(
all(`SELECT observedAt, fiatPrice, resolution FROM PriceHistory ORDER BY observedAt`),
[{ observedAt: '2025-01-01T00:00:00Z', fiatPrice: 1, resolution: 'day' },
{ observedAt: '2026-01-01T00:00:00Z', fiatPrice: 1, resolution: 'day' }]);
assert.deepEqual(fetchCalls, []);
});
it('heeft geen daggrid, en zegt dat ook', async () => {
bronkeuze(vasteKoersProvider(), ['spot', 'history_point']);
assert.equal(await facade.hasDailyGridFor(asset()), false);
});
it('weigert een reeks te verzinnen als een vaste koers tóch daily_history claimt', async () => {
bronkeuze(vasteKoersProvider({ capabilities: ['daily_history'] }), ['daily_history']);
await assert.rejects(() => facade.ensurePriceHistoryFor(asset()), /vaste koers/);
assert.deepEqual(prijsrijen(), [], 'en er staat niets verzonnen in de historie');
});
it('gebruikt de koers uit de tabel en niet een 1 uit de code', async () => {
bronkeuze(vasteKoersProvider({ koers: 0.5 }), ['spot']);
assert.deepEqual(await facade.liveSpotFor(asset()), { price: 0.5 });
});
});