import { readFileSync } from 'node:fs' import { describe, expect, it } from 'vitest' import { DATASET_NAMES, DIRECTORY_CATEGORIES, THEMES, assertOverlayParity, loadDataset, } from '../../src/lib/content' import { locales } from '../../src/i18n/locales' /* Content-integrity tests, ported from the pr1 typed-React content to the JSON core + per-locale overlay split: the DATA is what the sections render, so these guard it at build time in every language. */ describe('core + overlay integrity', () => { it('every dataset merges cleanly with every locale overlay (en/fr/nl parity)', () => { for (const name of DATASET_NAMES) { for (const locale of locales) { const rows = loadDataset(name, locale) expect(rows.length, `${name}/${locale}`).toBeGreaterThan(0) } } }) it('ids are unique within each dataset AND across all datasets (deep links)', () => { const all: string[] = [] for (const name of DATASET_NAMES) { const ids = loadDataset(name, 'en').map((row) => row.id) expect(new Set(ids).size, name).toBe(ids.length) for (const id of ids) expect(id, `${name}: ${id}`).toMatch(/^[a-z0-9-]+$/) all.push(...ids) } const dupes = all.filter((id, i) => all.indexOf(id) !== i) expect(dupes, `cross-dataset id collisions: ${dupes.join(', ')}`).toEqual([]) }) it('the parity check throws listing every missing and extra id', () => { const overlay = { a: { body: 'x' }, ghost: { body: 'y' }, _marker: true } expect(() => assertOverlayParity(['a', 'b', 'c'], overlay, 'unit')).toThrow( /missing ids \(2\): b, c.*extra ids \(1\): ghost/, ) expect(() => assertOverlayParity(['a', 'ghost'], overlay, 'unit')).not.toThrow() }) it('nl overlays carry the machine-translated stamp (no pending marker left)', () => { for (const name of DATASET_NAMES) { const raw = JSON.parse( readFileSync(new URL(`../../src/i18n/content/nl/${name}.json`, import.meta.url), 'utf8'), ) as Record expect(raw._machineTranslationPending, name).toBeUndefined() expect(raw._machineTranslated, name).toMatch(/^\d{4}-\d{2}-\d{2}$/) } }) }) describe('precedents timeline (events)', () => { const events = loadDataset('events', 'en') it('has the full 23-event arc, chronologically sorted', () => { expect(events).toHaveLength(23) const dates = events.map((e) => e.date) expect(dates).toEqual([...dates].sort()) }) it('opens in the 90s crypto wars, ends on the 9 July 2026 second-reading vote', () => { expect(events[0]?.id).toBe('clipper-chip') expect(events[0]?.date).toBe('1993') expect(events[events.length - 1]?.date).toBe('2026-07-09') }) it('every event is titled, bodied and primary-sourced over https in every locale', () => { for (const locale of locales) { for (const ev of loadDataset('events', locale)) { expect(ev.title.length, `${ev.id}/${locale}`).toBeGreaterThan(3) expect(ev.body.length, `${ev.id}/${locale}`).toBeGreaterThan(80) expect(ev.src.href.startsWith('https://'), `${ev.id}: ${ev.src.href}`).toBe(true) } } }) }) describe('big brother observatory', () => { const drift = loadDataset('observatory', 'en') it('has 35 entries, chronologically sorted', () => { expect(drift).toHaveLength(35) const dates = drift.map((e) => e.date) expect(dates).toEqual([...dates].sort()) }) it('covers all 5 fronts and the 3 region groups (no filter chip is ever empty)', () => { const themes = new Set(drift.flatMap((e) => e.themes)) for (const t of THEMES) expect(themes.has(t), t).toBe(true) const groups = new Set( drift.map((e) => (e.region === 'eu' ? 'eu' : e.region === 'fr' ? 'fr' : 'world')), ) expect([...groups].sort()).toEqual(['eu', 'fr', 'world']) }) it('every entry is sourced over https and stays disjoint from the precedents timeline', () => { const timelineSources = new Set(loadDataset('events', 'en').map((e) => e.src.href)) for (const ev of drift) { expect(ev.src.href.startsWith('https://'), `${ev.id}: ${ev.src.href}`).toBe(true) expect(timelineSources.has(ev.src.href), `${ev.id} reuses a precedents source`).toBe(false) } }) it('pending-verification flags only sit on the unverified 2025-09 → 2026-07 batch', () => { for (const ev of drift.filter((e) => e._pendingVerification)) { expect( ev.date >= '2025-09', `${ev.id} (${ev.date}) flagged but part of the verified batch`, ).toBe(true) } }) }) describe('open-source directory', () => { const SPDX_ALLOWED = new Set([ 'AGPL-3.0', 'GPL-3.0', 'GPL-2.0', 'GPL-2.0/3.0', 'LGPL-3.0', 'MIT', 'Apache-2.0', 'MPL-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'EUPL-1.2', 'Artistic/GPL', ]) const directory = loadDataset('directory', 'en') it('has 67 entries: free-software license allowlist, https links', () => { expect(directory).toHaveLength(67) for (const e of directory) { expect(SPDX_ALLOWED.has(e.license), `${e.name}: license ${e.license}`).toBe(true) expect(e.url.startsWith('https://'), e.name).toBe(true) } }) it('names are unique and no category of the 14 is empty', () => { const names = directory.map((e) => e.name) expect(new Set(names).size).toBe(names.length) expect(DIRECTORY_CATEGORIES).toHaveLength(14) for (const cat of DIRECTORY_CATEGORIES) { expect( directory.some((e) => e.category === cat), cat, ).toBe(true) } }) it('the undisclosed self-promo and scaffolding entries stay removed', () => { const names = new Set(directory.map((e) => e.name)) for (const gone of ['Nika', 'goose', 'OpenHands', 'Aider', 'vLLM']) { expect(names.has(gone), gone).toBe(false) } expect(directory.some((e) => e.url.includes('nika.sh'))).toBe(false) }) }) describe('migration checklist', () => { const checklist = loadDataset('checklist', 'en') it('has the 14-step plan with all three effort levels', () => { expect(checklist).toHaveLength(14) const levels = new Set(checklist.map((c) => c.level)) expect([...levels].sort()).toEqual(['green', 'red', 'yellow']) }) }) describe('allied initiatives', () => { const allies = loadDataset('allies', 'en') it('lists 8 campaigns and 8 chroniclers, all https', () => { expect(allies.filter((a) => a.group === 'campaigns')).toHaveLength(8) expect(allies.filter((a) => a.group === 'chroniclers')).toHaveLength(8) for (const a of allies) expect(a.url.startsWith('https://'), a.name).toBe(true) }) })