// Generated, do not edit. sha256:b4e0276b7d4bc8c4728c77282dde12358131e2103dd3182aa9dbf27cc334764b import { describe, it, beforeEach, afterEach } from 'node:test'; import assert from 'node:assert/strict'; import { openDatabase, closeDatabase, database, run } from '../../server/db/conn.mjs'; import { SCHEMA } from '../../server/db/schema.js'; import { seedAssets, seedPriceSource, seedDashboardProfile, seedProviders, seedPreferences } from '../../server/db/seeds.js'; import { DASHBOARD, PREFERENCES } from '../../shared/config.js'; const verseDatabase = (munt) => { closeDatabase(); openDatabase(':memory:'); run(SCHEMA); run(`INSERT INTO Portfolio (id, baseCurrency, createdAt) VALUES (1, ?, '2026-01-01T00:00:00Z')`, [munt]); }; beforeEach(() => verseDatabase('EUR')); afterEach(() => closeDatabase()); const profielen = () => database().prepare(`SELECT * FROM DashboardProfile ORDER BY id`).all().map(r => ({ ...r })); const widgets = () => database().prepare(`SELECT * FROM DashboardWidget ORDER BY instanceKey`).all().map(r => ({ ...r })); describe('seedDashboardProfile', () => { it('maakt één actief systeemprofiel', () => { seedDashboardProfile(); const p = profielen(); assert.equal(p.length, 1); assert.equal(p[0].name, 'Standaard'); assert.equal(p[0].isSystem, 1); assert.equal(p[0].isActive, 1); assert.equal(p[0].sortOrder, 0); assert.equal(p[0].searchName, 'standaard', 'afgeleid via toSearchName, niet met de hand'); }); it('zet de standaardindeling als rijen neer', () => { seedDashboardProfile(); assert.deepEqual(widgets().map(w => w.type).sort(), DASHBOARD.DEFAULT_LAYOUT.map(w => w.type).sort()); assert.deepEqual(widgets().map(w => w.instanceKey).sort(), DASHBOARD.DEFAULT_LAYOUT.map(w => w.instId).sort()); }); it('laat hoogte en rij open, want die komen uit de meting', () => { seedDashboardProfile(); for (const w of widgets()) { assert.equal(w.spanHeight, null, `${w.instanceKey} heeft al een hoogte`); assert.equal(w.gridY, 0); assert.ok(w.spanWidth > 0, 'de breedte komt wél uit de registry: die wordt gekozen'); } }); it('neemt de voorkeurskolom mee, zodat Meldingen rechts begint', () => { seedDashboardProfile(); const kolom = Object.fromEntries(widgets().map(w => [w.instanceKey, w.gridX])); assert.equal(kolom.clocks, undefined, 'geen wereldklokken bij een verse installatie'); assert.equal(kolom.notifications, DASHBOARD.GRID_COLUMNS - DASHBOARD.WIDGETS.notifications.defaultSpan); }); it('koppelt de widgets aan het profiel en laat assetId leeg', () => { seedDashboardProfile(); const id = profielen()[0].id; for (const w of widgets()) { assert.equal(w.profileId, id); assert.equal(w.assetId, null, 'geen enkele standaardwidget hangt aan één asset'); assert.equal(w.settings, null, 'en geen van hen heeft parameters'); } }); it('is idempotent', () => { seedDashboardProfile(); seedDashboardProfile(); assert.equal(profielen().length, 1); assert.equal(widgets().length, DASHBOARD.DEFAULT_LAYOUT.length, 'geen dubbele widget-rijen'); }); it('vertelt of hij iets heeft aangemaakt', () => { assert.equal(seedDashboardProfile(), true, 'eerste keer: aangemaakt'); assert.equal(seedDashboardProfile(), false, 'tweede keer: niets te doen'); }); it('loopt niet op de unieke index als er al een ander actief profiel is', () => { run(`INSERT INTO DashboardProfile (name, searchName, sortOrder, isActive, isSystem, createdAt, updatedAt) VALUES ('Eigen','eigen',0,1,0,'2026-01-01T00:00:00Z','2026-01-01T00:00:00Z')`); seedDashboardProfile(); const p = profielen(); assert.equal(p.length, 2); assert.equal(p.filter(x => x.isActive).length, 1, 'nog steeds precies één actief'); assert.equal(p.find(x => x.isActive).name, 'Eigen', 'de bestaande keuze blijft staan'); assert.equal(p.find(x => x.isSystem).isActive, 0); }); }); describe('seeds: de vaste-koers-provider en het EUR-asset', () => { const provider = () => database().prepare(`SELECT * FROM Provider WHERE key = 'fixed'`).get(); const bronkeuze = () => database().prepare( `SELECT capability, rank, status FROM PriceSource WHERE assetId = (SELECT id FROM Asset WHERE symbol = 'EUR') ORDER BY capability`).all().map(r => ({ ...r })); it('zet de provider neer met een vaste koers van 1,00, zijn dekking en zijn twee capabilities', () => { seedProviders(); const p = provider(); assert.equal(p.isSystem, 1); assert.equal(p.baseUrl, '', 'geen URL, want er gaat geen verzoek uit'); assert.equal(database().prepare(`SELECT fiatPrice FROM ProviderFixedRate WHERE providerId = ?`).get(p.id).fiatPrice, 1); assert.deepEqual( database().prepare(`SELECT symbol FROM ProviderCoverage WHERE providerId = ?`).all(p.id).map(r => r.symbol), ['EUR']); assert.deepEqual( database().prepare(`SELECT capability FROM ProviderEndpoint WHERE providerId = ? ORDER BY capability`) .all(p.id).map(r => r.capability), ['history_point', 'spot'], 'geen daily_history: een vaste koers heeft geen reeks'); }); it('geeft alleen de vaste-koers-provider een vaste koers', () => { seedProviders(); assert.deepEqual( database().prepare(`SELECT p.key FROM ProviderFixedRate f JOIN Provider p ON p.id = f.providerId`).all() .map(r => r.key), ['fixed']); }); it('zet het EUR-asset neer in centen', () => { seedAssets(); const eur = database().prepare(`SELECT * FROM Asset WHERE symbol = 'EUR'`).get(); assert.equal(eur.kind, 'fiat'); assert.equal(eur.unit, 'EUR'); assert.equal(eur.subunit, 'cent', 'de cent is de opslag-eenheid van 10^decimals'); assert.equal(eur.decimals, 2); assert.equal(eur.displayUnit, 'unit', 'niemand leest een saldo in centen'); }); it('koppelt EUR aan de vaste koers voor de spot en de historie', () => { seedProviders(); seedAssets(); seedPriceSource(null, 'confirmed'); assert.deepEqual(bronkeuze(), [ { capability: 'history', rank: 1, status: 'confirmed' }, { capability: 'spot', rank: 1, status: 'confirmed' }, ]); }); it('geeft een bron alleen de keuze die hij kan leveren', () => { seedProviders(); seedAssets(); database().exec(`DELETE FROM ProviderEndpoint WHERE capability <> 'spot' AND providerId = (SELECT id FROM Provider WHERE key = 'fixed')`); seedPriceSource(null, 'confirmed'); assert.deepEqual(bronkeuze(), [{ capability: 'spot', rank: 1, status: 'confirmed' }]); }); it('is idempotent: twee keer zaaien levert één provider en één asset', () => { seedProviders(); seedAssets(); seedPriceSource(null, 'confirmed'); seedProviders(); seedAssets(); seedPriceSource(null, 'confirmed'); const n = (q) => database().prepare(q).get().n; assert.equal(n(`SELECT COUNT(*) AS n FROM Provider WHERE key = 'fixed'`), 1); assert.equal(n(`SELECT COUNT(*) AS n FROM Asset WHERE symbol = 'EUR'`), 1); assert.equal(n(`SELECT COUNT(*) AS n FROM ProviderFixedRate`), 1); assert.equal(bronkeuze().length, 2); }); }); describe('seeds per rekenvaluta', () => { const zaai = () => { seedProviders(); seedAssets(); seedPriceSource(null, 'confirmed'); }; const dekking = (key) => database().prepare( `SELECT symbol FROM ProviderCoverage WHERE providerId = (SELECT id FROM Provider WHERE key = ?) ORDER BY symbol`) .all(key).map(r => r.symbol); const startbron = (symbool) => database().prepare( `SELECT DISTINCT Provider.key FROM PriceSource JOIN Provider ON Provider.id = PriceSource.providerId JOIN Asset ON Asset.id = PriceSource.assetId WHERE Asset.symbol = ?`).all(symbool).map(r => r.key); const fiat = () => database().prepare(`SELECT symbol, name, subunit, decimals FROM Asset WHERE kind = 'fiat' ORDER BY symbol`) .all().map(r => ({ ...r })); it('in euro: de vaste koers op de euro, en de dollar voor zijn koers', () => { zaai(); assert.deepEqual(dekking('fixed'), ['EUR']); assert.deepEqual(fiat().map(a => a.symbol), ['EUR', 'USD'], 'geen derde munt als de rekenvaluta al één van de twee is'); }); it('in dollars: de vaste koers op de dollar, en de euro zonder eigen bron', () => { verseDatabase('USD'); zaai(); assert.deepEqual(dekking('fixed'), ['USD']); assert.deepEqual(startbron('USD'), ['fixed']); assert.deepEqual(startbron('EUR'), [], 'de koers van de euro komt van de dienst'); }); it('geeft een later toegevoegd BTC de bron die de portefeuille al heeft', () => { zaai(); run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit) VALUES ('BTC', 'Bitcoin', 'bitcoin', 'crypto', 'BTC', 'sat', 8, 'subunit')`); const id = database().prepare(`SELECT id FROM Asset WHERE symbol = 'BTC'`).get().id; seedPriceSource(id, 'confirmed'); assert.deepEqual(startbron('BTC'), [], 'een verse database heeft geen mempool-provider'); run(`INSERT INTO Provider (key, name, searchName, baseUrl, authenticationKind, authenticationRequired, isActive, isSeed, isSystem, createdAt) VALUES ('mempool', 'mempool', 'mempool', 'https://mempool.space', 'none', 0, 1, 1, 0, '2026-01-01T00:00:00Z')`); const pid = database().prepare(`SELECT id FROM Provider WHERE key = 'mempool'`).get().id; run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate) VALUES (?, 'spot', 'GET', '/api/v1/prices')`, [pid]); seedPriceSource(id, 'confirmed'); assert.deepEqual(startbron('BTC'), ['mempool']); }); it('geeft de assets uit de onboarding geen eigen bron: hun koers komt van de dienst', async () => { const { ASSET_DEFINITIONS } = await import('../../shared/config.js'); zaai(); for (const d of Object.values(ASSET_DEFINITIONS)) { run(`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit) VALUES (?,?,?,?,?,?,?,?)`, [d.symbol, d.name, d.name.toLowerCase(), d.kind, d.unit, d.subunit, d.decimals, d.displayUnit]); seedPriceSource(database().prepare(`SELECT id FROM Asset WHERE symbol = ?`).get(d.symbol).id, 'confirmed'); assert.deepEqual(startbron(d.symbol), [], `${d.symbol} kreeg een eigen bron`); } }); it('zet alleen de systeemproviders neer', () => { zaai(); const rijen = database().prepare(`SELECT key, isSystem FROM Provider ORDER BY key`).all().map(r => ({ ...r })); assert.deepEqual(rijen.map(r => r.key), ['atreides_services', 'fixed', 'manual']); assert.ok(rijen.every(r => r.isSystem === 1)); }); it('in ponden: drie fiat-assets, en alleen de rekenvaluta met een bron', () => { verseDatabase('GBP'); zaai(); const munten = fiat(); assert.deepEqual(munten.map(({ name, ...rest }) => rest), [ { symbol: 'EUR', subunit: 'cent', decimals: 2 }, { symbol: 'GBP', subunit: null, decimals: 2 }, { symbol: 'USD', subunit: 'cent', decimals: 2 }, ]); const pond = munten.find(a => a.symbol === 'GBP').name; assert.match(pond, /^[A-Z].*pond$/, `'${pond}' is geen Nederlandse naam met hoofdletter`); assert.deepEqual(dekking('fixed'), ['GBP']); assert.deepEqual(startbron('GBP'), ['fixed']); assert.deepEqual(startbron('USD'), []); }); it('zet in een verse database alleen de fiat-assets neer, geen crypto en geen metaal', () => { zaai(); const soorten = database().prepare(`SELECT DISTINCT kind FROM Asset`).all().map(r => r.kind); assert.deepEqual(soorten, ['fiat']); }); it('geeft een munt zonder centen ook geen centen', () => { verseDatabase('JPY'); seedAssets(); assert.equal(fiat().find(a => a.symbol === 'JPY').decimals, 0); }); it('weigert te zaaien zonder Portfolio-rij, in plaats van de euro aan te nemen', () => { closeDatabase(); openDatabase(':memory:'); run(SCHEMA); assert.throws(() => seedAssets(), /Portfolio row/); assert.throws(() => seedProviders(), /Portfolio row/); assert.throws(() => seedPriceSource(null, 'confirmed'), /Portfolio row/); }); }); describe('seedPreferences', () => { const waarde = (scope, key) => JSON.parse(database().prepare( `SELECT value FROM Preference WHERE scope = ? AND key = ?`).get(scope, key).value); it('zet de dienst vooraan in een verse database', () => { seedPreferences(); assert.equal(waarde('app', 'market_spot'), 'first'); assert.equal(PREFERENCES.app.market_spot.initial, 'off', 'de terugval van een bestaande portefeuille'); }); it('zet de AI-koppeling uit in een verse database, en laat de terugval aan', () => { seedPreferences(); assert.equal(waarde('ai', 'enabled'), 0); assert.equal(PREFERENCES.ai.enabled.initial, 1, 'de terugval van een bestaande portefeuille'); }); it('laat de andere voorkeuren op hun initial', () => { seedPreferences(); assert.equal(waarde('app', 'result_signs'), PREFERENCES.app.result_signs.initial); }); });