mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 00:44:07 +01:00
test(ship): the PR check precedes the Greptile dispatch, and each skip names its reason
Runs Step 10's generated check against stub gh for gh missing, not logged in, a non-GitHub remote, no PR, a failed lookup and an existing PR. Fails on the previous text (which printed PR: unknown and dispatched). Co-authored-by: dgrant <dgrant@users.noreply.github.com>
This commit is contained in:
1 parent
edd011185c
commit
195aa4eeda
1 file changed
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* /ship Step 10 (CEO-19, ENG-1): the PR check runs before the Greptile triage
|
||||
* dispatch, and every outcome except an existing PR skips the dispatch with
|
||||
* its reason. The check block is executed from the generated section against
|
||||
* stub `gh` binaries.
|
||||
*/
|
||||
import { afterAll, describe, expect, test } from 'bun:test';
|
||||
import { spawnSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const SECTION = fs.readFileSync(path.join(ROOT, 'ship/sections/greptile.md'), 'utf8');
|
||||
const CHECK = SECTION.match(/```bash\n([\s\S]*?)\n```/)![1];
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-ship-greptile-'));
|
||||
afterAll(() => fs.rmSync(tmp, { recursive: true, force: true }));
|
||||
|
||||
/** Run the check with a PATH holding only `head` and, unless null, a stub gh. */
|
||||
function check(gh: string | null): string {
|
||||
const bin = fs.mkdtempSync(path.join(tmp, 'bin-'));
|
||||
fs.symlinkSync(Bun.which('head')!, path.join(bin, 'head'));
|
||||
if (gh !== null) fs.writeFileSync(path.join(bin, 'gh'), `#!${Bun.which('bash')}\n${gh}\n`, { mode: 0o755 });
|
||||
const r = spawnSync(Bun.which('bash')!, ['-c', CHECK], { cwd: tmp, encoding: 'utf8', timeout: 10_000, env: { PATH: bin } });
|
||||
expect(r.status, r.stderr).toBe(0);
|
||||
return r.stdout.trim();
|
||||
}
|
||||
|
||||
describe('/ship Step 10 Greptile PR check', () => {
|
||||
test('the PR check precedes the dispatch, and only an existing PR dispatches', () => {
|
||||
const checkIdx = SECTION.indexOf('gh pr view --json number');
|
||||
const dispatchIdx = SECTION.indexOf('Dispatch a subagent through Agent');
|
||||
expect(checkIdx).toBeGreaterThan(-1);
|
||||
expect(dispatchIdx).toBeGreaterThan(checkIdx);
|
||||
const between = SECTION.slice(checkIdx, dispatchIdx);
|
||||
expect(between).toContain('Only `PR: exists` dispatches.');
|
||||
expect(between).toContain('"Greptile: not run (<reason>); runs on the PR once it exists"');
|
||||
expect(SECTION).not.toContain('PR: unknown');
|
||||
});
|
||||
|
||||
const ok = 'exit 0';
|
||||
const cases: Array<[string, string | null, string]> = [
|
||||
['gh missing', null, 'PR: skip (gh not installed)'],
|
||||
['gh not logged in', `[ "$1" = auth ] && { echo "You are not logged into any GitHub hosts." >&2; exit 1; }; ${ok}`, 'PR: skip (gh not logged in)'],
|
||||
['non-GitHub remote', `[ "$1" = repo ] && { echo "none of the git remotes configured for this repository point to a known GitHub host" >&2; exit 1; }; ${ok}`,
|
||||
'PR: skip (not a GitHub remote: none of the git remotes configured for this repository point to a known GitHub host)'],
|
||||
['no PR yet', `[ "$1" = pr ] && { echo 'no pull requests found for branch "feat"' >&2; exit 1; }; ${ok}`, 'PR: skip (no PR yet)'],
|
||||
['other lookup error', `[ "$1" = pr ] && { printf 'HTTP 502: bad gateway\\nretry later\\n' >&2; exit 1; }; ${ok}`, 'PR: skip (PR lookup failed: HTTP 502: bad gateway)'],
|
||||
['PR exists', `[ "$1" = pr ] && { echo 12; exit 0; }; ${ok}`, 'PR: exists'],
|
||||
];
|
||||
for (const [name, gh, expected] of cases) {
|
||||
test(name, () => expect(check(gh)).toBe(expected));
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user