/** * scaffold-doc — CLIENT mode blocking guards. * * Each violated invariant must FAIL the run (errors[]) and write NOTHING — * the old silent degradation shipped doc pages that were never routed. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { generate } from '../generate.js' import { makeClientProject, clientSpec, exists, CLIENT_WEB } from './fixtures.js' let root: string beforeEach(() => { root = fs.mkdtempSync(path.join(os.tmpdir(), 'sdoc-guards-')) }) afterEach(() => { fs.rmSync(root, { recursive: true, force: true }) }) const REGISTRY_REL = [...CLIENT_WEB, 'src', 'extensions', 'docs-gaf-affairesRegistry.ts'].join('/') async function expectBlocked(spec: ReturnType, needle: string): Promise { const res = await generate(spec) expect(res.errors.length).toBeGreaterThan(0) expect(res.errors.join('\n')).toContain(needle) // Guards are all-or-nothing: nothing may have been written. expect(res.filesCreated).toEqual([]) expect(res.filesModified).toEqual([]) expect(exists(spec.projectPath, REGISTRY_REL)).toBe(false) } describe('scaffold-doc client guards', () => { it('rejects non-user types (DocRenderer is not exported by the package)', async () => { makeClientProject(root) await expectBlocked(clientSpec(root, { type: 'developer' }), 'ONLY user-type') }) it('rejects a namespace whose kebab disagrees with routePath', async () => { makeClientProject(root) await expectBlocked(clientSpec(root, { namespace: 'docs-other-thing' }), 'invariant violated') }) it('rejects a routePath rooted on a platform-owned /docs/business segment', async () => { makeClientProject(root) await expectBlocked( clientSpec(root, { namespace: 'docs-administration-users', routePath: 'administration/users', pageImportPath: '@/pages/docs/business/administration/users' }), 'platform-owned', ) }) it('rejects a pageImportPath carrying the source-only platform|personal group segment', async () => { makeClientProject(root) await expectBlocked( clientSpec(root, { pageImportPath: '@/pages/docs/business/personal/gaf/affaires' }), 'NO platform|personal segment', ) }) it('rejects a project without src/extensions/', async () => { makeClientProject(root, { withExtensions: false }) await expectBlocked(clientSpec(root), 'src/extensions/ not found') }) it('rejects a missing doc page outside dryRun (phantom lazy import)', async () => { makeClientProject(root, { withPage: false }) await expectBlocked(clientSpec(root), 'author the page BEFORE') }) it('fails on an undetectable mode and asks for an explicit one', async () => { // No package.json dependency, no componentRegistry marker, no source markers. makeClientProject(root, { withDependency: false, withExtensions: false }) const res = await generate(clientSpec(root)) expect(res.mode).toBe('unknown') expect(res.errors.join('\n')).toContain('"mode":"source"|"client"') }) it('an explicit mode override unblocks an ambiguous project', async () => { // Ambiguous detection (no dependency, no generated marker) but the real // client layout is there — the explicit override must proceed and succeed. makeClientProject(root, { withDependency: false, withExtensions: false }) fs.mkdirSync(path.join(root, ...CLIENT_WEB, 'src', 'extensions'), { recursive: true }) const res = await generate(clientSpec(root, { mode: 'client' })) expect(res.mode).toBe('client') expect(res.errors).toEqual([]) expect(exists(root, REGISTRY_REL)).toBe(true) }) })