Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,129 @@
|
||||
// Generated, do not edit. sha256:f2615dafd4b52aacda7e6453bb0c703f6c9c0f0f88c1579820810800ed320281
|
||||
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');
|
||||
const { momentumOf, clearMomentum } = await import('../../client/price/momentum.js');
|
||||
|
||||
let fetchCalls = [];
|
||||
let antwoord = { EUR: 100, USD: null };
|
||||
globalThis.fetch = async (url, opties) => {
|
||||
fetchCalls.push({ url, body: opties?.body });
|
||||
return { ok: true, status: 200, json: async () => antwoord };
|
||||
};
|
||||
|
||||
const { run, lastId, one } = harness.exports;
|
||||
let btc, goud;
|
||||
|
||||
function provider(key, symbolen) {
|
||||
run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired,
|
||||
isActive, isSeed, isSystem, createdAt)
|
||||
VALUES (?,?,?, 'https://voorbeeld.test','none',0,1,1,0,'2026-09-21T10:00:00.000Z')`, [key, key, key]);
|
||||
const id = lastId();
|
||||
for (const s of symbolen) run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?,?)`, [id, s]);
|
||||
run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate, queryTemplate)
|
||||
VALUES (?, 'spot', 'GET', '/prijs', NULL)`, [id]);
|
||||
return id;
|
||||
}
|
||||
function bronkeuze(providerId, assetId) {
|
||||
run(`INSERT INTO PriceSource (assetId, capability, providerId, rank, status)
|
||||
VALUES (?, 'spot', ?, 1, 'confirmed')`, [assetId, providerId]);
|
||||
}
|
||||
const assetRij = id => one(`SELECT * FROM Asset WHERE id = ?`, [id]);
|
||||
|
||||
beforeEach(() => {
|
||||
harness.recreate();
|
||||
facade.clearSpotCache();
|
||||
fetchCalls = [];
|
||||
antwoord = { EUR: 100, USD: null };
|
||||
run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit)
|
||||
VALUES ('BTC','Bitcoin','bitcoin','crypto','BTC','sat',8,'unit')`);
|
||||
btc = lastId();
|
||||
run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit)
|
||||
VALUES ('XAU','Goud','goud','metal','g','mg',3,'unit')`);
|
||||
goud = lastId();
|
||||
});
|
||||
|
||||
describe('de spot wordt gecached', () => {
|
||||
it('doet ÉÉN verzoek voor twee vragen om dezelfde spot', async () => {
|
||||
const p = provider('bron', ['BTC']);
|
||||
bronkeuze(p, btc);
|
||||
const eerste = await facade.liveSpotFor(assetRij(btc));
|
||||
const tweede = await facade.liveSpotFor(assetRij(btc));
|
||||
assert.deepEqual(eerste, { EUR: 100, USD: null });
|
||||
assert.deepEqual(tweede, eerste, 'hetzelfde antwoord');
|
||||
assert.equal(fetchCalls.length, 1, 'en maar één keer naar buiten');
|
||||
});
|
||||
|
||||
it('haalt na leegmaken WEL opnieuw op', async () => {
|
||||
const p = provider('bron', ['BTC']);
|
||||
bronkeuze(p, btc);
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
facade.clearSpotCache();
|
||||
antwoord = { EUR: 111, USD: null };
|
||||
assert.deepEqual(await facade.liveSpotFor(assetRij(btc)), { EUR: 111, USD: null },
|
||||
'de nieuwe koers, dus er is echt opnieuw gevraagd');
|
||||
assert.equal(fetchCalls.length, 2);
|
||||
});
|
||||
|
||||
it('houdt twee assets apart, en vraagt één asset maar één keer', async () => {
|
||||
bronkeuze(provider('bron-btc', ['BTC']), btc);
|
||||
bronkeuze(provider('bron-goud', ['XAU']), goud);
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
await facade.liveSpotFor(assetRij(goud));
|
||||
assert.equal(fetchCalls.length, 2, 'twee assets zijn twee vragen');
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
assert.equal(fetchCalls.length, 2, 'en hetzelfde asset nog eens is geen nieuwe vraag');
|
||||
});
|
||||
|
||||
it('onthoudt welke bron de spot gaf, per asset, en vergeet dat met de cache', async () => {
|
||||
bronkeuze(provider('bitvavo', ['BTC']), btc);
|
||||
assert.equal(facade.lastSpotSource(assetRij(btc)), null);
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
assert.equal(facade.lastSpotSource(assetRij(btc)), 'bitvavo');
|
||||
assert.equal(facade.lastSpotSource(assetRij(goud)), null, 'een ander asset vroeg niets');
|
||||
facade.clearSpotCache();
|
||||
assert.equal(facade.lastSpotSource(assetRij(btc)), null);
|
||||
});
|
||||
|
||||
it('voedt de korte trend met elk vers antwoord', async () => {
|
||||
clearMomentum();
|
||||
bronkeuze(provider('bron', ['BTC']), btc);
|
||||
antwoord = { price: 100 };
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
facade.clearSpotCache();
|
||||
antwoord = { price: 110 };
|
||||
await facade.liveSpotFor(assetRij(btc));
|
||||
await new Promise(r => setImmediate(r));
|
||||
assert.deepEqual([momentumOf(btc).up, momentumOf(btc).down], [1, 0]);
|
||||
const opgeslagen = JSON.parse(one(`SELECT value FROM Preference WHERE scope = 'momentum' AND key = ?`, [String(btc)]).value);
|
||||
assert.equal(opgeslagen.last, 110);
|
||||
assert.equal(opgeslagen.moves.length, 1);
|
||||
assert.ok(Number.isFinite(opgeslagen.moves[0].at), 'elke beweging draagt zijn tijd');
|
||||
});
|
||||
|
||||
it('bewaart een MISLUKTE poging niet', async () => {
|
||||
const p = provider('bron', ['BTC']);
|
||||
bronkeuze(p, btc);
|
||||
globalThis.fetch = async (url, opties) => {
|
||||
fetchCalls.push({ url, body: opties?.body });
|
||||
throw new Error('geen netwerk in deze test');
|
||||
};
|
||||
assert.equal(await facade.liveSpotFor(assetRij(btc)), null);
|
||||
assert.equal(await facade.liveSpotFor(assetRij(btc)), null);
|
||||
assert.equal(fetchCalls.length, 2, 'twee pogingen, want er is niets bewaard');
|
||||
globalThis.fetch = async (url, opties) => {
|
||||
fetchCalls.push({ url, body: opties?.body });
|
||||
return { ok: true, status: 200, json: async () => antwoord };
|
||||
};
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user