mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
test: repair the CI reds the retarget onto main surfaced (test-only)
Seven test files, no product change. The pull_request run at 2d73fadd
failed 5 Linux shards and 4 Windows shards:
- test/ship-frozen-scope-actor.test.ts imported only test code, so the
test-of-test ratchet refused it; the first case now recomputes the loop
summary with summarizeReviewLoop/reviewLoopLine from lib/review-scope and
asserts the exact REVIEW_LOOP line. Its three cases replay the bash
workflow through bash -c and the shebang bins, so they are POSIX-only.
- test/codex-host.test.ts: the new gate case is Claude-specific (33 -> 34).
- test/paid-retry-supervision.test.ts: the gate census fixture now carries a
trial record for the one planned behavior-kind gate case, the way
test/ci-paid-coordination.test.ts already does.
- test/review-fix-range.test.ts asserted the pre-#3487 DIFF_BASE sentence;
it now asserts the ${RANGE_BASE:-merge-base} clause that carries the same
contract (every reviewer reads the range from pass 2 on).
- test/gstack-coauthor-trailers.test.ts: the fixture sets its own git
identity; a CI runner has none to auto-detect ("Committer identity
unknown").
- test/review-scope-followups.test.ts and test/gstack-ship-receipt-docs.test.ts:
the gh and old-git stand-ins are sh scripts on a colon-joined PATH, which
CreateProcess cannot run, so those three cases skip on win32.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
2d73fadd56
commit
f9e8f7b1e2
7 files changed
+23
-11
No files matched your search
@@ -128,13 +128,13 @@ describe('per-case host metadata', () => {
|
||||
for (const id of Object.keys(E2E_HOSTS)) expect(Object.hasOwn(E2E_TOUCHFILES, id), id).toBe(true);
|
||||
});
|
||||
|
||||
test('classifies 52 adapter-only, 33 Claude-specific, 26 rework and 1 host-neutral case', () => {
|
||||
test('classifies 52 adapter-only, 34 Claude-specific, 26 rework and 1 host-neutral case', () => {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const support of Object.values(E2E_HOSTS)) counts[support] = (counts[support] ?? 0) + 1;
|
||||
expect(counts.both).toBe(52);
|
||||
expect(counts.rework).toBe(26);
|
||||
expect(counts['host-neutral']).toBe(1);
|
||||
expect(['pty', 'sdk', 'subagents', 'skill-tool', 'hermetic', 'self-host'].reduce((sum, reason) => sum + (counts[reason] ?? 0), 0)).toBe(33);
|
||||
expect(['pty', 'sdk', 'subagents', 'skill-tool', 'hermetic', 'self-host'].reduce((sum, reason) => sum + (counts[reason] ?? 0), 0)).toBe(34);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ import { EXIT } from '../lib/headless-artifacts';
|
||||
const BIN = path.resolve(import.meta.dir, '../bin/gstack-coauthor-trailers');
|
||||
const temps: string[] = [];
|
||||
afterAll(() => { for (const dir of temps) fs.rmSync(dir, { recursive: true, force: true }); });
|
||||
const ENV = { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null', GIT_CONFIG_NOSYSTEM: '1' };
|
||||
const ENV = {
|
||||
...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null', GIT_CONFIG_NOSYSTEM: '1',
|
||||
GIT_COMMITTER_NAME: 'Porter', GIT_COMMITTER_EMAIL: 'porter@example.invalid', GIT_AUTHOR_NAME: 'Porter', GIT_AUTHOR_EMAIL: 'porter@example.invalid',
|
||||
};
|
||||
|
||||
function git(cwd: string, args: string[], input?: string) {
|
||||
const r = spawnSync('git', args, { cwd, env: ENV, encoding: 'utf8', timeout: 30_000, input });
|
||||
|
||||
@@ -170,7 +170,8 @@ describe('carry follow-ups: git floor, named refusal, branch-touched walk, digit
|
||||
expect((plan as any).message.split(', ').length).toBe(CARRY_NAMED_PATHS);
|
||||
});
|
||||
|
||||
test('carry refuses git below 2.39 by name (RECEIPT_GIT_TOO_OLD) and names a product path the branch touched', () => {
|
||||
// The old-git stand-in is a sh script on a colon-joined PATH, which CreateProcess cannot run on Windows.
|
||||
test.skipIf(process.platform === 'win32')('carry refuses git below 2.39 by name (RECEIPT_GIT_TOO_OLD) and names a product path the branch touched', () => {
|
||||
const repo = fixture();
|
||||
makePr(repo, 'pr-carry', { expects: '1.2.4.0' });
|
||||
const body = path.join(repo.root, 'body.md');
|
||||
|
||||
@@ -4,7 +4,7 @@ import { join } from 'node:path';
|
||||
import {
|
||||
buildPaidShardArgs, buildRunManifest, parseRunManifest, planPaidShards,
|
||||
DEFAULT_JOBS, parseCliOptions, paidShardWallUpperBoundMs, resolvePaidShardBudget, retriesForFiles, verifySliceResults, collectPaidTestFiles, selectPaidTestFiles,
|
||||
shardFile, sliceExecutionOrder, sliceSupervisedWallMs, CASE_SHARDED_FILES, sliceCiTimeoutMinutes, resolvePaidShardTimeoutMs,
|
||||
shardFile, shardCaseId, shardTrial, sliceExecutionOrder, sliceSupervisedWallMs, CASE_SHARDED_FILES, sliceCiTimeoutMinutes, resolvePaidShardTimeoutMs,
|
||||
} from '../scripts/test-paid-shards';
|
||||
import {
|
||||
ALL_TIERS, AUQ_CONSISTENCY_RETRY_BUDGET, FILE_RETRY_BUDGETS,
|
||||
@@ -331,6 +331,7 @@ test('gate census requires all seven distinct slice results and its own reconcil
|
||||
files: [row.file], status: 'passed' as const, exitCode: 0, elapsedMs: 1, skippedTests: 0,
|
||||
executedTests: STRICT_RETRY_CASE_BUDGETS.find(budget => budget.file === row.file)?.cases ?? 1,
|
||||
...(row.budget ? { budget: row.budget } : {}),
|
||||
...(row.trial ? { trial: { case: shardCaseId(row.file)!, trial: shardTrial(row.file)!, ...row.trial, outcome: 'passed' as const, cost_usd: 0, duration_ms: 1 } } : {}),
|
||||
})),
|
||||
}));
|
||||
expect(verifySliceResults(manifest, results)).toEqual({ ok: true, problems: [] });
|
||||
|
||||
@@ -211,7 +211,7 @@ describe('ship Step 9 instructions', () => {
|
||||
const army = fs.readFileSync(path.join(ROOT, 'ship/sections/review-army.md'), 'utf8').replace(/\s+/g, ' ');
|
||||
test('pass 2+ reads the range since the previous pass under a full token; the fix-range token is /review\'s alone', () => {
|
||||
expect(army).toContain('Every entry saves `PASS_HEAD=$(git rev-parse HEAD)`; from pass 2 on, `RANGE_BASE` is the previous pass\'s PASS_HEAD.');
|
||||
expect(army).toContain('use `DIFF_BASE=$RANGE_BASE` instead, so every reviewer reads what changed since the previous pass.');
|
||||
expect(army).toContain('`DIFF_BASE=${RANGE_BASE:-$(git merge-base origin/<base> HEAD)}`, so with `RANGE_BASE` exported from pass 2 on every reviewer reads what changed since the previous pass');
|
||||
expect(army).toContain('Every pass captures a full token (no `--fix-range`), so the record binds the whole tree even when reviewers read only the range.');
|
||||
expect(army).not.toContain('--start review --fix-range');
|
||||
expect(army).toContain('gstack-review-scope classify --base <base> --pass $PASS --range-base $RANGE_BASE');
|
||||
|
||||
@@ -54,7 +54,8 @@ describe('deferred-issue search page and the repository argument gh accepts', ()
|
||||
return { dir, log };
|
||||
};
|
||||
|
||||
test('an exactly-full search page is reported truncated and recorded on the issue it files; a short page is not', () => {
|
||||
// The gh stand-in is a sh script on a colon-joined PATH, which CreateProcess cannot run on Windows.
|
||||
test.skipIf(process.platform === 'win32')('an exactly-full search page is reported truncated and recorded on the issue it files; a short page is not', () => {
|
||||
const repo = fixture();
|
||||
const classified = path.join(repo.root, 'classified.json');
|
||||
fs.writeFileSync(classified, JSON.stringify([finding('lib/legacy.ts:7:data-loss', 'Unbounded delete')]));
|
||||
@@ -75,7 +76,7 @@ describe('deferred-issue search page and the repository argument gh accepts', ()
|
||||
expect(JSON.parse(r2.out)[0]).toEqual({ fingerprint: 'lib/legacy.ts:7:data-loss', path: 'lib/legacy.ts:7', severity: 'CRITICAL', summary: 'Unbounded delete', issue: 900, url: 'https://example.test/acme/app/issues/900' });
|
||||
});
|
||||
|
||||
test('with --repo the visibility read passes the repository positionally, so a MEDIUM finding on a public repository waits for a human and stays unfiled', () => {
|
||||
test.skipIf(process.platform === 'win32')('with --repo the visibility read passes the repository positionally, so a MEDIUM finding on a public repository waits for a human and stays unfiled', () => {
|
||||
const repo = fixture();
|
||||
const classified = path.join(repo.root, 'classified.json');
|
||||
fs.writeFileSync(classified, JSON.stringify([
|
||||
|
||||
@@ -11,6 +11,7 @@ import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
createShipFrozenScopeFixture, FROZEN_SCOPE_DEFER_IDENTITY, FROZEN_SCOPE_FIX_IDENTITY, shipFrozenScopeFailures, shipFrozenScopeWorkflow,
|
||||
} from './helpers/ship-frozen-scope-actor';
|
||||
import { reviewLoopLine, summarizeReviewLoop } from '../lib/review-scope';
|
||||
|
||||
type Fixture = ReturnType<typeof createShipFrozenScopeFixture>;
|
||||
let workflow = '';
|
||||
@@ -78,7 +79,9 @@ async function act(f: Fixture, options: { fix?: boolean; passes?: 1 | 2 } = {})
|
||||
sh(f, f.commands.continue!);
|
||||
}
|
||||
|
||||
test('a scripted actor that fixes the diff, defers the pre-existing defect and converges on pass 2 passes the verdict', async () => {
|
||||
// The actor replays the bash workflow through bash -c and the shebang bins, so it is POSIX-only like the paid case it mirrors.
|
||||
const posixTest = test.skipIf(process.platform === 'win32');
|
||||
posixTest('a scripted actor that fixes the diff, defers the pre-existing defect and converges on pass 2 passes the verdict', async () => {
|
||||
const f = fixture();
|
||||
await act(f);
|
||||
const verdict = await shipFrozenScopeFailures(f, { exitReason: 'success' });
|
||||
@@ -89,9 +92,12 @@ test('a scripted actor that fixes the diff, defers the pre-existing defect and c
|
||||
expect(rows[0]!.findings.map((x: any) => [x.fingerprint, x.action])).toEqual([[FROZEN_SCOPE_FIX_IDENTITY, 'auto-fixed'], [FROZEN_SCOPE_DEFER_IDENTITY, 'deferred']]);
|
||||
expect(rows[1]!.range_base).toBe(verdict.evidence.head);
|
||||
expect(verdict.evidence.payments).toBe(verdict.evidence.originalPayments);
|
||||
const summary = summarizeReviewLoop(rows, verdict.evidence.start.started_at);
|
||||
expect(summary).toEqual(verdict.evidence.loop);
|
||||
expect(reviewLoopLine(summary)).toBe('REVIEW_LOOP: passes=2 blocking_found=2 deferred=1 noted=0 fixed=1 scope=frozen');
|
||||
}, 120_000);
|
||||
|
||||
test('the fixture interface refuses the undeclared interactions before any decision', async () => {
|
||||
posixTest('the fixture interface refuses the undeclared interactions before any decision', async () => {
|
||||
const f = fixture();
|
||||
expect(await hook(f, 'Edit', { file_path: f.payments, old_string: 'card', new_string: '?' })).toBe('Edit outside invoice.ts');
|
||||
expect(await hook(f, 'Write', { file_path: f.payments, content: '' })).toBe('Write outside invoice.ts and the review record');
|
||||
@@ -104,7 +110,7 @@ test('the fixture interface refuses the undeclared interactions before any decis
|
||||
expect(f.snapshot().executions.filter(e => !e.allowed)).toHaveLength(6);
|
||||
}, 120_000);
|
||||
|
||||
test('leaving the diff defect in place or exiting after one pass are named failures', async () => {
|
||||
posixTest('leaving the diff defect in place or exiting after one pass are named failures', async () => {
|
||||
const f = fixture();
|
||||
await act(f, { fix: false, passes: 1 });
|
||||
const verdict = await shipFrozenScopeFailures(f, { exitReason: 'success' });
|
||||
|
||||
Reference in new issue
Block a user