import { afterEach, describe, expect, it } from 'vitest' import { spawnSync } from 'node:child_process' import { writeFileSync, rmSync, mkdirSync, existsSync } from 'node:fs' import { join, resolve } from 'node:path' // AZ-485 / F4 — verifies the STC-ARCH-01 static gate (scripts/check-arch-imports.mjs): // - AC-5 : passes on the migrated codebase as-is // - AC-4 : fails when a synthetic cross-component deep import is added // - AC-4 : ignores the F3-pending exemption (features/annotations/classColors) // - AC-4 : ignores deep imports written inside // line comments // // Exercises the actual gate via subprocess so a regression in the script // (regex drift, exemption logic, exit code) trips the test even if the bash // glue in run-tests.sh keeps reporting PASS. // // All offending substrings are built via concatenation at runtime so the // scanner itself does not flag this test file when it walks `tests/**`. const REPO_ROOT = resolve(__dirname, '..') const SCRIPT = join(REPO_ROOT, 'scripts', 'check-arch-imports.mjs') const FIXTURE_DIR = join(REPO_ROOT, 'tests', '_arch_fixtures') const FROM = 'fr' + 'om' const UP2 = '..' + '/..' const DEEP_API = `${UP2}/src/api/cl` + 'ient' const DEEP_CLASSCOLORS = `${UP2}/src/features/annotations/classCo` + 'lors' function runCheck(): { status: number; stderr: string } { const res = spawnSync('node', [SCRIPT, `--root=${REPO_ROOT}`], { cwd: REPO_ROOT, encoding: 'utf8', }) return { status: res.status ?? -1, stderr: res.stderr ?? '' } } function writeFixture(filename: string, content: string): string { mkdirSync(FIXTURE_DIR, { recursive: true }) const path = join(FIXTURE_DIR, filename) writeFileSync(path, content, 'utf8') return path } describe('AZ-485 STC-ARCH-01 — no cross-component deep imports', () => { afterEach(() => { if (existsSync(FIXTURE_DIR)) rmSync(FIXTURE_DIR, { recursive: true, force: true }) }) it('AC-5: passes on the migrated codebase (no fixtures)', () => { // Assert const { status, stderr } = runCheck() expect(stderr, stderr).toBe('') expect(status).toBe(0) }) it('AC-4: FAILS when a deep import into another component is introduced', () => { // Arrange const body = `import { api } ${FROM} '${DEEP_API}'\nexport const _force = api\n` writeFixture('synthetic_deep_import.ts', body) // Act const { status, stderr } = runCheck() // Assert expect(status).not.toBe(0) expect(stderr).toMatch(/STC-ARCH-01/) expect(stderr).toMatch(/synthetic_deep_import\.ts/) expect(stderr).toMatch(/src\/api\/client/) }) it('AC-4: still PASSES when only the classColors F3-pending exemption is used', () => { // Arrange const body = `import { FALLBACK_CLASS_NAMES } ${FROM} '${DEEP_CLASSCOLORS}'\n` + `export const _force = FALLBACK_CLASS_NAMES\n` writeFixture('classcolors_exemption.ts', body) // Act const { status, stderr } = runCheck() // Assert expect(stderr, stderr).toBe('') expect(status).toBe(0) }) it('AC-4: deep imports inside line comments do not trip the gate', () => { // Arrange const body = `// import { api } ${FROM} '${DEEP_API}'\nexport const _x = 1\n` writeFixture('commented_out_deep_import.ts', body) // Act const { status } = runCheck() // Assert expect(status).toBe(0) }) })