/** * Drift lock: the tables the `/external-api` SKILL.md carries inline must stay * byte-consistent with `lib/external-api-catalog.ts`. The skill is read by an * agent that will not open the TypeScript, so a stale table there is a wrong * catalogue code or a wrong permission shipped to a third party. Edit BOTH or * this suite fails. */ import { readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' import { EXTERNAL_API_CATALOG_MARKER, EXTERNAL_API_RESERVED_MARKER, renderOperationTableBlock, renderReservedCodesBlock, SOCLE_RESERVED_CODES, PUBLIC_API_OPERATIONS, } from '../external-api-catalog.js' const skillsRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..') const SKILL = 'external-api/SKILL.md' const AUDIT_SKILL = 'development/audit-dev-external-api/SKILL.md' function readSkill(rel: string): string { return readFileSync(join(skillsRoot, rel), 'utf8') } 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].trim() } describe('external-api-catalog drift — the /external-api SKILL.md tables', () => { const md = readSkill(SKILL) it('carries the operation table equal to PUBLIC_API_OPERATIONS', () => { expect(extractBlock(md, EXTERNAL_API_CATALOG_MARKER, SKILL)).toBe(renderOperationTableBlock()) }) it('carries the reserved-code table equal to SOCLE_RESERVED_CODES', () => { expect(extractBlock(md, EXTERNAL_API_RESERVED_MARKER, SKILL)).toBe(renderReservedCodesBlock()) }) }) describe('external-api-catalog drift — the audit SKILL.md documents every implemented rule', () => { it('DEV-XAPI-001..014 each have a rule block', async () => { const md = readSkill(AUDIT_SKILL) const documented = [...md.matchAll(/^### (DEV-XAPI-\d{3})\b/gm)].map(m => m[1]) const { IMPLEMENTED_RULES } = await import( '../../development/audit-dev-external-api/cli/audit-dev-external-api/audit.js' ) expect(documented).toEqual([...IMPLEMENTED_RULES]) }) it('names the platform-reserved codes the audit refuses', () => { const md = readSkill(AUDIT_SKILL) for (const code of SOCLE_RESERVED_CODES) { expect(md).toContain(`\`${code}\``) } }) }) describe('external-api-catalog — the operation table is internally coherent', () => { it('exactly one operation keeps the canonical (suffix-less) code', () => { expect(PUBLIC_API_OPERATIONS.filter(o => o.codeSuffix === null)).toHaveLength(1) expect(PUBLIC_API_OPERATIONS.find(o => o.codeSuffix === null)?.operation).toBe('read') }) it('every write operation is catalogued as Write, and only reads as Read', () => { for (const op of PUBLIC_API_OPERATIONS) { expect(op.accessType).toBe(op.operation === 'read' ? 'Read' : 'Write') } }) it('no two operations share an HTTP verb — the verb → operation map stays a function', () => { const verbs = PUBLIC_API_OPERATIONS.flatMap(o => o.httpVerbs) expect(new Set(verbs).size).toBe(verbs.length) }) })