Files
nextcraft/apps/cli/tests/fresh-clone-e2e.test.ts
T
CIAgent 02d324b896 test(P01): harness wiring — coverage, timeouts, markers, turbo pipeline, leak fixes (REQ-7-001)
- pytest-cov + [tool.coverage] config (95% branch, term-missing every run); c8
  wired around tsx --test in web (95.9%) + cli (76.9%) — no runner swap (D-052)
- markers fast/real_server/userns under --strict-markers (D-053); real_server ×5
  uvicorn suites + userns ×2 files + 1 test (test_manager real-unshare catch);
  test_sandbox_agent unmarked per G-105 (plain stdlib process), outlier timeout(120)
- pytest-timeout signal method: 60s global, 180s via conftest collection hook for
  marked tiers; BOTH tiers probe-proven (60.07s / 180.07s)
- FAST=1 fast subset: 621 passed, 49 deselected, 55.4s bare (MH-M2 <60s holds);
  FAST implies NO_COV per verifier GAP-1 fix
- CLI fresh-clone E2E env-gated (CLI_E2E=1, D-055): visible skip, opt-in green
  (902s aggregate step budget); path-invocation silent return → visible skip
- web#test turbo task + root test/web:test/web:test:cov/cli:test:cov/cli:test:e2e/
  ai:test:fast scripts; pnpm test runs 3/3 workspaces (3m)
- leak fixes: AI_PROVIDER via monkeypatch (probe-proven restore),
  seed_verified_identity check-first + IntegrityError-only, test_engine.py:428
  Path.read_text (P3 item done early)
- PLAN.md count discipline: baseline 670/53/64 (671→670 collection fact; 61→64
  v0.5.7 hotfix tests); Task 1-1-04 no-split decision recorded (6.8s combined)

---ci---
phase: 1
milestone: v0.7
status: execute
---/ci---
2026-09-26 17:40:05 +00:00

84 lines
3.5 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdtempSync, existsSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const cliDir = join(dirname(fileURLToPath(import.meta.url)), "..");
const repoRoot = join(cliDir, "..", "..");
// P1 harness wiring (D-055, Task 1-2-02): the real 600s fresh-clone bootstrap
// is opt-in — it never runs inside the default `cli:test` suite. Setting
// CLI_E2E=1 (or `pnpm cli:test:e2e`) runs it; without the gate the test
// reports a VISIBLE skip with the reason, never a silent green.
test(
"fresh-clone E2E: clone -> doctor -> bootstrap -> verify (happy path, transcript)",
{
skip: process.env.CLI_E2E
? false
: "env-gated: set CLI_E2E=1 (pnpm cli:test:e2e) to run the real 600s bootstrap",
},
async () => {
const work = mkdtempSync(join(tmpdir(), "nc-e2e-"));
const cloneDir = join(work, "nextcraft");
const clone = spawnSync(
"git",
["clone", "--quiet", "--no-hardlinks", repoRoot, cloneDir],
{ encoding: "utf8", timeout: 120000 },
);
assert.equal(clone.status, 0, `clone failed: ${clone.stderr}`);
const binary = join(repoRoot, "apps", "cli", "dist", "nextcraft-linux-x64");
const cli = existsSync(binary)
? binary
: join(cliDir, "node_modules", ".bin", "tsx");
const transcript: string[] = [];
const step = (name: string, args: string[], expect: number, timeoutMs: number) => {
const { NODE_TEST_CONTEXT, ...restEnv } = process.env as Record<string, string | undefined>;
const env = {
...restEnv,
PATH: [join(process.env.HOME ?? "/home", ".local/bin"), process.env.PATH].filter(Boolean).join(":"),
NEXTCRAFT_VERSION: "v-e2e",
} as Record<string, string | undefined>;
const run = spawnSync(cli, existsSync(binary) ? args : [join(cliDir, "src", "index.ts"), ...args], {
cwd: cloneDir,
encoding: "utf8",
timeout: timeoutMs,
env,
});
transcript.push(`$ nextcraft ${args.join(" ")} -> exit ${run.status}`);
if (run.status !== expect) {
transcript.push(run.stdout, run.stderr);
assert.fail(
`${name} exited ${run.status} (expected ${expect})\nTRANSCRIPT:\n${transcript.join("\n")}\nstderr: ${run.stderr}`,
);
}
return run.stdout + run.stderr;
};
const doctorOut = step("doctor", ["doctor"], 0, 60000);
assert.ok(doctorOut.includes("node"), "doctor mentions node");
assert.ok(doctorOut.includes("pnpm"), "doctor mentions pnpm");
step("bootstrap", ["bootstrap"], 0, 600000);
const verifyOut = step("verify", ["verify"], 0, 120000);
assert.ok(verifyOut.includes("venv"), "verify covers venv");
assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".env")), ".env created in clone");
assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".venv")), "venv created in clone");
assert.ok(
existsSync(join(cloneDir, "node_modules", ".pnpm")),
"workspace node_modules present",
);
const envExample = readFileSync(join(repoRoot, "apps", "ai-service", ".env.example"), "utf8");
const envClone = readFileSync(join(cloneDir, "apps", "ai-service", ".env"), "utf8");
assert.equal(envClone, envExample, ".env content matches template");
console.log("E2E TRANSCRIPT:\n" + transcript.join("\n"));
},
);