'use strict'; const fs = require('fs'); const os = require('os'); const path = require('path'); const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'psitransfer-settings-')); process.env.PSITRANSFER_UPLOAD_DIR = tmpDir; const { test, beforeEach, after } = require('node:test'); const assert = require('node:assert'); const config = require('../../config'); const settings = require('../../lib/settings'); const PNG = Buffer.concat([Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), Buffer.alloc(32)]); const SVG = Buffer.from(''); beforeEach(() => { fs.rmSync(config.settingsDir, { recursive: true, force: true }); settings.reset(); }); after(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); }); test('settings live inside the data directory', () => { assert.strictEqual(config.settingsDir, path.join(tmpDir, '.settings')); }); test('brand falls back to the configured defaults', () => { const brand = settings.brand(); assert.strictEqual(brand.name, 'Dodo Transfer'); assert.strictEqual(brand.from, 2026); assert.strictEqual(brand.customLogo, false); assert.match(brand.logoUrl, /assets\/brand-logo\?v=[0-9a-f]{10}$/); }); test('footer shows a range once the start year is in the past', () => { assert.strictEqual(settings.footerText('ACME', 2026, 2026), '© 2026 ACME'); assert.strictEqual(settings.footerText('ACME', 2026, 2028), '© 2026–2028 ACME'); assert.strictEqual(settings.footerText('', 2026, 2026), '© 2026'); }); test('update validates and persists', () => { settings.update({ name: ' My Transfer ', owner: 'Team', from: '2024' }); assert.deepStrictEqual( JSON.parse(fs.readFileSync(path.join(config.settingsDir, 'settings.json'), 'utf8')), { name: 'My Transfer', owner: 'Team', from: 2024 } ); settings.reset(); assert.strictEqual(settings.brand().name, 'My Transfer'); assert.throws(() => settings.update({ name: ' ' }), /name/); assert.throws(() => settings.update({ name: 'x'.repeat(41) }), /name/); assert.throws(() => settings.update({ from: 1999 }), /year/); assert.throws(() => settings.update({ from: 'abc' }), /year/); assert.throws(() => settings.update({ owner: 'x'.repeat(61) }), /owner/); // failed updates change nothing assert.strictEqual(settings.brand().name, 'My Transfer'); }); test('logo is detected by its content', () => { assert.strictEqual(settings.detectLogo(PNG).mime, 'image/png'); assert.strictEqual(settings.detectLogo(SVG).mime, 'image/svg+xml'); assert.strictEqual(settings.detectLogo(Buffer.from('RIFF\0\0\0\0WEBPVP8 ')).mime, 'image/webp'); assert.strictEqual(settings.detectLogo(Buffer.from('')), null); assert.strictEqual(settings.detectLogo(Buffer.from('GIF89a......')), null); }); test('svg logos with scripts are rejected', () => { const evil = Buffer.from(''); assert.throws(() => settings.detectLogo(evil), /script/i); const handler = Buffer.from(''); assert.throws(() => settings.detectLogo(handler), /script/i); }); test('custom logo replaces the default and can be restored', () => { const before = settings.brand(); const after1 = settings.setLogo(PNG); assert.strictEqual(after1.customLogo, true); assert.notStrictEqual(after1.logoUrl, before.logoUrl, 'url changes with the image (cache busting)'); assert.ok(fs.existsSync(path.join(config.settingsDir, 'logo.png'))); // another type removes the old file settings.setLogo(SVG); assert.ok(fs.existsSync(path.join(config.settingsDir, 'logo.svg'))); assert.ok(!fs.existsSync(path.join(config.settingsDir, 'logo.png'))); assert.strictEqual(settings.logoInfo().mime, 'image/svg+xml'); const restored = settings.clearLogo(); assert.strictEqual(restored.customLogo, false); assert.strictEqual(restored.logoUrl, before.logoUrl); assert.ok(!fs.existsSync(path.join(config.settingsDir, 'logo.svg'))); }); test('invalid logos are refused', () => { assert.throws(() => settings.setLogo(Buffer.alloc(0)), /no image/); assert.throws(() => settings.setLogo(Buffer.from('not an image at all')), /PNG/); assert.throws(() => settings.setLogo(Buffer.concat([PNG, Buffer.alloc(settings.MAX_LOGO_SIZE)])), /1MB/); assert.strictEqual(settings.brand().customLogo, false); });