104 lines
4.4 KiB
JavaScript
104 lines
4.4 KiB
JavaScript
'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('<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"><rect width="1" height="1"/></svg>');
|
||
|
||
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('<html><svg></svg></html>')), null);
|
||
assert.strictEqual(settings.detectLogo(Buffer.from('GIF89a......')), null);
|
||
});
|
||
|
||
test('svg logos with scripts are rejected', () => {
|
||
const evil = Buffer.from('<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>');
|
||
assert.throws(() => settings.detectLogo(evil), /script/i);
|
||
const handler = Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"></svg>');
|
||
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);
|
||
});
|