test(lib): extract shared mini test runner for coordination tests (#2663)

Address CodeRabbit review on #2311: dedupe the local test(name, fn)
harness and route all reporter output through a shared helper
(tests/lib/helpers/mini-test-runner.js) instead of direct console.log.
This commit is contained in:
Alexis D.
2026-08-04 00:29:31 -04:00
committed by GitHub
parent 203aac7710
commit ff15079b9f
4 changed files with 93 additions and 82 deletions
+13 -24
View File
@@ -25,24 +25,14 @@ const {
verifyDependenciesClosed,
} = require('../../scripts/lib/github-coordination/state');
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (err) {
console.log(`${name}`);
console.log(` Error: ${err.message}`);
return false;
}
}
const { test, banner, section, summary } = require('./helpers/mini-test-runner');
let passed = 0;
let failed = 0;
console.log('\n=== parsing.js — uncovered branches ===\n');
banner('parsing.js — uncovered branches');
console.log('normalizeBodyForComparison:');
section('normalizeBodyForComparison:');
if (test('handles null body (uses empty string fallback)', () => {
const result = normalizeBodyForComparison(null);
@@ -61,7 +51,7 @@ if (test('normalizes lastSyncAt timestamps in body text', () => {
assert.ok(!result.includes('2024-01-01'));
})) passed++; else failed++;
console.log('\nparseStringList:');
section('parseStringList:');
if (test('returns empty array for null', () => {
assert.deepStrictEqual(parseStringList(null), []);
@@ -83,7 +73,7 @@ if (test('filters out empty parts from double-commas', () => {
assert.deepStrictEqual(parseStringList('a,,b'), ['a', 'b']);
})) passed++; else failed++;
console.log('\nmergeIssueBody — empty body branch:');
section('mergeIssueBody — empty body branch:');
if (test('returns rendered state when issue body is empty string', () => {
const state = { status: 'available', schemaVersion: 'v1', kind: 'epic', owner: null, branch: null, validation: 'pending', review: 'not-requested', project: { state: 'backlog', fields: {} }, dependencies: [], tasks: [], labels: [], lastAction: 'sync' };
@@ -97,9 +87,9 @@ if (test('returns rendered state when issue body is null', () => {
assert.ok(result.includes('ecc-coordination:start'));
})) passed++; else failed++;
console.log('\n=== state.js — uncovered branches ===\n');
banner('state.js — uncovered branches');
console.log('buildIssueStateFromAction — options absent (false branches):');
section('buildIssueStateFromAction — options absent (false branches):');
const baseIssue = { number: 1, labels: [], body: '' };
const baseState = {
@@ -133,7 +123,7 @@ if (test('buildIssueStateFromAction — currentState.tasks not array → re-extr
assert.ok(Array.isArray(result.tasks));
})) passed++; else failed++;
console.log('\ndesiredLabelsForState — uncovered status/review/validation branches:');
section('desiredLabelsForState — uncovered status/review/validation branches:');
if (test('includes published label for status "published"', () => {
const labels = desiredLabelsForState({ status: 'published' });
@@ -160,7 +150,7 @@ if (test('includes review-changes-requested label for review "changes-requested"
assert.ok(labels.includes('coordination:review-changes-requested'));
})) passed++; else failed++;
console.log('\nmapStateToWorkItemStatus — uncovered switch cases:');
section('mapStateToWorkItemStatus — uncovered switch cases:');
if (test('"validated" → "in-progress"', () => {
assert.strictEqual(mapStateToWorkItemStatus('validated'), 'in-progress');
@@ -182,7 +172,7 @@ if (test('"unknown-state" → "open" (default)', () => {
assert.strictEqual(mapStateToWorkItemStatus('unknown-state'), 'open');
})) passed++; else failed++;
console.log('\nassertIssueClaimable:');
section('assertIssueClaimable:');
if (test('throws when issue is not open', () => {
assert.throws(
@@ -204,7 +194,7 @@ if (test('does not throw for open, unclaimed issue', () => {
});
})) passed++; else failed++;
console.log('\nverifyDependenciesClosed:');
section('verifyDependenciesClosed:');
if (test('returns empty array when dependencyNumbers is not an array', () => {
const result = verifyDependenciesClosed('r/r', null, {}, []);
@@ -240,7 +230,7 @@ if (test('warns via stderr and skips when dependency issue is not in allIssues l
assert.ok(stderrOutput.includes('dependency issue #5 not found'), `expected stderr warning, got: ${stderrOutput}`);
})) passed++; else failed++;
console.log('\ndefaultCoordinationState — edge branches:');
section('defaultCoordinationState — edge branches:');
if (test('owner is null when issue has no author', () => {
const result = defaultCoordinationState({ number: 1, labels: [] });
@@ -264,5 +254,4 @@ if (test('handles null issue', () => {
assert.deepStrictEqual(result.tasks, []);
})) passed++; else failed++;
console.log(`\n Results: ${passed} passed, ${failed} failed`);
if (failed > 0) process.exit(1);
summary(passed, failed);
+14 -25
View File
@@ -19,17 +19,7 @@ const {
DEFAULT_SECTION_MARKER,
} = require('../../scripts/lib/github-coordination/policy');
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (err) {
console.log(`${name}`);
console.log(` Error: ${err.message}`);
return false;
}
}
const { test, banner, section, summary } = require('./helpers/mini-test-runner');
function withTempDir(fn) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-policy-test-'));
@@ -51,9 +41,9 @@ function writeConfig(tmpDir, content) {
let passed = 0;
let failed = 0;
console.log('\n=== Testing github-coordination/policy.js ===\n');
banner('Testing github-coordination/policy.js');
console.log('loadPolicy — no config file:');
section('loadPolicy — no config file:');
if (test('returns default policy when no config file exists in rootDir', () => {
withTempDir(tmpDir => {
@@ -74,7 +64,7 @@ if (test('returns default policy when custom configPath does not exist', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — configPath argument:');
section('loadPolicy — configPath argument:');
if (test('uses configPath when explicitly provided', () => {
withTempDir(tmpDir => {
@@ -95,7 +85,7 @@ if (test('falls back to rootDir config file when configPath is null', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — invalid JSON:');
section('loadPolicy — invalid JSON:');
if (test('throws on invalid JSON', () => {
withTempDir(tmpDir => {
@@ -104,7 +94,7 @@ if (test('throws on invalid JSON', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — non-object JSON:');
section('loadPolicy — non-object JSON:');
if (test('throws when top-level JSON is null', () => {
withTempDir(tmpDir => {
@@ -127,7 +117,7 @@ if (test('throws when top-level JSON is a string', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — labels merging:');
section('loadPolicy — labels merging:');
if (test('merges labels when parsed.labels is a plain object', () => {
withTempDir(tmpDir => {
@@ -162,7 +152,7 @@ if (test('falls back to empty labels when parsed.labels is a string', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — review merging:');
section('loadPolicy — review merging:');
if (test('merges review when parsed.review is a plain object', () => {
withTempDir(tmpDir => {
@@ -197,7 +187,7 @@ if (test('falls back when parsed.review is an array', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — validation merging:');
section('loadPolicy — validation merging:');
if (test('merges validation when parsed.validation is a plain object', () => {
withTempDir(tmpDir => {
@@ -215,7 +205,7 @@ if (test('falls back when parsed.validation is not an object', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — branchModel merging:');
section('loadPolicy — branchModel merging:');
if (test('merges branchModel when parsed.branchModel is a plain object', () => {
withTempDir(tmpDir => {
@@ -234,7 +224,7 @@ if (test('falls back when parsed.branchModel is not an object', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — project merging:');
section('loadPolicy — project merging:');
if (test('merges project when parsed.project is a plain object', () => {
withTempDir(tmpDir => {
@@ -261,7 +251,7 @@ if (test('falls back when parsed.project is null', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — project.fieldNames merging:');
section('loadPolicy — project.fieldNames merging:');
if (test('merges fieldNames when project.fieldNames is a plain object', () => {
withTempDir(tmpDir => {
@@ -296,7 +286,7 @@ if (test('falls back when project.fieldNames is an array', () => {
});
})) passed++; else failed++;
console.log('\nloadPolicy — sourcePath:');
section('loadPolicy — sourcePath:');
if (test('sets sourcePath to the resolved config file path', () => {
withTempDir(tmpDir => {
@@ -306,5 +296,4 @@ if (test('sets sourcePath to the resolved config file path', () => {
});
})) passed++; else failed++;
console.log(`\n Results: ${passed} passed, ${failed} failed`);
if (failed > 0) process.exit(1);
summary(passed, failed);
+10 -33
View File
@@ -16,17 +16,7 @@ const {
const { DEFAULT_SCHEMA_VERSION, DEFAULT_POLICY } = require('../../scripts/lib/github-coordination/policy');
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (err) {
console.log(`${name}`);
console.log(` Error: ${err.message}`);
return false;
}
}
const { test, testAsync, banner, section, summary, fatal } = require('./helpers/mini-test-runner');
function makeStore() {
const calls = [];
@@ -42,15 +32,15 @@ function makeStore() {
let passed = 0;
let failed = 0;
console.log('\n=== Testing github-coordination/store.js ===\n');
banner('Testing github-coordination/store.js');
console.log('epicWorkItemId:');
section('epicWorkItemId:');
if (test('produces a stable ID from repo and issue number', () => {
assert.strictEqual(epicWorkItemId('acme/my-repo', 42), 'github-acme-my-repo-epic-42');
})) passed++; else failed++;
console.log('\nupsertCoordinationWorkItem — null store:');
section('upsertCoordinationWorkItem — null store:');
if (test('returns null when store is null', () => {
const result = upsertCoordinationWorkItem(null, 'r/r', { number: 1 }, {}, 'sync');
@@ -62,7 +52,7 @@ if (test('returns null when store is undefined', () => {
assert.strictEqual(result, null);
})) passed++; else failed++;
console.log('\nupsertCoordinationWorkItem — with store:');
section('upsertCoordinationWorkItem — with store:');
if (test('passes schemaVersion from state when present', () => {
const store = makeStore();
@@ -179,30 +169,17 @@ if (test('sets sessionId to null when options.sessionId absent', () => {
assert.strictEqual(store.calls[0].sessionId, null);
})) passed++; else failed++;
console.log('\nopenStore — dbPath: false:');
section('openStore — dbPath: false:');
async function runAsyncTests() {
let asyncPassed = 0;
let asyncFailed = 0;
try {
if (await testAsync('returns null when dbPath is false', async () => {
const result = await openStore({ dbPath: false });
assert.strictEqual(result, null);
console.log(' ✓ returns null when dbPath is false');
asyncPassed++;
} catch (err) {
console.log(' ✗ returns null when dbPath is false');
console.log(` Error: ${err.message}`);
asyncFailed++;
}
})) passed++; else failed++;
const totalPassed = passed + asyncPassed;
const totalFailed = failed + asyncFailed;
console.log(`\n Results: ${totalPassed} passed, ${totalFailed} failed`);
if (totalFailed > 0) process.exit(1);
summary(passed, failed);
}
runAsyncTests().catch(err => {
console.error(`Unexpected async test failure: ${err.message}`);
process.exit(1);
fatal(`Unexpected async test failure: ${err.message}`);
});
+56
View File
@@ -0,0 +1,56 @@
/**
* Shared mini test harness for standalone tests/lib/*.test.js scripts.
*
* Centralizes test execution and console reporting so individual test
* files don't duplicate the runner or log directly.
*/
'use strict';
function report(message) {
console.log(message);
}
function test(name, fn) {
try {
fn();
report(`${name}`);
return true;
} catch (err) {
report(`${name}`);
report(` Error: ${err.message}`);
return false;
}
}
async function testAsync(name, fn) {
try {
await fn();
report(`${name}`);
return true;
} catch (err) {
report(`${name}`);
report(` Error: ${err.message}`);
return false;
}
}
function banner(title) {
report(`\n=== ${title} ===`);
}
function section(label) {
report(`\n${label}`);
}
function summary(passed, failed) {
report(`\n Results: ${passed} passed, ${failed} failed`);
if (failed > 0) process.exit(1);
}
function fatal(message) {
console.error(message);
process.exit(1);
}
module.exports = { test, testAsync, banner, section, summary, fatal };