chore: crush git history - reborn from consolidation on 2026-03-10
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
// src/test/cli/packaging.test.ts — packaging and entrypoint hardening tests
|
||||
// Ensures exactly one canonical invocation path and no broken alternatives.
|
||||
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
||||
const ROOT = new URL('../../../', import.meta.url).pathname;
|
||||
const DIST_CLI = join(ROOT, 'dist/cli/index.js');
|
||||
const PACKAGE_JSON = join(ROOT, 'package.json');
|
||||
|
||||
function run(args: string[], cwd?: string) {
|
||||
const result = spawnSync('node', [DIST_CLI, ...args], {
|
||||
encoding: 'utf8',
|
||||
cwd: cwd || ROOT,
|
||||
timeout: 30000,
|
||||
});
|
||||
return {
|
||||
stdout: result.stdout || '',
|
||||
stderr: result.stderr || '',
|
||||
status: result.status,
|
||||
signal: result.signal,
|
||||
};
|
||||
}
|
||||
|
||||
describe('packaging', () => {
|
||||
it('dist/cli/index.js exists after build', () => {
|
||||
assert(existsSync(DIST_CLI), `Missing ${DIST_CLI} — run npm run build first`);
|
||||
});
|
||||
|
||||
it('--help exits 0 and prints expected help text', () => {
|
||||
const { stdout, status } = run(['--help']);
|
||||
assert.strictEqual(status, 0, `Expected exit 0, got ${status}. stderr: ${run(['--help']).stderr}`);
|
||||
assert(stdout.includes('apophis'), 'Help should mention apophis');
|
||||
assert(stdout.includes('Commands:'), 'Help should list commands');
|
||||
assert(stdout.includes('init'), 'Help should mention init');
|
||||
assert(stdout.includes('verify'), 'Help should mention verify');
|
||||
});
|
||||
|
||||
it('--version exits 0 and prints version', () => {
|
||||
const { stdout, status } = run(['--version']);
|
||||
assert.strictEqual(status, 0, `Expected exit 0, got ${status}`);
|
||||
assert.match(stdout, /2\.0\.0/, `Version should include 2.0.0, got: ${stdout}`);
|
||||
});
|
||||
|
||||
it('init --help exits 0 and prints init help', () => {
|
||||
const { stdout, status } = run(['init', '--help']);
|
||||
assert.strictEqual(status, 0);
|
||||
assert(stdout.includes('apophis init'), 'Should show init help header');
|
||||
assert(stdout.includes('--preset'), 'Should mention --preset');
|
||||
});
|
||||
|
||||
it('verify --help exits 0 and prints verify help', () => {
|
||||
const { stdout, status } = run(['verify', '--help']);
|
||||
assert.strictEqual(status, 0);
|
||||
assert(stdout.includes('apophis verify'), 'Should show verify help header');
|
||||
assert(stdout.includes('--routes'), 'Should mention --routes');
|
||||
});
|
||||
|
||||
it('frobnicate exits 2 with "Unknown command"', () => {
|
||||
const { stdout, stderr, status } = run(['frobnicate']);
|
||||
assert.strictEqual(status, 2, `Expected exit 2, got ${status}`);
|
||||
const combined = stdout + stderr;
|
||||
assert(combined.includes('Unknown command'), `Should report unknown command. Got: ${combined}`);
|
||||
});
|
||||
|
||||
it('verify --unknown-flag exits 2 with "Unknown flag"', () => {
|
||||
const { stdout, stderr, status } = run(['verify', '--unknown-flag']);
|
||||
assert.strictEqual(status, 2, `Expected exit 2, got ${status}`);
|
||||
const combined = stdout + stderr;
|
||||
assert(combined.includes('Unknown flag'), `Should report unknown flag. Got: ${combined}`);
|
||||
});
|
||||
|
||||
it('doctor --mode verify does not reject --mode as unknown', () => {
|
||||
const { stdout, stderr, status } = run(['doctor', '--mode', 'verify', '--cwd', 'src/cli/__fixtures__/tiny-fastify']);
|
||||
const combined = stdout + stderr;
|
||||
assert.notStrictEqual(status, 3, `Should not crash. Output: ${combined}`);
|
||||
assert(!combined.includes('Unknown flag: --mode'), `Should accept --mode flag. Output: ${combined}`);
|
||||
});
|
||||
|
||||
// For each of the 7 commands, verify they do NOT print "Not yet implemented"
|
||||
const commands = ['init', 'verify', 'observe', 'qualify', 'replay', 'doctor', 'migrate'];
|
||||
for (const cmd of commands) {
|
||||
it(`${cmd} does not print "Not yet implemented"`, () => {
|
||||
// Some commands may fail for config reasons; we just assert they don't say "Not yet implemented"
|
||||
const { stdout, stderr } = run([cmd]);
|
||||
const combined = stdout + stderr;
|
||||
assert(
|
||||
!combined.includes('Not yet implemented'),
|
||||
`Command ${cmd} appears to be a placeholder. Output: ${combined}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
it('npx apophis --help works via temp package.json bin reference', () => {
|
||||
const tmpDir = join(tmpdir(), `apophis-packaging-test-${Date.now()}`);
|
||||
mkdirSync(tmpDir, { recursive: true });
|
||||
|
||||
const pkg = {
|
||||
name: 'test-consumer',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'apophis-fastify': `file:${ROOT}`,
|
||||
},
|
||||
};
|
||||
writeFileSync(join(tmpDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
||||
|
||||
// We don't actually npm install; instead we verify the bin path resolves correctly
|
||||
// by checking the package.json bin field points to dist/cli/index.js
|
||||
const rootPkg = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8'));
|
||||
assert.strictEqual(rootPkg.bin.apophis, 'dist/cli/index.js', 'package.json bin must point to dist/cli/index.js');
|
||||
assert.strictEqual(rootPkg.main, 'dist/index.js', 'package.json main must point to dist/index.js');
|
||||
|
||||
// Verify the file exists at the resolved path
|
||||
const resolvedBin = join(ROOT, rootPkg.bin.apophis);
|
||||
assert(existsSync(resolvedBin), `Resolved bin path does not exist: ${resolvedBin}`);
|
||||
|
||||
// Clean up temp dir
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('npm pack produces a tarball with the bin entry', () => {
|
||||
const result = spawnSync('npm', ['pack', '--dry-run', '--json'], {
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
timeout: 30000,
|
||||
});
|
||||
assert.strictEqual(result.status, 0, `npm pack --dry-run failed: ${result.stderr}`);
|
||||
const packOutput = JSON.parse(result.stdout);
|
||||
const files = packOutput[0]?.files?.map((f: { path: string }) => f.path) || [];
|
||||
assert(files.includes('dist/cli/index.js'), 'Tarball must include dist/cli/index.js');
|
||||
});
|
||||
|
||||
it('npx apophis --help works in a temp project after npm install', () => {
|
||||
const tmpDir = join(tmpdir(), `apophis-npx-test-${Date.now()}`);
|
||||
mkdirSync(tmpDir, { recursive: true });
|
||||
|
||||
const pkg = {
|
||||
name: 'npx-test',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'apophis-fastify': `file:${ROOT}`,
|
||||
},
|
||||
};
|
||||
writeFileSync(join(tmpDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
||||
|
||||
const installResult = spawnSync('npm', ['install', '--silent'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 120000,
|
||||
});
|
||||
assert.strictEqual(installResult.status, 0, `npm install failed: ${installResult.stderr}`);
|
||||
|
||||
const helpResult = spawnSync('npx', ['apophis', '--help'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 30000,
|
||||
});
|
||||
assert.strictEqual(helpResult.status, 0, `npx apophis --help failed: ${helpResult.stderr}`);
|
||||
assert(helpResult.stdout.includes('apophis'), 'Help should mention apophis');
|
||||
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('npx apophis doctor works in a temp project after npm install', () => {
|
||||
const tmpDir = join(tmpdir(), `apophis-npx-test-${Date.now()}`);
|
||||
mkdirSync(tmpDir, { recursive: true });
|
||||
|
||||
const pkg = {
|
||||
name: 'npx-test',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'apophis-fastify': `file:${ROOT}`,
|
||||
},
|
||||
};
|
||||
writeFileSync(join(tmpDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
||||
|
||||
const installResult = spawnSync('npm', ['install', '--silent'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 120000,
|
||||
});
|
||||
assert.strictEqual(installResult.status, 0, `npm install failed: ${installResult.stderr}`);
|
||||
|
||||
const doctorResult = spawnSync('npx', ['apophis', 'doctor'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 30000,
|
||||
});
|
||||
// doctor exits non-zero when peer deps are missing in a bare temp project,
|
||||
// but it should still run and print the header
|
||||
assert(doctorResult.stdout.includes('APOPHIS Doctor'), `Doctor should run and print header. stdout: ${doctorResult.stdout} stderr: ${doctorResult.stderr}`);
|
||||
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('declares supported Node policy and default confidence test path', () => {
|
||||
const rootPkg = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8'));
|
||||
assert.strictEqual(rootPkg.engines.node, '^20.0.0 || ^22.0.0');
|
||||
assert.strictEqual(rootPkg.scripts.test, 'npm run build && npm run test:src && npm run test:cli');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user