Files
AtreidesAssetsFree/tests/repo/preferences.test.mjs
T

169 lines
6.9 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:72e7504c7f13026263eeeda6dcafb41dafbff8bea070ad9a3fc74a1411bd4d43
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createDbHarness } from '../helpers/db-node.mjs';
import { PREFERENCES, ENUMS, CONFIG } from '../../shared/config.js';
process.env.ASSETAPP_DATA_DIR = mkdtempSync(join(tmpdir(), 'assetapp-prefs-'));
const harness = createDbHarness({ seedPreferences: true });
mock.module('../../server/db.js', { namedExports: harness.exports });
const { getPref, getSettings, setPref, updateSettings } = await import('../../server/data/preferences.js');
beforeEach(() => harness.recreate());
const ruweWaarde = (scope, key) => {
const r = harness.exports.one(`SELECT value FROM Preference WHERE scope=? AND key=?`, [scope, key]);
return r ? r.value : null;
};
describe('setPref: validatie tegen de registry', () => {
it('accepteert elke toegestane enum-waarde', async () => {
for (const thema of ENUMS.THEME) {
await setPref('app', 'theme', thema);
assert.equal(getPref('app', 'theme'), thema);
}
});
it('weigert een enum-waarde buiten de registry', async () => {
await assert.rejects(() => setPref('app', 'theme', 'neon'), /theme/);
assert.equal(getPref('app', 'theme'), PREFERENCES.app.theme.initial,
'de oude waarde blijft staan');
});
it('maakt van een bool een 0 of 1', async () => {
await setPref('app', 'blur', true);
assert.equal(getPref('app', 'blur'), 1, 'true wordt 1');
await setPref('app', 'blur', 0);
assert.equal(getPref('app', 'blur'), 0);
});
it('weigert een bool-waarde die geen 0 of 1 oplevert', async () => {
await assert.rejects(() => setPref('app', 'blur', 2), /blur/);
await assert.rejects(() => setPref('app', 'blur', 'ja'), /blur/,
'niet-numerieke tekst levert NaN op en hoort geweigerd te worden');
});
it('eist bij een int een geheel getal binnen min/max', async () => {
const spec = PREFERENCES.app.auto_blur_min;
await setPref('app', 'auto_blur_min', spec.min);
assert.equal(getPref('app', 'auto_blur_min'), spec.min);
await assert.rejects(() => setPref('app', 'auto_blur_min', 2.5), (e) => e.key === 'settings.prefWantsInteger');
await assert.rejects(() => setPref('app', 'auto_blur_min', spec.min - 1), (e) => e.key === 'settings.prefBelowMin');
await assert.rejects(() => setPref('app', 'auto_blur_min', spec.max + 1), (e) => e.key === 'settings.prefAboveMax');
});
it('laat een string ongemoeid', async () => {
await setPref('app', 'last_backup_at', '2026-10-27T10:00:00Z');
assert.equal(getPref('app', 'last_backup_at'), '2026-10-27T10:00:00Z');
});
it('bewaart een scope zonder registry-regel as-is', async () => {
const view = { sort: { key: 'occurredAt', dir: 'desc' }, group: null };
await setPref('table.trades', 'view', view);
assert.deepEqual(getPref('table.trades', 'view'), view);
});
});
describe('getPref: terugval en coercie', () => {
it('geeft de initiële waarde uit de registry als er geen rij is', () => {
harness.exports.run(`DELETE FROM Preference`);
assert.equal(getPref('app', 'theme'), PREFERENCES.app.theme.initial);
});
it('geeft de meegegeven fallback bij een onbekende sleutel', () => {
assert.equal(getPref('bestaat', 'niet', 'terugval'), 'terugval');
assert.equal(getPref('bestaat', 'niet'), undefined, 'zonder fallback: undefined');
});
it('coërceert een opgeslagen enum-waarde die niet meer bestaat', () => {
assert.ok(!ENUMS.ACCENT.includes('paars'));
harness.exports.run(
`INSERT INTO Preference (scope,key,value) VALUES ('app','accent','"paars"')
ON CONFLICT(scope,key) DO UPDATE SET value = excluded.value`);
assert.equal(getPref('app', 'accent'), PREFERENCES.app.accent.initial);
});
it('laat een geldige enum-waarde ongemoeid', () => {
const geldig = ENUMS.ACCENT.at(-1);
harness.exports.run(
`INSERT INTO Preference (scope,key,value) VALUES ('app','accent',?)
ON CONFLICT(scope,key) DO UPDATE SET value = excluded.value`, [JSON.stringify(geldig)]);
assert.equal(getPref('app', 'accent'), geldig);
});
});
describe('voorkeuren dragen geen geheimen meer', () => {
it('heeft nergens in de registry nog een secret-vlag', () => {
const metVlag = [];
for (const [scope, keys] of Object.entries(PREFERENCES)) {
for (const [key, spec] of Object.entries(keys)) {
if (spec.secret) metVlag.push(`${scope}.${key}`);
}
}
assert.deepEqual(metVlag, []);
});
it('kent de nwc-scope niet meer', () => {
assert.equal(PREFERENCES.nwc, undefined,
'de NWC-URI hoort server-side te staan (data/secrets.json, sleutel nwc_<bron-id>)');
});
it('slaat elke waarde op als leesbare JSON, ook onder een onbekende scope', async () => {
await setPref('view.demo', 'stand', { sort: 'naam' });
assert.deepEqual(JSON.parse(ruweWaarde('view.demo', 'stand')), { sort: 'naam' },
'een vrije scope wordt as-is bewaard; er is geen versleutel-pad meer dat dit onderschept');
});
});
describe('getSettings / updateSettings', () => {
it('levert elke app-voorkeur plus de schemaversie', () => {
const s = getSettings();
for (const key of Object.keys(PREFERENCES.app)) {
if (!PREFERENCES.app[key].secret) assert.ok(key in s, `${key} ontbreekt`);
}
assert.equal(s.version, CONFIG.SCHEMA_VERSION);
});
it('werkt meerdere instellingen in één keer bij', async () => {
await updateSettings({ theme: 'light', locale: 'nl' });
assert.equal(getPref('app', 'theme'), 'light');
assert.equal(getSettings().locale, 'nl');
});
it('bewaart de taal bij de installatie en niet in de portefeuille', async () => {
await updateSettings({ locale: 'en' });
assert.equal(ruweWaarde('app', 'locale'), null);
const bestand = join(process.env.ASSETAPP_DATA_DIR, CONFIG.INSTALLATION.SETTINGS_FILE_NAME);
assert.equal(JSON.parse(readFileSync(bestand, 'utf8')).locale, 'en');
harness.recreate();
assert.equal(getSettings().locale, 'en', 'een andere database verandert de taal niet');
});
it('weigert een taal zonder catalogus', async () => {
await updateSettings({ locale: 'nl' });
await assert.rejects(() => updateSettings({ locale: 'xx' }), /Unknown locale/);
assert.equal(getSettings().locale, 'nl');
});
it('slaat undefined-waarden over', async () => {
await updateSettings({ locale: 'nl' });
await updateSettings({ theme: 'light', locale: undefined });
assert.equal(getSettings().locale, 'nl');
});
it('weigert een onbekende instelling', async () => {
await assert.rejects(() => updateSettings({ verzonnen: 1 }), (e) => e.key === 'settings.unknownSetting');
});
it('valideert nog steeds per type', async () => {
await assert.rejects(() => updateSettings({ theme: 'neon' }), /theme/);
});
});