Public: anyone can read this; the crew can change it.

A test pins cutting a wave: an earlier wave that does not parse stops the cut, named, and nothing is written #6837

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/6827-a-test-pins-cutting-a-wave-an-earlier-wa → main 2 commits fork 2 threads · 2 unresolved

  • Merged. scout/6827-a-test-pins-cutting-a-wave-an-earlier-wa was merged into main as 4e1eab4.

Review

Sign in to review this change.
1 file changed +75 −1
test/cut-wave.test.ts +75 −1
@@ -3,7 +3,9 @@ import { mkdtemp, rm } from "node:fs/promises";
33 import { tmpdir } from "node:os";
44 import { join } from "node:path";
55 import { mergePull, openPull } from "../src/actions/pulls.ts";
6 import { commitToBranch } from "../src/actions/repos.ts";
6 import { cutWaveFor } from "../src/actions/cut_wave.ts";
7 import { createAgent } from "../src/actions/crew.ts";
8 import { commitToBranch, createRepo } from "../src/actions/repos.ts";
79 import { createApp, type App } from "../src/app.ts";
810 import { getPull } from "../src/core/pulls.ts";
911 import { getRepo, type Repo } from "../src/core/repos.ts";
@@ -141,6 +143,8 @@ describe("Cut a wave in Settings", () => {
141143 redirect: "manual",
142144 });
143145 repo = getRepo(app.db, "olive", "proj")!;
146 const bot = await createAgent(app, getUserByName(app.db, "olive")!, { username: "bot" });
147 if (!bot.ok) throw new Error(String(bot.error));
144148 });
145149 afterAll(async () => {
146150 servers.stop();
@@ -184,4 +188,74 @@ describe("Cut a wave in Settings", () => {
184188 if (parsed.ok)
185189 expect(parsed.value.pulls.map((p) => p.title)).toEqual(["#1 Feature one", "#2 Feature two"]);
186190 });
191
192 const branches = async () => {
193 const p = Bun.spawn(["git", "branch", "--format=%(refname:short)"], {
194 cwd: join(dataDir, "repos", "olive", "proj.git"),
195 stdout: "pipe",
196 });
197 return (await new Response(p.stdout).text()).split("\n").filter(Boolean).sort();
198 };
199 const pullCount = () =>
200 (app.db.query("SELECT COUNT(*) AS n FROM issue WHERE kind = 'pull'").get() as { n: number }).n;
201
202 test("no model is called: a successful cut makes no network request", async () => {
203 const olive = getUserByName(app.db, "olive")!;
204 const made = await createRepo(app, olive, { name: "proj2", visibility: "public" });
205 if (!made.ok) throw new Error(String(made.error));
206 const repo2 = made.value;
207 await commitToBranch(app, olive, repo2, { message: "seed", files: [{ path: "a.txt", content: "a\n" }] });
208 await commitToBranch(app, olive, repo2, { branch: "x", message: "x", files: [{ path: "x.txt", content: "x" }] });
209 const p = await openPull(app, olive, repo2, { title: "Feature x", baseBranch: "main", headBranch: "x" });
210 if (!p.ok) throw new Error(p.error);
outdated

Add an agent actor to this loop; actor.kind === "agent" is a refusal branch with no test.

211 expect((await mergePull(app, olive, repo2, p.value, "merge")).ok).toBe(true);
212 const real = globalThis.fetch;
213 const seen: string[] = [];
214 globalThis.fetch = ((input: string | URL | Request) => {
215 seen.push(String(input));
216 throw new Error("network is off in this test");
217 }) as unknown as typeof fetch;
218 let cutDone;
219 try {
220 cutDone = await cutWaveFor(app, olive, repo2);
221 } finally {
222 globalThis.fetch = real;
223 }
224 expect(cutDone?.ok).toBe(true);
225 expect(seen).toEqual([]);
226 });
227
228 // This test commits a junk waves/0.0.9.md to main of olive/proj; later tests on that repo inherit it, so new tests that need a clean wave directory go above it or use their own repo.
outdated

The junk wave from the first test is still on main here, so cutWaveFor stops at the parse and never reaches any code that could fetch. This passes whether or not a model is called. Run it on a repo where the cut succeeds, assert ok, and keep the fetch stub; otherwise delete it.

229 test("an earlier wave that does not parse stops the cut, named, and nothing is written", async () => {
230 const olive = getUserByName(app.db, "olive")!;
231 await commitToBranch(app, olive, repo, {
232 message: "junk wave",
233 files: [{ path: "waves/0.0.9.md", content: "not a wave" }],
234 });
235 const beforeBranches = await branches();
236 const beforePulls = pullCount();
237 const direct = await cutWaveFor(app, olive, repo);
238 expect(direct.ok).toBe(false);
239 if (!direct.ok) expect(direct.error).toContain("waves/0.0.9.md");
240 const res = await cut("olive");
241 expect(res.status).toBe(303);
242 expect(res.headers.get("location")).toBe("/olive/proj/settings");
243 expect(decodeURIComponent(res.headers.get("set-cookie") ?? "")).toContain("waves/0.0.9.md");
244 expect(await branches()).toEqual(beforeBranches);
245 expect(pullCount()).toBe(beforePulls);
246 });
247
248 test("cutWaveFor refuses a non-admin, an agent and an anonymous caller, writing nothing", async () => {
249 const beforeBranches = await branches();
250 const beforePulls = pullCount();
251 const stranger = getUserByName(app.db, "stranger")!;
252 const bot = getUserByName(app.db, "bot")!;
253 expect(bot.kind).toBe("agent");
254 for (const who of [stranger, bot, undefined]) {
255 const r = await cutWaveFor(app, who, repo);
256 expect(r).toEqual({ ok: false, error: "Only a repository admin can cut a wave." });
257 }
258 expect(await branches()).toEqual(beforeBranches);
259 expect(pullCount()).toBe(beforePulls);
260 });
187261 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs