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

A test pins proposing to the parent forge: a forge with no parent link, or another repository, is refused and nothing is sent #7128

Merged opened this

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

Merged

scout/forge:scout/7116-a-test-pins-proposing-to-the-parent-forg → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/7116-a-test-pins-proposing-to-the-parent-forg was merged into main as 367a8ad.

Review

Sign in to review this change.
1 file changed +110 −0
test/parent-forge-refusals.test.ts added +110
@@ -0,0 +1,110 @@
1 /**
2 * Proposing to the parent forge (#7116): a forge with no parent link, a
3 * repository that is not the one linked, and a link with no stored
4 * credential are each refused as `no_parent_forge`, and nothing is sent to
5 * the parent — not a push, not a pull.
6 */
7
8 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
9 import { mkdtemp, rm } from "node:fs/promises";
10 import { tmpdir } from "node:os";
11 import { join } from "node:path";
12 import { commitToBranch, createRepo } from "../src/actions/repos.ts";
13 import { proposeToParentForge } from "../src/actions/upstream.ts";
14 import { createApp, type App } from "../src/app.ts";
15 import type { Repo } from "../src/core/repos.ts";
16 import { createUser, type User } from "../src/core/users.ts";
17 import { keepParentCredential } from "../src/spawn/parent_credential.ts";
18 import { quietLogs } from "./helpers/globals.ts";
19
20 quietLogs();
21
22 describe("proposing to a parent forge that is not there for this repository", () => {
23 let dir = "";
24 let stub: ReturnType<typeof Bun.serve>;
25 let seen: string[] = [];
26 let n = 0;
27
28 beforeAll(async () => {
29 dir = await mkdtemp(join(tmpdir(), "forge-noparent-"));
30 stub = Bun.serve({
31 port: 0,
32 hostname: "127.0.0.1",
33 fetch(req) {
34 seen.push(`${req.method} ${new URL(req.url).pathname}`);
35 return new Response("{}", { status: 500 });
36 },
37 });
38 });
39
40 afterAll(async () => {
41 await stub.stop(true);
42 await rm(dir, { recursive: true, force: true });
43 });
44
45 /** A child forge with one repository that has a commit to propose. */
46 const child = async (
47 owner: string,
48 name: string,
49 parentDoc?: (tip: string) => Record<string, unknown>,
50 ): Promise<{ app: App; user: User; repo: Repo }> => {
51 const app = createApp({ dataDir: join(dir, `c${n++}`), port: 0, host: "kid.test" });
52 const made = await createUser(app.db, {
53 origin: "seed",
54 trusted: true,
55 username: owner,
56 email: `${owner}@forge.local`,
57 password: "a-long-refusal-passphrase",
58 adminIfFirst: true,
59 });
60 if (!made.ok) throw new Error(made.error);
61 const repo = await createRepo(app, made.value, { name, visibility: "public" });
62 if (!repo.ok) throw new Error(String(repo.error));
63 const c = await commitToBranch(app, made.value, repo.value, {
64 branch: "main",
65 message: "Start",

parentDoc takes a tip that is always "" and no caller uses it. Make it a zero-argument function.

66 files: [{ path: "README.md", content: "hi\n" }],
67 });
68 if (!c.ok) throw new Error(JSON.stringify(c.error));
69 if (parentDoc !== undefined)
70 await Bun.write(join(app.config.dataDir, "parent.json"), JSON.stringify(parentDoc("")));
71 return { app, user: made.value, repo: repo.value };
72 };
73
74 const doc = (repo: string): Record<string, unknown> => ({
75 upstream: `http://127.0.0.1:${stub.port}`,
76 name: "kid",
77 forgeCode: { repo, upstream: "alice/app", mode: "tell-me", tip: "", chain: [] },
78 });
79
80 const refused = async (c: { app: App; user: User; repo: Repo }) => {
81 seen = [];
82 const r = await proposeToParentForge(c.app, c.user, c.repo);
83 expect(r.ok ? "ok" : r.error).toBe("no_parent_forge");
84 expect(seen).toEqual([]);
85 };
86
87 test("a forge with no parent.json is refused", async () => {
88 await refused(await child("kid", "app"));
89 });
90
91 test("a forge linked to alice/app proposing from bob/other is refused", async () => {
92 const c = await child("bob", "other", () => doc("alice/app"));
93 const kept = await keepParentCredential(c.app, { child: "kid", token: "a-proposal-token-1" });
94 if (!kept.ok) throw new Error(kept.error);
95 await refused(c);
96 });
97
98 test("a link with no stored credential is refused", async () => {
99 await refused(await child("alice", "app", () => doc("alice/app")));
100 });
101
102 test("the same link with a credential gets past the refusal", async () => {
103 const c = await child("alice", "app", () => doc("alice/app"));
104 const kept = await keepParentCredential(c.app, { child: "kid", token: "a-proposal-token-1" });
105 if (!kept.ok) throw new Error(kept.error);
106 seen = [];
107 const r = await proposeToParentForge(c.app, c.user, c.repo);
108 expect(r.ok ? "ok" : r.error).not.toBe("no_parent_forge");
109 });
110 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs