const test = require('node:test'); const assert = require('node:assert/strict'); const { getDefaultAppSettings, normalizeSettingValue, normalizeAppSettings, fetchAppSettings, saveAppSettings } = require('../src/data/app-settings'); test('application settings expose typed defaults', () => { const settings = getDefaultAppSettings(); assert.equal(settings['app.name'], 'Pulse Signage'); assert.equal(settings['uploads.image_max_bytes'], 100 * 1024 * 1024); assert.deepEqual(settings['uploads.allowed_mime_types'], ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml', 'video/mp4', 'video/webm', 'video/ogg']); assert.equal(settings['uploads.optimize_images'], true); assert.equal(settings['security.session_lifetime_days'], 14); assert.equal(settings['security.allow_user_session_revocation'], true); assert.equal(settings['security.max_active_sessions'], 0); assert.equal(settings['security.password_min_length'], 10); assert.equal(settings['security.password_min_categories'], 3); assert.equal(settings['security.require_password_change_for_new_users'], true); assert.equal(settings['security.require_password_change_after_admin_reset'], true); assert.equal(settings['security.login_max_attempts'], 5); assert.equal(settings['security.login_lockout_minutes'], 15); assert.equal(settings['security.login_rate_limit_scope'], 'both'); assert.equal(settings['data-sources.rss_default_interval_value'], 60); assert.equal(settings['data-sources.rss_default_interval_unit'], 'minutes'); assert.equal(settings['data-sources.api_default_interval_value'], 60); assert.equal(settings['data-sources.api_default_interval_unit'], 'minutes'); }); test('application settings normalize supported values and reject invalid values', () => { assert.equal(normalizeSettingValue('player.default_fade_between_slides', 'false'), false); assert.equal(normalizeSettingValue('uploads.optimize_images', 'false'), false); assert.deepEqual(normalizeSettingValue('uploads.allowed_mime_types', ['image/png', ' image/png', 'video/mp4']), ['image/png', 'video/mp4']); assert.throws(() => normalizeSettingValue('player.default_fade_between_slides', 'bounce'), /Invalid value/); assert.throws(() => normalizeSettingValue('uploads.image_max_bytes', 0), /Invalid value/); assert.throws(() => normalizeSettingValue('unknown.setting', 'value'), /Unknown application setting/); }); test('fetch app settings merges stored values with defaults', async () => { const pool = { async query() { return [[ { setting_key: 'uploads.image_max_bytes', setting_value: '2097152' }, { setting_key: 'uploads.optimize_images', setting_value: 'false' }, { setting_key: 'unknown.setting', setting_value: 'ignored' } ]]; } }; const settings = await fetchAppSettings(pool); assert.equal(settings['uploads.image_max_bytes'], 2097152); assert.equal(settings['uploads.optimize_images'], false); assert.equal(settings['player.default_fade_between_slides'], true); }); test('save app settings writes only supplied keys in one transaction', async () => { const queries = []; const connection = { async beginTransaction() { queries.push('begin'); }, async query(sql, params) { queries.push({ sql, params }); }, async commit() { queries.push('commit'); }, async rollback() { queries.push('rollback'); }, release() { queries.push('release'); } }; const pool = { async getConnection() { return connection; } }; const saved = await saveAppSettings(pool, { 'player.default_fade_between_slides': false }, 7); assert.equal(saved['player.default_fade_between_slides'], false); assert.equal(queries[0], 'begin'); assert.equal(queries[queries.length - 2], 'commit'); assert.equal(queries[queries.length - 1], 'release'); assert.equal(queries.filter(function (entry) { return entry && entry.sql; }).length, 2); });