Versie 0.2.0

This commit is contained in:
Atreides Assets committed 2026-10-29 11:57:29 +01:00
commit e77d8fd373
495 files changed
+81026

No files matched your search

+202
View File
@@ -0,0 +1,202 @@
// Generated, do not edit. sha256:fc183f4c5a327b7b47dcb1a430409a48861cde95adfddadca5231c79a33dc477
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert/strict';
import { createDbHarness } from '../helpers/db-node.mjs';
import { CONFIG } from '../../shared/config.js';
const harness = createDbHarness();
mock.module('../../server/db.js', { namedExports: harness.exports });
const { recordIntegrationCall, listIntegrationCalls, lastIntegrationFailures, countServiceCall, serviceState } =
await import('../../server/data/integration-log.js');
beforeEach(() => harness.recreate());
const rijen = () => harness.exports.all(`SELECT * FROM IntegrationCall ORDER BY id`);
const dagenGeleden = (d) => new Date(Date.now() - d * 86_400_000).toISOString();
const fout = (extra = {}) => ({
target: 'provider', providerKey: 'goldapi', operation: 'spot', subject: 'XAU', outcome: 'fail',
httpStatus: 403, errorKey: 'integrations.price.httpFailed', detail: 'Forbidden', latencyMs: 120, ...extra,
});
function oudeRij(occurredAt, providerKey = 'goldapi') {
harness.exports.run(
`INSERT INTO IntegrationCall (target, providerKey, operation, outcome, occurredAt) VALUES ('provider', ?, 'spot', 'fail', ?)`,
[providerKey, occurredAt]);
}
describe('recordIntegrationCall: wat er nooit in mag', () => {
it('vervangt een sleutel die de bron in zijn melding herhaalt, ook URL-gecodeerd', async () => {
const sleutel = 'goldapi-abc+123/xyz';
await recordIntegrationCall(fout({ detail: `Invalid key ${sleutel} (${encodeURIComponent(sleutel)})` }),
{ secrets: [sleutel] });
const [r] = rijen();
assert.ok(!r.detail.includes(sleutel), r.detail);
assert.ok(!r.detail.includes(encodeURIComponent(sleutel)), r.detail);
assert.match(r.detail, /\*\*\*/);
});
it('vervangt het token van de dienst in een Bearer-waarde, ook zonder dat het token is meegegeven', async () => {
await recordIntegrationCall({ target: 'service', operation: '/v1/spot', outcome: 'fail',
detail: 'echo Authorization: Bearer s3cr3t-token-waarde' });
assert.equal(rijen()[0].detail, 'echo Authorization: Bearer ***');
});
it('laat geen URL staan, want bij query_key staat de sleutel in de query', async () => {
await recordIntegrationCall(fout({ detail: 'GET https://api.example.test/v1/price?apikey=K123456 failed' }));
const [r] = rijen();
assert.ok(!/https?:|K123456|example\.test/.test(r.detail), r.detail);
assert.match(r.detail, /<url>/);
});
it('vervangt een sleutelparameter in losse tekst', async () => {
await recordIntegrationCall(fout({ detail: 'rejected token=abcdef1234 and api_key=zzz999' }));
assert.equal(rijen()[0].detail, 'rejected token=*** and api_key=***');
});
it('weigert een URL als operatie in plaats van hem op te schonen', async () => {
await assert.rejects(recordIntegrationCall(fout({ operation: 'https://api.example.test/spot?key=x' })),
/operation is a path or a capability/);
await assert.rejects(recordIntegrationCall(fout({ operation: '/v1/spot?instrument=x' })),
/operation is a path or a capability/);
assert.equal(rijen().length, 0);
});
it('kort een lange melding in tot de grens, zichtbaar', async () => {
await recordIntegrationCall(fout({ detail: 'x'.repeat(1000) }));
const { detail } = rijen()[0];
assert.equal(detail.length, CONFIG.INTEGRATION_LOG.DETAIL_MAX_CHARS);
assert.ok(detail.endsWith('…'));
});
it('bewaart in errorKey alleen sleutels en geen vrije tekst uit een antwoord', async () => {
await recordIntegrationCall({ target: 'service', operation: '/v1/series', outcome: 'fail',
errorKey: 'marketService.rejected api.noSeries <script>alert(1)</script>' });
assert.equal(rijen()[0].errorKey, 'marketService.rejected api.noSeries');
});
it('bewaart een request-id alleen bij de dienst en alleen in de afgesproken vorm', async () => {
await recordIntegrationCall({ target: 'service', operation: '/v1/spot', outcome: 'fail', requestId: 'a1b2c3d4-0000-4000-8000-000000000000' });
await recordIntegrationCall({ target: 'service', operation: '/v1/spot', outcome: 'fail', requestId: 'kort' });
await recordIntegrationCall(fout({ requestId: 'a1b2c3d4-0000-4000-8000-000000000000' }));
assert.deepEqual(rijen().map(r => r.requestId), ['a1b2c3d4-0000-4000-8000-000000000000', null, null]);
});
});
describe('recordIntegrationCall: contractguards', () => {
it('weigert een onbekende soort en een onbekende uitkomst, ook "ok"', async () => {
await assert.rejects(recordIntegrationCall(fout({ target: 'lnd' })), /Unknown integration target/);
await assert.rejects(recordIntegrationCall(fout({ outcome: 'ok' })), /Unknown integration outcome/);
assert.equal(rijen().length, 0);
});
it('eist een providerKey bij een provider en negeert hem bij de dienst', async () => {
await assert.rejects(recordIntegrationCall(fout({ providerKey: '' })), /needs a providerKey/);
await recordIntegrationCall({ target: 'service', providerKey: 'goldapi', operation: '/v1/spot', outcome: 'refused' });
assert.equal(rijen()[0].providerKey, null);
});
it('legt het tijdstip van versturen vast tot op de milliseconde', async () => {
await recordIntegrationCall(fout({ occurredAt: Date.UTC(2026, 9, 19, 8, 30, 15, 123) }));
assert.equal(rijen()[0].occurredAt, '2026-10-19T08:30:15.123Z');
});
});
describe('opruimen bij elke schrijfactie', () => {
it('haalt rijen ouder dan de termijn weg en laat rijen binnen de termijn staan', async () => {
const dagen = CONFIG.INTEGRATION_LOG.RETENTION_DAYS;
oudeRij(dagenGeleden(dagen + 1), 'oud');
oudeRij(dagenGeleden(dagen - 1), 'binnen');
await recordIntegrationCall(fout());
assert.deepEqual(rijen().map(r => r.providerKey), ['binnen', 'goldapi']);
});
it('houdt het plafond aan door de oudste rijen weg te halen', async () => {
const max = CONFIG.INTEGRATION_LOG.MAX_ROWS;
const nu = new Date().toISOString();
harness.exports.run(`WITH RECURSIVE n(i) AS (SELECT 1 UNION ALL SELECT i + 1 FROM n WHERE i < ?)
INSERT INTO IntegrationCall (target, providerKey, operation, outcome, occurredAt)
SELECT 'provider', 'p' || i, 'spot', 'fail', ? FROM n`, [max, nu]);
await recordIntegrationCall(fout());
const { n } = harness.exports.one(`SELECT COUNT(*) AS n FROM IntegrationCall`);
assert.equal(n, max);
assert.ok(!harness.exports.one(`SELECT 1 AS x FROM IntegrationCall WHERE providerKey = 'p1'`));
assert.ok(harness.exports.one(`SELECT 1 AS x FROM IntegrationCall WHERE providerKey = 'p2'`));
assert.ok(harness.exports.one(`SELECT 1 AS x FROM IntegrationCall WHERE providerKey = 'goldapi'`));
});
});
describe('countServiceCall: de dienst in ApiCallMetric', () => {
const tellingen = () => harness.exports.all(`SELECT capability, okCount, failCount FROM ApiCallMetric`);
it('telt onder de systeemprovider van de dienst', async () => {
harness.exports.run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired, isActive, isSeed, isSystem, createdAt)
VALUES ('atreides_services', 'Atreides Services', 'atreides services', '', 'none', 0, 1, 1, 1, '2026-10-19')`);
await countServiceCall({ capability: 'spot', ok: true, latencyMs: 30 });
await countServiceCall({ capability: 'spot', ok: false, latencyMs: 10 });
assert.deepEqual(tellingen(), [{ capability: 'spot', okCount: 1, failCount: 1 }]);
});
it('telt niets zonder die rij, in plaats van te gooien', async () => {
await countServiceCall({ capability: 'spot', ok: true, latencyMs: 30 });
assert.deepEqual(tellingen(), []);
});
});
describe('serviceState: het oordeel van het lampje', () => {
const dienst = () => harness.exports.run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired, isActive, isSeed, isSystem, createdAt)
VALUES ('atreides_services', 'Atreides Services', 'atreides services', '', 'none', 0, 1, 1, 1, '2026-10-19')`);
const gelukt = (minuut) => harness.exports.run(`INSERT INTO ApiCallMetric (providerId, capability, bucketMinute, okCount, failCount, sumLatencyMs, maxLatencyMs)
SELECT id, 'spot', ?, 1, 0, 10, 10 FROM Provider WHERE key = 'atreides_services'`, [minuut]);
const mislukt = (at, outcome = 'fail') => recordIntegrationCall({ target: 'service', operation: '/v1/spot', outcome, occurredAt: at });
it('zonder log en zonder teller: geen oordeel', () => {
dienst();
assert.deepEqual({ ...serviceState(), lastFail: null }, { ok: null, at: null, lastFail: null, lastOkAt: null });
});
it('een fout na het laatste succes is rood, ook in dezelfde minuut', async () => {
dienst();
gelukt('2026-10-19T08:00');
await mislukt('2026-10-19T08:00:30.000Z');
assert.deepEqual([serviceState().ok, serviceState().at], [false, '2026-10-19T08:00:30.000Z']);
});
it('een succes na de laatste fout is groen', async () => {
dienst();
await mislukt('2026-10-19T08:00:30.000Z');
gelukt('2026-10-19T08:01');
assert.deepEqual([serviceState().ok, serviceState().at], [true, '2026-10-19T08:01:00.000Z']);
});
it('een leeg antwoord maakt het lampje niet rood', async () => {
dienst();
gelukt('2026-10-19T08:00');
await mislukt('2026-10-19T08:05:00.000Z', 'empty');
assert.equal(serviceState().ok, true);
});
});
describe('lezen', () => {
it('listIntegrationCalls: nieuwste eerst, binnen het venster, te filteren', async () => {
oudeRij(dagenGeleden(10), 'buiten');
await recordIntegrationCall(fout({ providerKey: 'a' }));
await recordIntegrationCall({ target: 'service', operation: '/v1/series', outcome: 'empty' });
await recordIntegrationCall(fout({ providerKey: 'b' }));
assert.deepEqual(listIntegrationCalls().map(r => r.providerKey), ['b', null, 'a']);
assert.deepEqual(listIntegrationCalls({ target: 'service' }).map(r => r.operation), ['/v1/series']);
assert.deepEqual(listIntegrationCalls({ providerKey: 'a' }).length, 1);
assert.deepEqual(listIntegrationCalls({ days: 30 }).map(r => r.providerKey), ['b', null, 'a', 'buiten']);
assert.deepEqual(listIntegrationCalls({ limit: 1 }).map(r => r.providerKey), ['b']);
});
it('lastIntegrationFailures: per bron de laatste, de dienst als eigen bron', async () => {
await recordIntegrationCall(fout({ providerKey: 'a', detail: 'eerste' }));
await recordIntegrationCall({ target: 'service', operation: '/v1/spot', outcome: 'fail', detail: 'dienst' });
await recordIntegrationCall(fout({ providerKey: 'a', detail: 'tweede' }));
assert.deepEqual(lastIntegrationFailures().map(r => [r.providerKey, r.detail]),
[['a', 'tweede'], [null, 'dienst']]);
});
});