/** * registry-guard — the takeover guard that keeps scaffold-component from * shipping a DEAD DUPLICATE page: a componentKey the live registry already * serves from another file (legacy app, pages outside the canonical * convention) must fail-close unless the spec consents with allowTakeover. */ import { describe, it, expect } from 'vitest' import { checkRegistryCollisions } from '../registry-guard.js' const EMITTED = new Map([ ['list', 'D:/app/web/src/pages/fleet/bikes/BikesListPage.tsx'], ['detail', 'D:/app/web/src/pages/fleet/bikes/BikeDetailPage.tsx'], ]) function byKey(entries: Array<[string, { file: string; resolvedAbsPath: string | null }]>) { return new Map(entries) } describe('checkRegistryCollisions', () => { it('flags a key served from a different page file', () => { const collisions = checkRegistryCollisions({ byKey: byKey([ ['fleet.bikes', { file: 'src/extensions/componentRegistry.generated.ts', resolvedAbsPath: 'D:/app/web/src/pages/fleet/bikes/FleetBikeListPage.tsx' }], ]), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, }) expect(collisions).toHaveLength(1) expect(collisions[0]!.key).toBe('fleet.bikes') expect(collisions[0]!.view).toBe('list') expect(collisions[0]!.registeredFile).toContain('FleetBikeListPage') }) it('passes when the registry already points at the emitted file (regenerate in place)', () => { const collisions = checkRegistryCollisions({ byKey: byKey([ ['fleet.bikes', { file: 'src/extensions/fleet-bikesRegistry.ts', resolvedAbsPath: 'D:\\app\\web\\src\\pages\\fleet\\bikes\\BIKESLISTPAGE.TSX' }], ]), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, }) // path comparison is slash- and case-insensitive (win32) expect(collisions).toEqual([]) }) it('matches the routeParent 4-segment key shape', () => { const collisions = checkRegistryCollisions({ byKey: byKey([ ['fleet.bikes.maintenance.bikes.detail', { file: 'src/extensions/x.ts', resolvedAbsPath: 'D:/app/web/src/pages/legacy/Old.tsx' }], ]), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, }) expect(collisions).toHaveLength(1) expect(collisions[0]!.view).toBe('detail') }) it('ignores keys of other sections/modules and views this run does not emit', () => { const collisions = checkRegistryCollisions({ byKey: byKey([ ['fleet.stations', { file: 'src/extensions/x.ts', resolvedAbsPath: 'D:/app/web/src/pages/S.tsx' }], ['billing.bikes', { file: 'src/extensions/x.ts', resolvedAbsPath: 'D:/app/web/src/pages/B.tsx' }], ['fleet.bikes.create', { file: 'src/extensions/x.ts', resolvedAbsPath: 'D:/app/web/src/pages/F.tsx' }], ]), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, // no 'create' emitted }) expect(collisions).toEqual([]) }) it('an unresolved live import is a collision too (the key serves a dead page)', () => { const collisions = checkRegistryCollisions({ byKey: byKey([ ['fleet.bikes', { file: 'src/extensions/componentRegistry.generated.ts', resolvedAbsPath: null }], ]), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, }) expect(collisions).toHaveLength(1) expect(collisions[0]!.registeredFile).toContain('unresolved import') }) it('empty registry (fresh app) — no collisions', () => { const collisions = checkRegistryCollisions({ byKey: new Map(), appCode: 'fleet', module: 'bikes', section: 'bikes', emittedByView: EMITTED, }) expect(collisions).toEqual([]) }) })