import { describe, it, expect } from 'vitest' import { readFileSync } from 'node:fs' import { join } from 'node:path' import { fileURLToPath } from 'node:url' import { IMPLICIT_VIEW_SUFFIXES, SUFFIX_URL, BASE_VIEW_KEYS, isImplicitSuffix } from '../implicit-suffixes.js' /** * The suffix table is a MIRROR of the socle's DynamicRouter IMPLICIT_SUFFIXES. * Its siblings (core-catalog's person-triggers:v1, platform-capabilities:v1, * permission-actions' blocks) are all drift-tested against their prose copy — * this one was not, so a table edited on one side only would either hard-err * every registry key using a new suffix (DEV-UI-046 blocking correct code) or * green-light keys that now fall into ProtectedCatchAll. */ const skillsRoot = join(fileURLToPath(new URL('.', import.meta.url)), '..', '..') function extractBlock(md: string, marker: string, file: string): string { const re = new RegExp(`([\\s\\S]*?)`) const m = md.match(re) if (!m) throw new Error(`marker "${marker}" not found in ${file}`) return m[1] } describe('implicit-suffixes ↔ routing-dynamic prose (drift lock)', () => { it('the documented suffix list matches lib/implicit-suffixes.ts exactly', () => { const file = 'development/audit/routing-dynamic/SKILL.md' const block = extractBlock(readFileSync(join(skillsRoot, file), 'utf8'), 'implicit-suffixes:v1', file) const documented = [...block.matchAll(/\.([a-z][a-z-]*)/g)].map(m => m[1]).sort() expect(documented).toEqual([...IMPLICIT_VIEW_SUFFIXES].sort()) }) it('the prose headline states the real count', () => { const file = 'development/audit/routing-dynamic/SKILL.md' const md = readFileSync(join(skillsRoot, file), 'utf8') const m = /\*\*(\d+) IMPLICIT_SUFFIXES\*\*/.exec(md) expect(m).not.toBeNull() expect(Number(m![1])).toBe(IMPLICIT_VIEW_SUFFIXES.length) }) }) describe('implicit-suffixes — internal consistency', () => { it('every suffix carries a URL pattern, and BASE views are NOT suffixes', () => { expect(IMPLICIT_VIEW_SUFFIXES).toEqual(Object.keys(SUFFIX_URL)) for (const base of BASE_VIEW_KEYS) expect(isImplicitSuffix(base)).toBe(false) }) it('id-bearing patterns are the ones carrying /:id (the click_row_in_parent contract)', () => { expect(SUFFIX_URL.detail).toBe('/:id') expect(SUFFIX_URL.create).toBe('/create') expect(SUFFIX_URL.import).toBe('/import') }) })