Files

94 lines
5.8 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:6f5b8502746b6ae69cfa949fb746f614f73c7a3a69761281686d9078e5f7285a
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert/strict';
import { createDbHarness } from '../helpers/db-node.mjs';
const harness = createDbHarness({ seedPreferences: true });
mock.module('../../server/db.js', { namedExports: harness.exports });
const { listProviders, getProvider } = await import('../../server/data/providers.js');
const { listAssets } = await import('../../server/data/assets.js');
const { integrationsJson, INTEGRATIONS_FORMAT } = await import('../../server/integrations-export.mjs');
const { run, lastId } = harness.exports;
function provider(key, { system = false, auth = 'none', required = 0, header = null, param = null } = {}) {
run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired, authenticationHeaderName,
authenticationParameterName, isActive, isSeed, isSystem, createdAt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, 1, ?, '2026-10-10')`,
[key, key, key, system ? '' : `https://${key}.example`, auth, required, header, param, system ? 1 : 0]);
return lastId();
}
const endpoint = (id, capability, path, query = null, quote = null, interval = null) =>
run(`INSERT INTO ProviderEndpoint (providerId, pathTemplate, queryTemplate, capability, method, quoteCurrency, minIntervalSeconds)
VALUES (?, ?, ?, ?, 'GET', ?, ?)`, [id, path, query, capability, quote, interval]);
const output = (id, capability, field, path, transform) =>
run(`INSERT INTO ProviderOutputMap (providerId, jsonPath, capability, internalField, transform) VALUES (?, ?, ?, ?, ?)`, [id, path, capability, field, transform]);
const symbol = (id, sym, externalId, { catchAll = false, unit = null } = {}) =>
run(`INSERT INTO ProviderInputMap (providerId, symbol, externalId, quoteUnit, isCatchAll) VALUES (?, ?, ?, ?, ?)`,
[id, catchAll ? null : sym, externalId, unit, catchAll ? 1 : 0]);
const covers = (id, ...syms) => { for (const s of syms) run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?, ?)`, [id, s]); };
const asset = (sym, kind) => run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit)
VALUES (?, ?, ?, ?, ?, NULL, 8, 'unit')`, [sym, sym, sym.toLowerCase(), kind, sym]);
const exported = (base = 'EUR') => integrationsJson(listProviders().map(p => getProvider(p.key)), listAssets(), base);
beforeEach(() => {
harness.recreate();
for (const k of ['manual', 'atreides_services', 'fixed']) provider(k, { system: true });
asset('XAU', 'metal');
asset('LINK', 'crypto');
const gold = provider('goldapi', { auth: 'header_key', required: 1, header: 'x-access-token' });
endpoint(gold, 'spot', '/api/{symbol}/EUR', null, 'EUR', 3600);
endpoint(gold, 'history_point', '/api/{symbol}/EUR/{date}', null, 'EUR');
output(gold, 'spot', 'price', 'price', 'troy_ounce_to_gram');
covers(gold, 'XAU');
run(`INSERT INTO ProviderCallLimit (providerId, callsPerMonth) VALUES (?, 100)`, [gold]);
const euler = provider('eulerpool', { auth: 'query_key', required: 1, param: 'token' });
endpoint(euler, 'daily_history', '/api/1/{external_id}/quotes/{symbol}', null, 'USD');
symbol(euler, null, 'crypto', { catchAll: true });
symbol(euler, 'XAU', 'commodity', { unit: 'ozt' });
covers(euler, 'LINK', 'XAU', 'NOASSET');
const bitvavo = provider('bitvavo', { auth: 'hmac' });
run(`INSERT INTO ProviderAuthenticationHmac (providerId, messageParts, keyHeader, signatureHeader, timestampHeader, windowHeader,
windowMs, hashAlgorithm, encoding) VALUES (?, 'timestamp,method,full_path,body', 'K', 'S', 'T', 'W', 10000, 'SHA-256', 'hex')`, [bitvavo]);
endpoint(bitvavo, 'spot', '/v2/ticker/price', 'market={symbol}-EUR', null);
covers(bitvavo, 'LINK');
});
describe('integrationsJson', () => {
it('carries every source with all it has, and no system provider', () => {
const e = exported();
assert.equal(e.format, INTEGRATIONS_FORMAT);
assert.deepEqual(e.providers.map(p => p.key), ['bitvavo', 'eulerpool', 'goldapi']);
const gold = e.providers.find(p => p.key === 'goldapi');
assert.deepEqual(gold.auth, { kind: 'header_key', required: true, headerName: 'x-access-token', parameterName: null, hmac: null });
assert.equal(gold.callsPerMonth, 100);
assert.deepEqual(gold.endpoints.map(x => [x.capability, x.pathTemplate, x.minIntervalSeconds]),
[['history_point', '/api/{symbol}/EUR/{date}', null], ['spot', '/api/{symbol}/EUR', 3600]]);
assert.deepEqual(gold.outputs, [{ capability: 'spot', internalField: 'price', jsonPath: 'price', transform: 'troy_ounce_to_gram' }]);
});
it('keeps the HMAC recipe and the catch-all, which only the reader can judge', () => {
const e = exported();
assert.equal(e.providers.find(p => p.key === 'bitvavo').auth.hmac.messageParts, 'timestamp,method,full_path,body');
assert.deepEqual(e.providers.find(p => p.key === 'eulerpool').inputMap, [
{ symbol: 'XAU', externalId: 'commodity', quoteUnit: 'ozt', isCatchAll: false },
{ symbol: null, externalId: 'crypto', quoteUnit: null, isCatchAll: true },
]);
});
it('fills in the unit of account for a NULL quote, and the kind of a covered symbol', () => {
const bitvavo = exported('USD').providers.find(p => p.key === 'bitvavo');
assert.equal(bitvavo.endpoints[0].quote, 'USD');
assert.deepEqual(exported().providers.find(p => p.key === 'eulerpool').coverage,
[{ symbol: 'LINK', kind: 'crypto' }, { symbol: 'NOASSET', kind: null }, { symbol: 'XAU', kind: 'metal' }]);
});
it('carries no secret and gives the same file twice', () => {
const text = JSON.stringify(exported());
assert.ok(!/api_key|api_secret|secret"/i.test(text), 'a secret field is in the export');
assert.equal(JSON.stringify(exported()), text);
});
});