| 495 | // Convenience for the common assertion pattern. Dumps stderr/stdout when |
| 496 | // the exit code doesn't match — saves debugging time on CI failures. |
| 497 | function expectExit(result: RunResult, expected: number, label = "opencode") { |
| 498 | if (result.exitCode === expected) return |
| 499 | const tail = (s: string, n: number) => (s.length > n ? "..." + s.slice(-n) : s) |
| 500 | // eslint-disable-next-line no-console |
| 501 | console.error(`[${label}] expected exit ${expected}, got ${result.exitCode} after ${result.durationMs}ms`) |
| 502 | // eslint-disable-next-line no-console |
| 503 | console.error(`[${label}] stderr (last 2000):\n${tail(result.stderr, 2000)}`) |
| 504 | // eslint-disable-next-line no-console |
| 505 | console.error(`[${label}] stdout (last 500):\n${tail(result.stdout, 500)}`) |
| 506 | throw new Error(`${label}: expected exit ${expected}, got ${result.exitCode}`) |
| 507 | } |
| 508 | |
| 509 | // `cliIt.live(name, fixture => effect)` is the same as |
| 510 | // `it.live(name, () => withCliFixture(fixture))` — one fewer nesting level at |