import { assert, assertEquals, assertRejects } from "jsr:@std/assert@1"; import { type PostContext, postRuntime } from "./post.ts"; import type { RuntimeManifest, RuntimeOutcome } from "./runtime.ts"; const context: PostContext = { apiUrl: "https://gitea.example/api/v1", repository: "owner/repo", issueIndex: "7", eventName: "issue_comment", }; async function fixture( source: Extract | { ok: true; body: string }, review?: string, anchors?: unknown, ): Promise<{ manifestPath: string; runtimeDir: string; reviewerFile: string; reviewPath: string; outcomeFile: string; }> { const runtimeDir = await Deno.makeTempDir(); const reviewerFile = `${runtimeDir}/reviewer-token`; const outcomeFile = `${runtimeDir}/outcome.json`; const profileHome = `${runtimeDir}/profile-0`; await Deno.mkdir(profileHome); await Deno.writeTextFile(reviewerFile, "reviewer-secret"); const outcome: RuntimeOutcome = source.ok ? { ...source, profile: { type: "codex", model: "gpt-test" }, reviewPath: `${profileHome}/review.md`, anchorsPath: `${profileHome}/anchors.json`, } : source; await Deno.writeTextFile(outcomeFile, JSON.stringify(outcome)); if (outcome.ok) { if (review !== undefined) { await Deno.writeTextFile(outcome.reviewPath, review, { mode: 0o600 }); await Deno.chmod(outcome.reviewPath, 0o600); } if (anchors !== undefined) { await Deno.writeTextFile(outcome.anchorsPath, JSON.stringify(anchors), { mode: 0o600, }); await Deno.chmod(outcome.anchorsPath, 0o600); } } const manifestPath = `${runtimeDir}/manifest.json`; const manifest: RuntimeManifest = { runtimeDir, manifestPath, reviewerFile, outcomeFile, profiles: [{ index: 0, type: "claude", model: "test", uid: Deno.uid()!, gid: Deno.gid()!, home: profileHome, tokenFile: null, }], }; await Deno.writeTextFile(manifestPath, JSON.stringify(manifest)); return { manifestPath, runtimeDir, reviewerFile, reviewPath: `${profileHome}/review.md`, outcomeFile, }; } const missing = async (path: string): Promise => { try { await Deno.stat(path); return false; } catch (error) { if (error instanceof Deno.errors.NotFound) return true; throw error; } }; Deno.test("posts a successful issue result and removes runtime credentials", async () => { const result = await fixture({ ok: true, body: "done" }); const requests: Array<{ url: string; init?: RequestInit }> = []; await postRuntime(result.manifestPath, context, (url, init) => { requests.push({ url: String(url), init }); return Promise.resolve(new Response(null, { status: 201 })); }); assertEquals(requests.length, 1); assertEquals( requests[0].url, `${context.apiUrl}/repos/owner/repo/issues/7/comments`, ); assertEquals(JSON.parse(String(requests[0].init?.body)), { body: "done\n\n> Profile: `codex` · `gpt-test`", }); assertEquals(requests[0].init?.headers, { Authorization: "token reviewer-secret", "Content-Type": "application/json", }); assert(await missing(result.runtimeDir)); }); Deno.test("retries a rejected inline review without anchors", async () => { const result = await fixture( { ok: true, body: "ignored final response" }, "Approved\nReady", [{ path: "run.ts", line: 1, side: "new", body: "Finding" }], ); const bodies: unknown[] = []; await postRuntime(result.manifestPath, { ...context, eventName: "pull_request", }, (_url, init) => { bodies.push(JSON.parse(String(init?.body))); return Promise.resolve( new Response(null, { status: bodies.length === 1 ? 422 : 200 }), ); }); assertEquals((bodies[0] as { comments: unknown[] }).comments.length, 1); assertEquals( (bodies[0] as { comments: Array<{ body: string }> }).comments[0].body, "Finding\n\n> Profile: `codex` · `gpt-test`", ); assertEquals((bodies[1] as { comments: unknown[] }).comments, []); assertEquals( (bodies[1] as { body: string }).body, "✅ Approved\nReady\n\n> Profile: `codex` · `gpt-test`", ); assert(await missing(result.runtimeDir)); }); Deno.test("reports each tried profile and keeps the action failed", async () => { const result = await fixture({ ok: false, failures: [ { type: "codex", model: "gpt-test", reason: "sign-in failed" }, { type: "claude", model: "claude-test", reason: "usage exhausted" }, { type: "opencode", model: "openrouter/test", reason: "failed" }, ], }); const bodies: unknown[] = []; await assertRejects( () => postRuntime(result.manifestPath, context, (_url, init) => { bodies.push(JSON.parse(String(init?.body))); return Promise.resolve(new Response(null, { status: 201 })); }), Error, "agent phase failed", ); assertEquals(bodies, [{ body: "AI bot failed.\n\n> Tried: `codex` · `gpt-test` (sign-in failed), " + "`claude` · `claude-test` (usage exhausted), " + "`opencode` · `openrouter/test` (failed)", }]); assert(!JSON.stringify(bodies).includes("reviewer-secret")); assert(await missing(result.runtimeDir)); }); Deno.test("drops failure entries that are not known labels", async () => { const result = await fixture({ ok: false, failures: [] }); await Deno.writeTextFile( result.outcomeFile, JSON.stringify({ ok: false, failures: [ { type: "codex", model: "gpt-test", reason: "token sk-secret leaked" }, { type: "shell", model: "x", reason: "failed" }, { type: "claude", model: "claude-test", reason: "sign-in failed" }, ], }), ); const bodies: unknown[] = []; await assertRejects( () => postRuntime(result.manifestPath, context, (_url, init) => { bodies.push(JSON.parse(String(init?.body))); return Promise.resolve(new Response(null, { status: 201 })); }), Error, "agent phase failed", ); assertEquals(bodies, [{ body: "AI bot failed.\n\n> Tried: `claude` · `claude-test` (sign-in failed)", }]); }); Deno.test("reports a generic failure when the runner wrote no outcome", async () => { const result = await fixture({ ok: false, failures: [] }); await Deno.remove(result.outcomeFile); const bodies: unknown[] = []; await assertRejects( () => postRuntime(result.manifestPath, context, (_url, init) => { bodies.push(JSON.parse(String(init?.body))); return Promise.resolve(new Response(null, { status: 201 })); }), Error, "agent phase failed", ); assertEquals(bodies, [{ body: "AI bot failed.\n\n> Profile: none" }]); assert(await missing(result.runtimeDir)); }); Deno.test("removes runtime credentials when posting fails", async () => { const result = await fixture({ ok: true, body: "done" }); await assertRejects( () => postRuntime( result.manifestPath, context, () => Promise.resolve(new Response("failed", { status: 500 })), ), Error, "500", ); assert(await missing(result.runtimeDir)); }); Deno.test("preserves a Gitea transport failure without retrying a comment", async () => { const result = await fixture({ ok: true, body: "done" }); let requests = 0; await assertRejects( () => postRuntime(result.manifestPath, context, () => { requests++; return Promise.reject(new Error(`network failure ${requests}`)); }), Error, "network failure 1", ); assertEquals(requests, 1); assert(await missing(result.runtimeDir)); }); Deno.test("refuses to follow an agent artifact symlink", async () => { if (Deno.build.os === "windows") return; const result = await fixture({ ok: true, body: "done" }); await Deno.symlink(result.reviewerFile, result.reviewPath); let requests = 0; await assertRejects( () => postRuntime(result.manifestPath, context, () => { requests++; return Promise.resolve(new Response(null, { status: 200 })); }), Error, "regular private file", ); assertEquals(requests, 1); assert(await missing(result.runtimeDir)); }); Deno.test("reports an attributed failure when a pull request review is missing", async () => { const result = await fixture({ ok: true, body: "done" }); const bodies: unknown[] = []; await assertRejects( () => postRuntime(result.manifestPath, { ...context, eventName: "pull_request", }, (_url, init) => { bodies.push(JSON.parse(String(init?.body))); return Promise.resolve(new Response(null, { status: 201 })); }), Error, "did not write", ); assertEquals(bodies, [{ body: "AI bot failed.\n\n> Tried: `codex` · `gpt-test` (failed)", }]); assert(await missing(result.runtimeDir)); });