143 lines
6.5 KiB
JavaScript
143 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 });
|
||
|
|
});
|
||
|
|
});
|