import { describe, expect, test } from 'bun:test'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { classifyModulePath } from './package-rules'; /** * Real input, not input built by the same helper as the expectation. * * These fixtures are verbatim `celilo module verify` output captured from the * live fleet on 2026-08-19 (see the fixture README). Every path below is a path * that actually exists in an installed module tree on celilo-mgr. celilo#951 * shipped 18 false positives because its comparator's test synthesised both * sides of the comparison and so never saw real input. */ function fixturePaths(name: string): string[] { const raw = readFileSync( join(process.cwd(), 'test-fixtures', 'module-integrity', `${name}-verify.txt`), 'utf-8', ); const paths: string[] = []; for (const line of raw.split('\n')) { const match = /(?:Checksum mismatch|Unexpected file): (.+)$/.exec(line.trim()); if (match?.[1]) paths.push(match[1]); } return paths; } describe('classifyModulePath against the live fleet listings', () => { for (const [moduleName, expectedCount] of [ ['wireguard-manager', 47], ['wireguard', 25], ] as const) { describe(moduleName, () => { const paths = fixturePaths(moduleName); test(`fixture carries all ${expectedCount} reported paths`, () => { expect(paths.length).toBe(expectedCount); }); test('no path in a real installed tree classifies unknown', () => { const unknown = paths.filter((p) => classifyModulePath(p) === 'unknown'); expect(unknown).toEqual([]); }); test('celilo-owned paths classify derived', () => { const derived = paths.filter( (p) => p.startsWith('generated/') || p.includes('node_modules/') || p === 'checksums.json' || p === 'signature.sig', ); // Guard the guard: if this is empty the assertion below is vacuous. expect(derived.length).toBeGreaterThan(0); for (const p of derived) { expect(`${p} => ${classifyModulePath(p)}`).toBe(`${p} => derived`); } }); test("the module's own content classifies package", () => { const own = paths.filter( (p) => !p.startsWith('generated/') && !p.includes('node_modules/') && p !== 'checksums.json' && p !== 'signature.sig', ); expect(own.length).toBeGreaterThan(0); for (const p of own) { expect(`${p} => ${classifyModulePath(p)}`).toBe(`${p} => package`); } }); }); } }); describe('classifyModulePath: source-tree paths that must never be installed', () => { test.each([ ['e2e/deploy.test.ts'], ['.git/config'], ['scripts/tsconfig.json'], ['.DS_Store'], ['server/src/api.test.ts'], ['wireguard.netapp'], ['node_modules/tldts/package.json'], ])('%s is unknown', (relPath) => { expect(classifyModulePath(relPath)).toBe('unknown'); }); }); describe('classifyModulePath: composes with includeNodeModulesPath', () => { test('the hook runtime closure is derived, not unknown', () => { expect(classifyModulePath('scripts/node_modules/tldts/index.js')).toBe('derived'); }); test('a .bin shim is still excluded', () => { expect(classifyModulePath('scripts/node_modules/.bin/tsc')).toBe('unknown'); }); test('non-scripts node_modules ships only @celilo/capabilities', () => { expect(classifyModulePath('node_modules/@celilo/capabilities/src/index.ts')).toBe('derived'); expect(classifyModulePath('node_modules/@celilo/cli/index.js')).toBe('unknown'); }); }); describe('a submodule tree obeys the same rules as a module tree', () => { // openspec/changes/submodules D1: a submodule declares everything an ordinary // module declares, so it must be CLASSIFIED like one too. Every rule keys on // segments[0], so without the recursion a nested tree gets different answers // than the same file one level up. test('authored source under a submodule is package', () => { expect(classifyModulePath('submodules/runner/manifest.yml')).toBe('package'); expect(classifyModulePath('submodules/runner/ansible/site.yml')).toBe('package'); expect(classifyModulePath('submodules/runner/scripts/hook.ts')).toBe('package'); }); test("a submodule's e2e tree is excluded, exactly as a module's is", () => { expect(classifyModulePath('e2e/thing.ts')).toBe('unknown'); expect(classifyModulePath('submodules/runner/e2e/thing.ts')).toBe('unknown'); }); test("a submodule's derived paths are derived", () => { expect(classifyModulePath('submodules/runner/generated/terraform/x.tfstate')).toBe('derived'); expect(classifyModulePath('submodules/runner/celilo/types.d.ts')).toBe('derived'); expect(classifyModulePath('submodules/runner/cookies.json')).toBe('derived'); expect(classifyModulePath('submodules/runner/screenshots/a.png')).toBe('derived'); }); test('source-tree noise under a submodule is still a finding', () => { expect(classifyModulePath('submodules/runner/.DS_Store')).toBe('unknown'); expect(classifyModulePath('submodules/runner/tsconfig.json')).toBe('unknown'); expect(classifyModulePath('submodules/runner/a.test.ts')).toBe('unknown'); }); // The directories themselves are shipped bytes and belong in the baseline. test('the submodules directory itself is package', () => { expect(classifyModulePath('submodules')).toBe('package'); expect(classifyModulePath('submodules/runner')).toBe('package'); }); });