/** * scaffold-routes — registry layout gate. Emitting a per-module registry on an * app whose componentRegistry.generated.ts registers pages of its OWN (legacy * MCP-era monolith / mixed) creates key collisions and arms the destructive * re-aggregation. The gate fail-closes and names the sanctioned migration * (split-component-registry); after migration the layout is per-module and the * gate never fires. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { validate } from '../validate.js' let web: string beforeEach(() => { web = fs.mkdtempSync(path.join(os.tmpdir(), 'sroutes-guard-')) }) afterEach(() => { fs.rmSync(web, { recursive: true, force: true }) }) function write(rel: string, content: string): void { const abs = path.join(web, rel) fs.mkdirSync(path.dirname(abs), { recursive: true }) fs.writeFileSync(abs, content) } function spec() { return { module: 'bikes', appCode: 'fleet', entities: [{ name: 'Bike', section: 'bikes', views: ['list', 'detail'] }], projectPath: web, } } const MONOLITH = `import { lazy } from 'react'; import { PageRegistry } from '@atlashub/smartstack'; PageRegistry.register('fleet.bikes', lazy(() => import('@/pages/fleet/bikes/FleetBikeListPage'))); ` describe('scaffold-routes — legacy layout gate', () => { it('fail-closes on a legacy monolith, naming split-component-registry', () => { write('src/extensions/componentRegistry.generated.ts', MONOLITH) const res = validate(spec()) expect(res.valid).toBe(false) expect(res.errors[0]).toContain('split-component-registry') expect(res.errors[0]).toContain('legacy MCP-era monolithic') }) it('fail-closes on a mixed layout too', () => { write('src/extensions/componentRegistry.generated.ts', MONOLITH) write( 'src/extensions/fleet-stationsRegistry.ts', `import { PageRegistry, lazyWithRetry } from '@atlashub/smartstack'; const S = lazyWithRetry(() => import('@/pages/fleet/stations/StationsListPage')); PageRegistry.register('fleet.stations', S); `, ) const res = validate(spec()) expect(res.valid).toBe(false) expect(res.errors[0]).toContain('mixed') }) it('passes on the canonical per-module layout (clean aggregate)', () => { write( 'src/extensions/componentRegistry.generated.ts', `import './fleet-bikesRegistry';\n`, ) write( 'src/extensions/fleet-bikesRegistry.ts', `import { PageRegistry, lazyWithRetry } from '@atlashub/smartstack'; const B = lazyWithRetry(() => import('@/pages/fleet/bikes/BikesListPage')); PageRegistry.register('fleet.bikes', B); `, ) const res = validate(spec()) expect(res.valid).toBe(true) }) it('passes on a fresh app (no registry at all)', () => { const res = validate(spec()) expect(res.valid).toBe(true) }) })