# burgee/testing

> Every export of burgee/testing, with its signature and doc comment: processRuntime, captureConsole, codeOf, fakeClock, fakeRuntime, finish and 7 more, plus 7 types.

Source: https://burgee.interlace.tools/docs/api/testing

<!-- Generated by scripts/api-reference.ts from the built dist/*.d.ts. Do not edit; run `npx tsx scripts/api-reference.ts`. -->

`burgee/testing` — run a burgee CLI in-process, with argv, env, stdin, cwd and
TTY-ness injected, and get back `{ code, stdout, stderr, json }`. Requirement T1.

A separate entry point so it is paid for per import (K6): a user's shipped CLI
imports `burgee` and never pulls a byte of this.

```ts
import { processRuntime, captureConsole, codeOf, … } from 'burgee/testing';
```

## Functions

### captureConsole

Route `console.*` into the fake runtime while a run lasts. Hosts print through
`console` in a few paths a public seam does not cover (yargs' help printer, a
handler that never met the output layer); a harness that let those reach the real
terminal would be measuring the wrong thing.

```ts
function captureConsole(rt: FakeRuntime): () => void;
```

| Parameter | Type |
| :-- | :-- |
| `rt` | `FakeRuntime` |

**Returns** `() => void`

### codeOf

The E1 code an unwound error carries, or `RUNTIME` when it carries none.

```ts
function codeOf(e: unknown): ExitCode;
```

| Parameter | Type |
| :-- | :-- |
| `e` | `unknown` |

**Returns** `ExitCode`

### fakeClock

Deterministic time for the harness. Starts at `start` (0 by default) and never moves on its own.

```ts
function fakeClock(start?: number): FakeClock;
```

| Parameter | Type |
| :-- | :-- |
| `start` (optional) | `number` |

**Returns** `FakeClock`

### fakeRuntime

A `Runtime` whose every part is under the test's control.

```ts
function fakeRuntime(opts: RunOptions): FakeRuntime;
```

| Parameter | Type |
| :-- | :-- |
| `opts` | `RunOptions` |

**Returns** `FakeRuntime`

### finish

Assemble the result (R2). `json` is set only when `--json` was in argv and stdout
parsed; a parse failure turns the run into `RUNTIME` with the parse error on
stderr, so a broken envelope cannot pass a test by accident.

```ts
function finish(rt: FakeRuntime, code: ExitCode, startedAt: number): RunResult;
```

| Parameter | Type |
| :-- | :-- |
| `rt` | `FakeRuntime` |
| `code` | `ExitCode` |
| `startedAt` | `number` |

**Returns** `RunResult`

### isExitCode

True for the seven codes in the contract and nothing else.

```ts
function isExitCode(n: unknown): n is ExitCode;
```

| Parameter | Type |
| :-- | :-- |
| `n` | `unknown` |

**Returns** `n is ExitCode`

### runBurgee

Run a burgee program in-process — the T1 harness for burgee itself. Env is injected,
never swapped: the engine reads env-bound options from the runtime it is given, so
`process.env` is untouched by construction. Exit unwinds through `RuntimeExit`.

```ts
function runBurgee(program: Manifest, opts: RunOptions): Promise<RunResult>;
```

| Parameter | Type |
| :-- | :-- |
| `program` | `Manifest` |
| `opts` | `RunOptions` |

**Returns** `Promise<RunResult>`

### stripAnsi

Strip ANSI escape sequences — the decision from the intent: `stdout` stays raw.

linegauge's `strip`, not a regex of this file's own. The one this used to carry,
`ESC[[0-9;]*[A-Za-z]`, left the private modes (`ESC[?25l`, which every spinner writes),
the colon form of an extended colour (`ESC[38:2::255:0:0m`, which chalk emits for
truecolor) and every OSC 8 hyperlink in the text a test asserted against.

```ts
function stripAnsi(text: string): string;
```

| Parameter | Type |
| :-- | :-- |
| `text` | `string` |

**Returns** `string`

### swapEnv

```ts
function swapEnv(env: Record<string, string> | undefined): () => void;
```

| Parameter | Type |
| :-- | :-- |
| `env` | `Record<string, string> \| undefined` |

**Returns** `() => void`

## Classes

### RuntimeExit

Thrown by `fakeRuntime.exit` so a handler that exits unwinds to the harness.

```ts
class RuntimeExit extends Error {
    readonly code: ExitCode;
    constructor(code: ExitCode);
}
```

## Constants

### ExitCode

E1 — exit codes are a contract. No other literal may reach `process.exitCode`.

```ts
const ExitCode: {
    /** Command completed. */
    readonly OK: 0;
    /** The command ran and failed. Never accompanied by help text (E2). */
    readonly RUNTIME: 1;
    /** Bad arguments, unknown command, missing flag, prompt needed in a non-TTY (P2). */
    readonly USAGE: 2;
    /** Config file or environment could not be loaded or validated (V1). */
    readonly CONFIG: 3;
    /** The user or caller cancelled. */
    readonly CANCELLED: 4;
    /**
     * E6 — the far side said no: a credential is missing, expired, or refused.
     *
     * Its own code because it is the most actionable one in the survey. `RUNTIME` means *it
     * failed, read the message*; `AUTH` means *log in and run it again*, and a script or an
     * agent can branch on that without parsing prose. `USAGE` says fix the script, `CONFIG`
     * says fix the runner, and this says fix the credential — three different responses that
     * collapsed into one code before it existed.
     *
     * **5, where `gh` uses 4.** Four is `CANCELLED` here and has been since the contract was
     * written, and moving a published code to match another tool's is a breaking change for
     * every consumer that already branches on it. The survey's other citation, `aws` v2, uses
     * 252/253/254 and agrees with nobody either; what matters is that the code is stable and
     * documented, not that it matches a particular neighbour.
     */
    readonly AUTH: 5;
    /** SIGINT after the terminal was restored (E5). */
    readonly SIGINT: 130;
};

type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
```

### ExitCodeValue

E1 — exit codes are a contract. No other literal may reach `process.exitCode`.

```ts
const ExitCode: {
    /** Command completed. */
    readonly OK: 0;
    /** The command ran and failed. Never accompanied by help text (E2). */
    readonly RUNTIME: 1;
    /** Bad arguments, unknown command, missing flag, prompt needed in a non-TTY (P2). */
    readonly USAGE: 2;
    /** Config file or environment could not be loaded or validated (V1). */
    readonly CONFIG: 3;
    /** The user or caller cancelled. */
    readonly CANCELLED: 4;
    /**
     * E6 — the far side said no: a credential is missing, expired, or refused.
     *
     * Its own code because it is the most actionable one in the survey. `RUNTIME` means *it
     * failed, read the message*; `AUTH` means *log in and run it again*, and a script or an
     * agent can branch on that without parsing prose. `USAGE` says fix the script, `CONFIG`
     * says fix the runner, and this says fix the credential — three different responses that
     * collapsed into one code before it existed.
     *
     * **5, where `gh` uses 4.** Four is `CANCELLED` here and has been since the contract was
     * written, and moving a published code to match another tool's is a breaking change for
     * every consumer that already branches on it. The survey's other citation, `aws` v2, uses
     * 252/253/254 and agrees with nobody either; what matters is that the code is stable and
     * documented, not that it matches a particular neighbour.
     */
    readonly AUTH: 5;
    /** SIGINT after the terminal was restored (E5). */
    readonly SIGINT: 130;
};

type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
```

### processRuntime

The real `Runtime`, for a caller that injects none.

`argv`, `env` and `cwd` are getters for the reason the block above gives; `isTTY` is one
too, because a stream's `isTTY` is a property of whatever stream is installed *now*, and
the harness swaps streams. `stdin`/`stdout`/`stderr` likewise.

```ts
const processRuntime: Runtime;
```

## Interfaces

### Clock

Time, as the layers above the parser see it (R14 of `cli-output-stack`). `now` is
monotonic milliseconds from an arbitrary origin; `schedule` runs `fn` after `ms` and
hands back the cancel. Typed structurally so the output stack can accept a `Runtime`
without importing one.

```ts
interface Clock {
    now(): number;
    schedule(fn: () => void, ms: number): () => void;
}
```

### FakeClock

A `Clock` that moves only when the test says so (R14): `tick` is the only source of time.

```ts
interface FakeClock extends Clock {
    /**
     * Advance by `ms`, running every callback that falls due, earliest first, then by order
     * scheduled. A callback that schedules inside the window runs in the same tick, so one that
     * reschedules itself at `0` would never leave the loop (real Node yields between turns);
     * a tick runs at most `TICK_CAP` callbacks and throws, naming the cap, when exceeded.
     */
    tick(ms: number): void;
    /** Callbacks scheduled and neither run nor cancelled. */
    pending(): number;
}
```

### FakeRuntime

```ts
interface FakeRuntime extends Runtime {
    out: string[];
    err: string[];
    clock: FakeClock;
}
```

### RunOptions

```ts
interface RunOptions {
    argv: string[];
    env?: Record<string, string>;
    stdin?: string | Readable;
    cwd?: string;
    /** `true` = every stream is a TTY; an object sets each; default: none is. */
    tty?: boolean | Partial<Runtime['isTTY']>;
    /** The clock the runtime reports; a fresh `fakeClock()` at 0 when not given. */
    clock?: FakeClock;
}
```

### RunResult

```ts
interface RunResult {
    code: ExitCode;
    stdout: string;
    stderr: string;
    /** Parsed stdout when `--json` was in argv and stdout parsed; see `finish`. */
    json?: unknown;
    durationMs: number;
}
```

### Runtime

The world, as the layers above the parser see it (design R1 of
`cli-testing-harness`). Nothing above the parser reads `process.*` directly; it
reads its `Runtime`, so a test can substitute every part of it.

```ts
interface Runtime {
    argv: string[];
    env: Record<string, string | undefined>;
    cwd: string;
    stdin: NodeJS.ReadableStream;
    stdout: Writer;
    stderr: Writer;
    isTTY: {
        stdin: boolean;
        stdout: boolean;
        stderr: boolean;
    };
    /** Ends the run with an E1 code. In the real runtime this never returns. */
    exit(code: ExitCode): never;
    /** `performance.now` and `setTimeout` in the real runtime; a manual tick in the harness. */
    clock: Clock;
}
```

### Writer

Anything that accepts text; `process.stdout` satisfies it, so does an array push.

```ts
interface Writer {
    write(chunk: string): unknown;
}
```
