import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; import { ConfigError, parseSiteConfig, sectionBySlug } from '../src/config.ts'; function minimal(overrides: Record = {}): Record { return { site: 'menu.hopsngrains.com', brand: { name: 'Hops n Grains' }, sections: [ { slug: 'food', label: 'Food', order: 2 }, { slug: 'bar', label: 'Bar', order: 1 }, ], ...overrides, }; } describe('parseSiteConfig', () => { it('applies defaults and sorts Sections by order', () => { const config = parseSiteConfig(minimal()); assert.deepEqual( config.sections.map((section) => section.slug), ['bar', 'food'], ); assert.equal(config.sections[0]?.shortLabel, 'Bar'); assert.equal(config.sections[0]?.note, null); assert.equal(config.brand.tagline, null); assert.equal(config.theme.accent, '#0088aa'); assert.equal(config.limits.maxPagesPerSet, 40); assert.equal(config.limits.maxUploadBytes, 30 * 1024 * 1024); }); it('keeps an explicit theme, short label and note', () => { const config = parseSiteConfig( minimal({ theme: { copper: '#b2703b' }, sections: [{ slug: 'wine', label: 'Wine', shortLabel: 'Wine', note: 'By the glass', order: 1 }], }), ); assert.equal(config.theme.copper, '#b2703b'); assert.equal(config.theme.steel, '#14181b'); assert.equal(config.sections[0]?.note, 'By the glass'); }); it('rejects a slug the router claims', () => { assert.throws( () => parseSiteConfig(minimal({ sections: [{ slug: 'admin', label: 'Admin', order: 1 }] })), ConfigError, ); }); it('rejects duplicate slugs and duplicate orders', () => { assert.throws( () => parseSiteConfig( minimal({ sections: [ { slug: 'bar', label: 'Bar', order: 1 }, { slug: 'bar', label: 'Bar again', order: 2 }, ], }), ), ConfigError, ); assert.throws( () => parseSiteConfig( minimal({ sections: [ { slug: 'bar', label: 'Bar', order: 1 }, { slug: 'food', label: 'Food', order: 1 }, ], }), ), ConfigError, ); }); it('rejects a malformed slug', () => { assert.throws( () => parseSiteConfig(minimal({ sections: [{ slug: 'Straight Drinks', label: 'x', order: 1 }] })), ConfigError, ); }); it('rejects a colour that is not #rrggbb', () => { assert.throws(() => parseSiteConfig(minimal({ theme: { steel: 'black' } })), ConfigError); }); it('rejects a bad limit and a missing brand', () => { assert.throws(() => parseSiteConfig(minimal({ limits: { maxPagesPerSet: 0 } })), ConfigError); assert.throws(() => parseSiteConfig({ site: 'x', sections: [] }), ConfigError); }); }); describe('sectionBySlug', () => { it('finds a configured Section and reports an unknown one as undefined', () => { const config = parseSiteConfig(minimal()); assert.equal(sectionBySlug(config, 'bar')?.label, 'Bar'); assert.equal(sectionBySlug(config, 'wine'), undefined); }); });