test: add typed failure classification module (#333)
This commit is contained in:
34
scripts/test-failure-reasons.cjs
Normal file
34
scripts/test-failure-reasons.cjs
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const TEST_GATE_REASON = Object.freeze({
|
||||
PASS: 'pass',
|
||||
TEST_FAILURE: 'test_failure',
|
||||
INFRA_FAILURE: 'infra_failure',
|
||||
UNKNOWN_FAILURE: 'unknown_failure',
|
||||
});
|
||||
|
||||
/**
|
||||
* Classify gsd-test style output + exit code into a typed reason.
|
||||
* @param {{exitCode:number, output?:string}} input
|
||||
* @returns {{ok:boolean, reason:string}}
|
||||
*/
|
||||
function classifyTestGateResult(input) {
|
||||
const exitCode = Number(input?.exitCode ?? 0);
|
||||
const output = String(input?.output ?? '');
|
||||
|
||||
if (exitCode === 0) return { ok: true, reason: TEST_GATE_REASON.PASS };
|
||||
|
||||
// gsd-test/gsd-test-summary infrastructure class (exit 2)
|
||||
if (exitCode === 2 || /infrastructure failure|worktree\.Construct/i.test(output)) {
|
||||
return { ok: false, reason: TEST_GATE_REASON.INFRA_FAILURE };
|
||||
}
|
||||
|
||||
// test failures (non-zero plus explicit FAIL lines)
|
||||
if (/\bFAIL\b|\d+\s+failures?\)|failed\)/i.test(output)) {
|
||||
return { ok: false, reason: TEST_GATE_REASON.TEST_FAILURE };
|
||||
}
|
||||
|
||||
return { ok: false, reason: TEST_GATE_REASON.UNKNOWN_FAILURE };
|
||||
}
|
||||
|
||||
module.exports = { TEST_GATE_REASON, classifyTestGateResult };
|
||||
37
tests/test-failure-reasons.test.cjs
Normal file
37
tests/test-failure-reasons.test.cjs
Normal file
@@ -0,0 +1,37 @@
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { TEST_GATE_REASON, classifyTestGateResult } = require('../scripts/test-failure-reasons.cjs');
|
||||
|
||||
describe('test failure reason classification', () => {
|
||||
test('classifies pass on exitCode 0', () => {
|
||||
const out = classifyTestGateResult({ exitCode: 0, output: 'all good' });
|
||||
assert.deepStrictEqual(out, { ok: true, reason: TEST_GATE_REASON.PASS });
|
||||
});
|
||||
|
||||
test('classifies infrastructure failure via exitCode 2', () => {
|
||||
const out = classifyTestGateResult({ exitCode: 2, output: 'ERROR: infrastructure failure' });
|
||||
assert.deepStrictEqual(out, { ok: false, reason: TEST_GATE_REASON.INFRA_FAILURE });
|
||||
});
|
||||
|
||||
test('classifies infrastructure failure via known worktree construct signature', () => {
|
||||
const out = classifyTestGateResult({
|
||||
exitCode: 1,
|
||||
output: 'worktree.Construct: worktree construction failed at merge',
|
||||
});
|
||||
assert.deepStrictEqual(out, { ok: false, reason: TEST_GATE_REASON.INFRA_FAILURE });
|
||||
});
|
||||
|
||||
test('classifies test failure via FAIL markers', () => {
|
||||
const out = classifyTestGateResult({
|
||||
exitCode: 1,
|
||||
output: 'linux FAIL 11274/11277 tests (3 failures)',
|
||||
});
|
||||
assert.deepStrictEqual(out, { ok: false, reason: TEST_GATE_REASON.TEST_FAILURE });
|
||||
});
|
||||
|
||||
test('classifies unknown failure when non-zero without known signatures', () => {
|
||||
const out = classifyTestGateResult({ exitCode: 1, output: 'nonzero but unknown format' });
|
||||
assert.deepStrictEqual(out, { ok: false, reason: TEST_GATE_REASON.UNKNOWN_FAILURE });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user