import { dirname, join, resolve } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { beforeAll, describe, expect, it } from 'vitest'; // The Core guard's area table (AGENTS.md, "Core Engine and engines"): engine paths come before the // Core areas, an unregistered source path stays Core, and a bare folder name belongs to the prefix // it names, so renaming a whole engine folder is not mistaken for a Core change. const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); interface AreaTable { classifyPath(relative: string | null): string | null; relativeToProject(filePath: string, projectDir: string): string | null; resolveAreaName(raw: string): string | null; isReleased(area: string, releasedAreas: string[]): boolean; } let areas: AreaTable; beforeAll(async () => { areas = await import(/* @vite-ignore */ pathToFileURL(join(root, '.claude', 'hooks', 'core-areas.mjs')).href) as AreaTable; }); describe('Core guard area table', () => { it.each([ 'src/compliance', 'src/compliance/', 'src/compliance/ui/regulatorymappingpanel.tsx', 'server/compliance', 'tests/compliance', 'src/assessment-context', 'src/reporting', 'server/reporting' ])('treats engine path %s as engine code', path => { expect(areas.classifyPath(path)).toBeNull(); }); it.each([ ['src/compliance-x', 'Unclassified Source'], ['src/compliance-x/file.ts', 'Unclassified Source'], ['src/newthing', 'Unclassified Source'], ['src/persistence', 'Store'], ['src/persistence/local-assessor-repository.ts', 'Store'], ['server/postgres', 'Store'], ['src/app', 'Presentation'], ['src/features/finding-reader/findingreader.tsx', 'Presentation'], ['server/index.ts', 'Platform'], ['tests/architecture/core-areas.test.ts', 'Core Tests'], ['agents.md', 'Governance'], ['.claude/hooks', 'Governance'], ['src/domain/review.ts', 'Client Records'] ])('keeps %s in Core area %s', (path, area) => { expect(areas.classifyPath(path)).toBe(area); }); it('leaves paths outside the project and documentation unguarded', () => { expect(areas.classifyPath(null)).toBeNull(); expect(areas.relativeToProject(join(root, '..', 'elsewhere.ts'), root)).toBeNull(); expect(areas.classifyPath('docs/implementation_plan.md')).toBeNull(); expect(areas.relativeToProject(join(root, 'src', 'Compliance', 'X.ts'), root)).toBe('src/compliance/x.ts'); }); it('resolves release names and never lets "alle" cover Governance', () => { expect(areas.resolveAreaName(' Core Tests ')).toBe('Core Tests'); expect(areas.resolveAreaName('unclassified source')).toBe('Unclassified Source'); expect(areas.resolveAreaName('compliance')).toBeNull(); expect(areas.isReleased('Store', ['alle'])).toBe(true); expect(areas.isReleased('Governance', ['alle'])).toBe(false); expect(areas.isReleased('Governance', ['Governance'])).toBe(true); expect(areas.isReleased('Store', ['Platform'])).toBe(false); }); });