import { describe, it, beforeEach } from 'node:test'; import assert from 'node:assert/strict'; import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFile } from 'node:child_process'; import { promisify } from 'node:util'; const exec = promisify(execFile); const __dirname = fileURLToPath(new URL('.', import.meta.url)); const FIXTURES = resolve(__dirname, '../fixtures'); const POSTURE_BIN = resolve(__dirname, '../../scanners/posture.mjs'); async function runPosture(args) { const { stdout, stderr } = await exec('node', [POSTURE_BIN, ...args], { timeout: 30000, cwd: resolve(__dirname, '../..'), }); return { stdout, stderr }; } async function runPostureJson(fixturePath) { const { stdout } = await runPosture([fixturePath, '--json']); return JSON.parse(stdout); } describe('posture.mjs CLI — healthy project', () => { let result; beforeEach(async () => { result = await runPostureJson(resolve(FIXTURES, 'healthy-project')); }); it('returns utilization with score and overhang', () => { assert.ok(typeof result.utilization.score === 'number'); assert.ok(typeof result.utilization.overhang === 'number'); assert.equal(result.utilization.score + result.utilization.overhang, 100); }); it('returns maturity level >= 2', () => { assert.ok(result.maturity.level >= 2); assert.ok(typeof result.maturity.name === 'string'); }); it('returns segment string', () => { assert.ok(typeof result.segment.segment === 'string'); assert.ok(result.segment.segment.length > 0); }); it('returns 9 area scores', () => { assert.equal(result.areas.length, 9); for (const area of result.areas) { assert.ok('id' in area); assert.ok('name' in area); assert.ok('grade' in area); assert.ok('score' in area); assert.ok('findingCount' in area); } }); it('exposes a token_efficiency area id', () => { const te = result.areas.find(a => a.id === 'token_efficiency'); assert.ok(te, 'token_efficiency id present'); }); it('returns overallGrade', () => { assert.ok(['A', 'B', 'C', 'D', 'F'].includes(result.overallGrade)); }); it('includes topActions array', () => { assert.ok(Array.isArray(result.topActions)); }); it('includes scannerEnvelope', () => { assert.ok(result.scannerEnvelope.meta); assert.ok(result.scannerEnvelope.scanners); assert.ok(result.scannerEnvelope.aggregate); }); }); describe('posture.mjs CLI — minimal project', () => { it('scores lower utilization than healthy', async () => { const healthy = await runPostureJson(resolve(FIXTURES, 'healthy-project')); const minimal = await runPostureJson(resolve(FIXTURES, 'minimal-project')); assert.ok(minimal.utilization.score < healthy.utilization.score, `minimal (${minimal.utilization.score}) should be < healthy (${healthy.utilization.score})`); }); it('has lower maturity than healthy', async () => { const healthy = await runPostureJson(resolve(FIXTURES, 'healthy-project')); const minimal = await runPostureJson(resolve(FIXTURES, 'minimal-project')); assert.ok(minimal.maturity.level <= healthy.maturity.level); }); }); describe('posture.mjs CLI — terminal output (v3 health format)', () => { it('scorecard contains health sections', async () => { const { stderr } = await runPosture([resolve(FIXTURES, 'healthy-project')]); assert.ok(stderr.includes('Config-Audit Health Score')); assert.ok(stderr.includes('Health:')); assert.ok(stderr.includes('Area Scores')); assert.ok(stderr.includes('areas scanned')); }); it('scorecard does NOT contain legacy metrics', async () => { const { stderr } = await runPosture([resolve(FIXTURES, 'healthy-project')]); assert.ok(!stderr.includes('Maturity:')); assert.ok(!stderr.includes('Utilization:')); assert.ok(!stderr.includes('Segment:')); }); it('scorecard excludes Feature Coverage from area display', async () => { const { stderr } = await runPosture([resolve(FIXTURES, 'healthy-project')]); assert.ok(!stderr.includes('Feature Coverage')); }); }); describe('posture.mjs CLI — JSON includes opportunityCount', () => { it('returns opportunityCount field', async () => { const result = await runPostureJson(resolve(FIXTURES, 'healthy-project')); assert.ok(typeof result.opportunityCount === 'number'); assert.ok(result.opportunityCount >= 0); }); it('JSON still includes legacy fields for backward compat', async () => { const result = await runPostureJson(resolve(FIXTURES, 'healthy-project')); assert.ok(typeof result.utilization.score === 'number'); assert.ok(typeof result.maturity.level === 'number'); assert.ok(typeof result.segment.segment === 'string'); }); });