import { describe, it, expect, vi } from 'vitest'; import { readFileSync } from 'node:fs'; /** * AC-3 itself — *"the suite fails loudly rather than skipping green"* — which nothing asserted. * * ⛔ Story 4.4 review, HIGH-3. The behaviour was correct and verified only by a reviewer running it by hand. Its corpus * twin has exactly this spec, and for a stated reason: without mocking `node:fs`, *"the only branch reachable from here * is the one that was never broken"*. The throw branch is the whole point of the guard, and it was untested. * * This spec lives in `unit`, not `corpus`: it mocks the filesystem, so it needs no checkout — which is also the only * way the ABSENT case can be exercised on a machine that has one. */ const GUARD = 'apps/cli/src/__tests__/goldenCheckout.ts'; describe('an absent golden checkout is a failure, not a skip (AC-3)', () => { const loadWithoutFixtures = async (): Promise => { vi.resetModules(); vi.doMock('node:fs', () => ({ existsSync: () => false })); try { const module = (await import('./goldenCheckout.js')) as { goldenSuite: (a: string, b: string, c: string) => unknown; }; return module.goldenSuite( 'golden-flows', 'Cannot run.', 'Because nothing is there.', ); } finally { vi.doUnmock('node:fs'); vi.resetModules(); } }; it('throws rather than returning a skippable suite', async () => { await expect(loadWithoutFixtures()).rejects.toThrow(/No golden fixtures/); }); it("says what was lost, in the caller's own words", async () => { // The three arguments exist so the message names what THIS suite cannot check, not a generic absence. await expect(loadWithoutFixtures()).rejects.toThrow( /Cannot run\. Because nothing is there\./, ); }); it('says how to fix it BOTH ways — clone it, or stop selecting these', async () => { const error = (await loadWithoutFixtures().catch( (e: unknown) => e, )) as Error; expect(error.message, 'names the repository to clone').toContain( 'hexasync-ideation-hub', ); expect(error.message, 'names the override').toContain( 'HEXASYNC_CONNECTIONS_FEATURE', ); expect(error.message, 'names the CI escape').toContain('yarn test:ci'); // And the resolved path, so a wrong override is visible rather than mysterious. expect(error.message).toContain('golden-flows/fixtures'); }); it('never consults process.env.CI', () => { // A guard keyed on CI decides for the runner instead of stating a fact about the machine it is on. expect(readFileSync(GUARD, 'utf8')).not.toContain('process.env.CI'); }); it('returns a suite with NO `.skip`, so AC-3 cannot be opted out of', () => { /** * `corpusCheckout.ts` narrows its return type for exactly this reason — *"Narrower than `typeof describe` on * purpose"*. This file used `typeof describe`, which carries `.skip`, so `goldenSuite(...).skip(...)` compiled * cleanly — re-opening the escape hatch the module exists to close. */ // The DECLARATION, not the prose: the docblock explaining the fix names the old type, and matching the whole file // failed on the very comment that records why it changed. expect( readFileSync(GUARD, 'utf8'), 'the return type re-opens `.skip`', ).not.toMatch(/type Suite\s*=\s*typeof describe/); }); });