test: base-red coverage pass 2 (write --base-red, summary line, classify usage before fetch, unresolved-head detail)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-29 17:09:12 +00:00
1 parent 74600b8d1a
commit 9d7f7aba81
1 file changed
+54 -1
+54 -1
View File
@@ -14,7 +14,7 @@ import * as os from 'node:os';
import * as path from 'node:path'; import * as path from 'node:path';
import { classifyBaseRed, parseEvidence, pausePath, readPause, receiptBaseRed, shellRunner, waitBaseGreen, writePause, type FailureEvidence, type Runner } from '../lib/base-red'; import { classifyBaseRed, parseEvidence, pausePath, readPause, receiptBaseRed, shellRunner, waitBaseGreen, writePause, type FailureEvidence, type Runner } from '../lib/base-red';
import { EXIT } from '../lib/headless-artifacts'; import { EXIT } from '../lib/headless-artifacts';
import { bindReceipt, extractReceipt, placeReceiptFirst } from '../lib/ship-receipt'; import { bindReceipt, extractReceipt, placeReceiptFirst, receiptSummaryLine } from '../lib/ship-receipt';
import { BASE_RED_KEY_VERSION, validatePolicy, type BaseRedPolicy } from '../lib/ship-policy'; import { BASE_RED_KEY_VERSION, validatePolicy, type BaseRedPolicy } from '../lib/ship-policy';
const ROOT = path.resolve(import.meta.dir, '..'); const ROOT = path.resolve(import.meta.dir, '..');
@@ -210,6 +210,7 @@ describe('coverage audit additions', () => {
const a = counting(); const a = counting();
const r = classifyBaseRed({ repoRoot: work, base: 'main', head: 'no-such-ref', evidence: evidence(), policy: POLICY, runner: a.runner }); const r = classifyBaseRed({ repoRoot: work, base: 'main', head: 'no-such-ref', evidence: evidence(), policy: POLICY, runner: a.runner });
expect([r.code, r.cases.every(c => c.verdict === 'IN_BRANCH'), a.calls]).toEqual(['BASE_RED_REPRO_ERROR', true, []]); expect([r.code, r.cases.every(c => c.verdict === 'IN_BRANCH'), a.calls]).toEqual(['BASE_RED_REPRO_ERROR', true, []]);
expect([r.detail, r.cases[0]!.reason]).toEqual([`cannot resolve origin/main or no-such-ref`, 'unresolved commit']);
}); });
// Generated by /ship Step 7 coverage audit (PR #3611). // Generated by /ship Step 7 coverage audit (PR #3611).
@@ -228,4 +229,56 @@ describe('coverage audit additions', () => {
expect(w.status).toBe(EXIT.refused); expect(w.status).toBe(EXIT.refused);
expect(w.stderr).toContain('BASE_RED_UNCONFIGURED'); expect(w.stderr).toContain('BASE_RED_UNCONFIGURED');
}); });
// Generated by /ship Step 7 coverage audit (PR #3611), pass 2.
// Value: protects=write --base-red adds the base_red block from a valid pause, and an unreadable or non-pause file exits usage with nothing written;
// fails_when=the flag is ignored, so an unconditional receipt is written for a paused run; why_new=no test runs write with --base-red;
// seam=none; no_claim=PR body upload via gh (local body file only)
test('write --base-red makes the receipt conditional, and a bad pause file writes nothing', () => {
fixture();
const ev = evidence();
const { file, record } = writePause(work, 'feat', classifyBaseRed({ repoRoot: work, base: 'main', head: 'HEAD', evidence: ev, policy: POLICY }), ev);
const body = path.join(root, 'body.md');
const run = (pause: string) => spawnSync('bun', [path.join(ROOT, 'bin', 'gstack-ship-receipt'), 'write', '--pr', '7', '--base', 'main', '--session-kind', 'x', '--body', body, '--base-red', pause], { cwd: work, encoding: 'utf8', timeout: 120_000, env: { ...process.env, ...ID, GSTACK_STATE_ROOT: state } });
for (const bad of [path.join(root, 'missing.json'), path.join(root, 'ev.json')]) {
fs.writeFileSync(path.join(root, 'ev.json'), JSON.stringify(ev));
fs.writeFileSync(body, 'prose\n');
const r = run(bad);
expect([r.status, r.stderr.includes('--base-red: '), r.stderr.includes('nothing written'), fs.readFileSync(body, 'utf8')]).toEqual([EXIT.usage, true, true, 'prose\n']);
}
const ok = run(file);
expect(ok.status).toBe(EXIT.ok);
const head = git('rev-parse', 'HEAD');
const written = fs.readFileSync(body, 'utf8');
expect(extractReceipt(written).receipt?.base_red).toEqual(receiptBaseRed(record));
const bound = bindReceipt(written, '7', [head]);
expect(bound.ok ? 'bound' : bound.code).toBe('QUEUE_RECEIPT_CONDITIONAL');
});
// Generated by /ship Step 7 coverage audit (PR #3611), pass 2.
// Value: protects=the SHIP_RECEIPT summary line names base_red=<base12> for a conditional receipt and omits it otherwise; fails_when=the base_red part is dropped or printed unconditionally;
// why_new=summary-line tests cover carried_from only; seam=none; no_claim=none beyond protects
test('the summary line names the red base only for a conditional receipt', () => {
const base = 'c'.repeat(40);
const plain = { schema_version: 1, head: 'a'.repeat(40), base: 'main' } as any;
expect(receiptSummaryLine({ ...plain, base_red: { cases: ['x'], files: ['t/x.sh'], base, lane: 'tests', expires_at: '2026-10-30T00:00:00Z' } })).toContain(` base_red=${base.slice(0, 12)}`);
expect(receiptSummaryLine(plain)).not.toContain('base_red=');
});
// Generated by /ship Step 7 coverage audit (PR #3611), pass 2.
// Value: protects=classify exits usage on a missing --failures, an unreadable file or invalid evidence before any git fetch; fails_when=evidence checks move after the policy fetch or stop refusing;
// why_new=parseEvidence is unit-tested only, never through the CLI; seam=none; no_claim=none beyond protects (GIT_TRACE proves no fetch ran)
test('classify refuses bad evidence with a usage exit before fetching anything', () => {
git('commit', '-q', '--allow-empty', '-m', 'init');
const trace = path.join(root, 'git-trace.log');
const bad = path.join(root, 'bad.json');
fs.writeFileSync(bad, JSON.stringify({ lane: 'tests', total: 1, failures: [{ id: 'x', file: '/abs.sh', signature: 's' }] }));
const cases: Array<[string[], string]> = [[[], '--failures <evidence.json> is required'], [['--failures', path.join(root, 'missing.json')], 'cannot read '], [['--failures', bad], 'must be repo-relative']];
for (const [args, why] of cases) {
const r = spawnSync('bun', [path.join(ROOT, 'bin', 'gstack-base-red'), 'classify', ...args], { cwd: work, encoding: 'utf8', timeout: 60_000, env: { ...process.env, ...ID, GSTACK_STATE_ROOT: state, GIT_TRACE: trace } });
expect([r.status, r.stderr.includes(why)]).toEqual([EXIT.usage, true]);
}
const traced = fs.readFileSync(trace, 'utf8');
expect([traced.includes('rev-parse'), traced.includes('fetch')]).toEqual([true, false]);
});
}); });