// Generated, do not edit. sha256:9871fb04293a9538a99372a76f6c50a51c7cfcd7eb2971b99eaf20efe539553d 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']) { 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) => { const body = JSON.parse(opties.body); fetchCalls.push(body.provider.key); const antwoord = body.date ? { time: Math.floor(Date.parse(`${body.date}T00:00:00Z`) / 1000), price: 60 } : { price: 60 }; return { ok: true, status: 200, json: async () => antwoord }; }; const { run, lastId, one } = harness.exports; let goud; const maand = new Date().toISOString().slice(0, 7); function provider(key, { limiet = null, gebruikt = 0, rang = 1, caps = ['spot', 'history_point'] } = {}) { 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-01T10:00:00.000Z')`, [key, key, key]); const id = lastId(); run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?, 'XAU')`, [id]); for (const c of caps) { run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate) VALUES (?, ?, 'GET', '/p')`, [id, c]); run(`INSERT OR IGNORE INTO PriceSource (assetId, capability, providerId, rank, status) VALUES (?, ?, ?, ?, 'confirmed')`, [goud, c === 'spot' ? 'spot' : 'history', id, rang]); } if (limiet) run(`INSERT INTO ProviderCallLimit (providerId, callsPerMonth, createdAt) VALUES (?, ?, '2026-09-01T00:00:00Z')`, [id, limiet]); if (gebruikt) { run(`INSERT INTO ApiCallMetric (providerId, capability, bucketMinute, okCount, failCount, sumLatencyMs, maxLatencyMs) VALUES (?, 'spot', ?, ?, 0, 0, 0)`, [id, `${maand}-01T00:01`, gebruikt]); } return id; } const asset = () => one(`SELECT * FROM Asset WHERE id = ?`, [goud]); beforeEach(() => { harness.recreate(); facade.clearSpotCache(); facade.forgetSeriesFetches(); facade.forgetUsage(); fetchCalls = []; 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 maandlimiet', () => { it('stuurt geen verzoek meer naar een bron op zijn limiet', async () => { provider('goldapi', { limiet: 100, gebruikt: 100 }); assert.equal(await facade.liveSpotFor(asset()), null); assert.deepEqual(fetchCalls, [], 'niets naar buiten'); }); it('telt de verzoeken van deze sessie mee, en weigert het eerste boven de limiet', async () => { provider('goldapi', { limiet: 3, gebruikt: 1 }); for (let i = 0; i < 3; i++) { facade.clearSpotCache(); await facade.liveSpotFor(asset()); } assert.equal(fetchCalls.length, 2, 'één stond er al, dus nog twee mogen'); }); it('schakelt voor de spot over op de volgende rang', async () => { provider('goldapi', { limiet: 100, gebruikt: 100, rang: 1 }); provider('reserve', { rang: 2 }); assert.deepEqual(await facade.liveSpotFor(asset()), { price: 60 }); assert.deepEqual(fetchCalls, ['reserve']); }); }); describe('fillClosesFor met een limiet', () => { it('gaat na de limiet van rang 1 verder bij rang 2', async () => { provider('goldapi', { limiet: 1, rang: 1 }); provider('reserve', { rang: 2 }); const uit = await facade.fillClosesFor(asset(), '2026-09-01', '2026-09-03', { today: '2026-09-24' }); assert.deepEqual(fetchCalls, ['goldapi', 'reserve', 'reserve']); assert.equal(uit.written, 3); assert.equal(uit.limitReached, false); }); it('stopt de ronde als geen enkele bron nog tegoed heeft, in plaats van alle dagen af te lopen', async () => { provider('goldapi', { limiet: 1 }); const uit = await facade.fillClosesFor(asset(), '2026-01-01', '2026-09-01', { today: '2026-09-24' }); assert.deepEqual(fetchCalls, ['goldapi']); assert.equal(uit.limitReached, true); assert.ok(uit.requested <= 2, `hij liep ${uit.requested} dagen af`); }); it('stopt tussen twee dagen als het signaal is afgebroken', async () => { provider('goldapi'); const stop = new AbortController(); stop.abort(); const uit = await facade.fillClosesFor(asset(), '2026-09-01', '2026-09-10', { today: '2026-09-24', signal: stop.signal }); assert.deepEqual(fetchCalls, []); assert.equal(uit.stopped, true); }); });