1a88ab3199
The v0.8 milestone: installable on any Debian-based server via docker (Gitea registry image, honest sandbox degrade), deb (amd64+arm64, advisory postinst), and arch-aware binary — with release consistency enforced by pre-commit hooks + Gitea Actions (3-runner fleet on CT 113, machine-made releases with the full 8-asset matrix + registry image), the CLI carrying the full suite (guide, channel-aware doctor), and the admin runbook (deploy/README.md, evidence-backed recipes). ---ci--- phase: 6 milestone: v0.8 status: complete requirements: covered: [REQ-8-001, REQ-8-002, REQ-8-003, REQ-8-004, REQ-8-005] partial: [] ---/ci---
54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import type { Ctx } from "../src/ctx.ts";
|
|
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
import net from "node:net";
|
|
import { run as spawnRun } from "../src/lib/spawn.ts";
|
|
import type { SpawnResult } from "../src/lib/spawn.ts";
|
|
|
|
export interface TestCtx extends Ctx {
|
|
out(): string;
|
|
err(): string;
|
|
writes: Record<string, string>;
|
|
}
|
|
|
|
export function testCtx(overrides: Partial<Ctx> = {}): TestCtx {
|
|
const out: string[] = [];
|
|
const err: string[] = [];
|
|
const writes: Record<string, string> = {};
|
|
const base = {
|
|
cwd: process.cwd(),
|
|
env: { ...process.env } as Record<string, string | undefined>,
|
|
stdout: { write: (s: string) => void out.push(s) },
|
|
stderr: { write: (s: string) => void err.push(s) },
|
|
spawn: async (cmd: string, args: string[], opts: Parameters<Ctx["spawn"]>[2]) =>
|
|
spawnRun(cmd, args, opts),
|
|
exists: (p: string) => existsSync(p),
|
|
readFile: (p: string) => (existsSync(p) ? readFileSync(p, "utf8") : undefined),
|
|
writeFile: (p: string, c: string) => {
|
|
writes[p] = c;
|
|
writeFileSync(p, c);
|
|
},
|
|
portFree: async (port: number, host: string) =>
|
|
new Promise<boolean>((resolve) => {
|
|
const srv = net.createServer();
|
|
srv.once("error", () => resolve(false));
|
|
srv.once("listening", () => srv.close(() => resolve(true)));
|
|
srv.listen(port, host);
|
|
}),
|
|
out: () => out.join(""),
|
|
err: () => err.join(""),
|
|
};
|
|
const ctx = { ...base, ...overrides } as unknown as TestCtx;
|
|
(ctx as unknown as { writes: Record<string, string> }).writes = writes;
|
|
return ctx;
|
|
}
|
|
|
|
export function fakeSpawn(
|
|
sequence: Array<{ match: (cmd: string, args: string[]) => boolean; result: SpawnResult }>,
|
|
): Ctx["spawn"] {
|
|
return async (cmd, args) => {
|
|
const hit = sequence.find((s) => s.match(cmd, args));
|
|
if (!hit) throw new Error(`unexpected spawn: ${cmd} ${args.join(" ")}`);
|
|
return hit.result;
|
|
};
|
|
}
|