import { describe, expect, it } from 'vitest'; import { planInstall, resolveConfig, settingsSchema } from '../src/install.js'; const DEV = 'did:plc:developer'; const blob = (/** @type {string} */ cid, mimeType = 'text/html') => ({ $type: 'blob', ref: { $link: cid }, mimeType, size: 10, }); function fixtures(overrides = {}) { const site = { uri: `at://${DEV}/dev.pdsjs.site.deploy/photos`, cid: 'bafysite', value: { $type: 'dev.pdsjs.site.deploy', files: [ { path: 'index.html', blob: blob('bafyindex'), cacheControl: 'public, max-age=300', }, ], fallback: 'index.html', createdAt: '2026-08-11T00:00:00.000Z', }, }; const manifest = { uri: `at://${DEV}/dev.pdsjs.app.manifest/photos`, cid: 'bafymanifest', value: { $type: 'dev.pdsjs.app.manifest', name: 'Photos', site: { uri: site.uri, cid: site.cid }, queries: [ { name: 'grain-galleries', collection: 'social.grain.gallery', sort: { field: 'createdAt', direction: 'desc' }, }, ], settings: [ { key: 'topo', label: 'Terrain header', type: 'boolean', default: true, }, { key: 'rows', type: 'select', options: ['compact', 'roomy'], default: 'roomy', }, ], createdAt: '2026-08-11T00:00:00.000Z', ...overrides, }, }; return { manifest, site }; } describe('settingsSchema', () => { it('keeps typed fields and drops what it cannot render', () => { const schema = settingsSchema([ { key: 'a', type: 'boolean' }, { key: 'b', type: 'wat' }, { type: 'string' }, { key: 'c', type: 'select', options: ['x', 1, 'y'] }, ]); expect(schema.map((f) => f.key)).toEqual(['a', 'c']); expect(schema[1].options).toEqual(['x', 'y']); expect(settingsSchema('nope')).toEqual([]); }); }); describe('resolveConfig', () => { const schema = settingsSchema([ { key: 'topo', type: 'boolean', default: true }, { key: 'rows', type: 'select', options: ['compact', 'roomy'] }, ]); it('applies defaults and checked overrides', () => { expect(resolveConfig(schema)).toEqual({ topo: true }); expect(resolveConfig(schema, { topo: false, rows: 'compact' })).toEqual({ topo: false, rows: 'compact', }); }); it('rejects unknown keys and wrong types', () => { expect(() => resolveConfig(schema, { nope: 1 })).toThrow(/No such setting/); expect(() => resolveConfig(schema, { topo: 'yes' })).toThrow( /true or false/, ); expect(() => resolveConfig(schema, { rows: 'wide' })).toThrow(/one of/); }); }); describe('planInstall', () => { it('plans copies, records, defs, and the settings snapshot', () => { const { manifest, site } = fixtures(); const plan = planInstall({ manifest, site, overrides: { topo: false }, now: '2026-08-11T01:00:00.000Z', }); expect(plan.localName).toBe('photos'); expect(plan.copies).toEqual([ { path: 'index.html', cid: 'bafyindex', contentType: 'text/html' }, ]); expect(plan.siteRecord.fallback).toBe('index.html'); expect( /** @type {Array<{cacheControl?: string}>} */ (plan.siteRecord.files)[0] .cacheControl, ).toBe('public, max-age=300'); expect(plan.queryDefs).toHaveLength(1); expect(plan.queryDefs[0].rkey).toBe('grain-galleries'); expect(plan.installRecord.manifest).toEqual({ uri: manifest.uri, cid: 'bafymanifest', }); expect(plan.installRecord.config).toEqual({ topo: false, rows: 'roomy' }); expect(plan.installRecord.name).toBe('Photos'); expect( /** @type {Array<{key: string}>} */ (plan.installRecord.settings).map( (f) => f.key, ), ).toEqual(['topo', 'rows']); }); it('copies a declared icon into the install record', () => { const { manifest, site } = fixtures({ icon: { $type: 'blob', ref: { $link: 'bafyicon' }, mimeType: 'image/svg+xml', size: 400, }, }); const plan = planInstall({ manifest, site }); expect(plan.icon).toEqual({ cid: 'bafyicon', contentType: 'image/svg+xml', }); expect( /** @type {{ref: {$link: string}}} */ (plan.installRecord.icon).ref.$link, ).toBe('bafyicon'); expect( planInstall({ manifest: fixtures().manifest, site }).icon, ).toBeNull(); }); it("carries a prior install's valid settings through an update", () => { const { manifest, site } = fixtures(); const plan = planInstall({ manifest, site, existingConfig: { rows: 'compact', topo: false, gone: 'stale', rowsBad: 1, }, overrides: { topo: true }, }); // kept: rows (still valid); dropped: unknown keys; override wins on topo expect(plan.installRecord.config).toEqual({ topo: true, rows: 'compact' }); }); it('prefers the suggested site name over the artifact record key', () => { const { manifest, site } = fixtures({ suggestedSite: 'pics' }); expect(planInstall({ manifest, site }).localName).toBe('pics'); expect(planInstall({ manifest, site, localName: 'mine' }).localName).toBe( 'mine', ); }); it('rejects a drifted site pin and bad names', () => { const { manifest, site } = fixtures(); expect(() => planInstall({ manifest, site: { ...site, cid: 'bafyother' } }), ).toThrow(/changed since this manifest/); expect(() => planInstall({ manifest, site, localName: 'Not_A_Label' }), ).toThrow(/DNS label/); }); it('rejects a site record that is not the pinned one or holds nothing', () => { const { manifest, site } = fixtures(); expect(() => planInstall({ manifest, site: { ...site, uri: `at://${DEV}/dev.pdsjs.site.deploy/other` }, }), ).toThrow(/not the one the manifest names/); expect(() => planInstall({ manifest, site: { ...site, value: { ...site.value, files: [] } }, }), ).toThrow(/no files/); }); });