Public: anyone can read this; the crew can change it.
test/issue-teams.test.ts's cross-team suite passes under the full check, and a hook failure there names itself #4760
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4756-test-issue-teams-test-ts-s-cross-team-su → main 1 commit fork
1 file
changed +56
−32
test/issue-teams.test.ts +56 −32
| @@ -75,41 +75,65 @@ describe("cross-team work", () => { | ||
| 75 | 75 | const reviewRuns = (pull: Pull) => |
| 76 | 76 | listRuns(app.db, repo.id, { issueId: pull.id }).filter((r) => r.reason === "review"); |
| 77 | 77 | |
| 78 | beforeAll(async () => { | |
| 79 | dataDir = await mkdtemp(join(tmpdir(), "forge-issue-teams-")); | |
| 80 | app = createApp({ dataDir, port: 0 }); | |
| 81 | const made = await createUser(app.db, { | |
| 82 | origin: "seed", | |
| 83 | trusted: true, | |
| 84 | username: "alice", | |
| 85 | email: "[email protected]", | |
| 86 | password: "a-good-passphrase", | |
| 87 | adminIfFirst: true, | |
| 88 | }); | |
| 89 | if (!made.ok) throw new Error(made.error); | |
| 90 | alice = made.value; | |
| 91 | const r = await createRepo(app, alice, { name: "garden", visibility: "private" }); | |
| 92 | if (!r.ok) throw new Error(String(r.error)); | |
| 93 | repo = r.value; | |
| 94 | const seeded = await commitToBranch(app, alice, repo, { | |
| 95 | message: "seed", | |
| 96 | files: [{ path: "README.md", content: "# garden\n" }], | |
| 97 | }); | |
| 98 | if (!seeded.ok) throw new Error(JSON.stringify(seeded.error)); | |
| 99 | for (const name of ["quill", "pike"]) { | |
| 100 | const agent = await createAgent(app, alice, { username: name }); | |
| 101 | if (!agent.ok) throw new Error(String(agent.error)); | |
| 102 | addToCrew(app, alice, repo, name); | |
| 103 | setCrewRole(app.db, repo.id, id(name), "reviewer"); | |
| 78 | /** Run one setup or teardown step; a failure names the step, so a red check says which. */ | |
| 79 | const step = async <T>(name: string, work: () => T | Promise<T>): Promise<T> => { | |
| 80 | try { | |
| 81 | return await work(); | |
| 82 | } catch (error) { | |
| 83 | throw new Error(`issue-teams ${name} failed: ${error instanceof Error ? error.message : String(error)}`, { | |
| 84 | cause: error, | |
| 85 | }); | |
| 104 | 86 | } |
| 105 | saveTeam(app.db, "design", { charter: "", dial: 0, members: [id("quill")] }); | |
| 106 | saveTeam(app.db, "data-science", { charter: "", dial: 0, members: [id("pike")] }); | |
| 107 | }, 30_000); | |
| 87 | }; | |
| 88 | ||
| 89 | beforeAll(async () => { | |
| 90 | await step("seed", async () => { | |
| 91 | dataDir = await mkdtemp(join(tmpdir(), "forge-issue-teams-")); | |
| 92 | app = createApp({ dataDir, port: 0 }); | |
| 93 | const made = await createUser(app.db, { | |
| 94 | origin: "seed", | |
| 95 | trusted: true, | |
| 96 | username: "alice", | |
| 97 | email: "[email protected]", | |
| 98 | password: "a-good-passphrase", | |
| 99 | adminIfFirst: true, | |
| 100 | }); | |
| 101 | if (!made.ok) throw new Error(made.error); | |
| 102 | alice = made.value; | |
| 103 | const r = await createRepo(app, alice, { name: "garden", visibility: "private" }); | |
| 104 | if (!r.ok) throw new Error(String(r.error)); | |
| 105 | repo = r.value; | |
| 106 | const seeded = await commitToBranch(app, alice, repo, { | |
| 107 | message: "seed", | |
| 108 | files: [{ path: "README.md", content: "# garden\n" }], | |
| 109 | }); | |
| 110 | if (!seeded.ok) throw new Error(JSON.stringify(seeded.error)); | |
| 111 | }); | |
| 112 | await step("agents", async () => { | |
| 113 | for (const name of ["quill", "pike"]) { | |
| 114 | const agent = await createAgent(app, alice, { username: name }); | |
| 115 | if (!agent.ok) throw new Error(String(agent.error)); | |
| 116 | addToCrew(app, alice, repo, name); | |
| 117 | setCrewRole(app.db, repo.id, id(name), "reviewer"); | |
| 118 | } | |
| 119 | }); | |
| 120 | await step("teams", () => { | |
| 121 | saveTeam(app.db, "design", { charter: "", dial: 0, members: [id("quill")] }); | |
| 122 | saveTeam(app.db, "data-science", { charter: "", dial: 0, members: [id("pike")] }); | |
| 123 | }); | |
| 124 | }, 120_000); | |
| 108 | 125 | |
| 109 | 126 | afterAll(async () => { |
| 110 | app.close(); | |
| 111 | await rm(dataDir, { recursive: true, force: true }); | |
| 112 | }); | |
| 127 | await step("close", async () => { | |
| 128 | // A push starts work nobody holds a handle to (pull heads following it, | |
| 129 | // reader processes); let it finish before the database closes under it | |
| 130 | // and the data dir goes, so load cannot turn it into a hook failure. | |
| 131 | for (let i = 0; i < 20; i++) await new Promise((done) => setImmediate(done)); | |
| 132 | await Bun.sleep(100); | |
| 133 | app?.close(); | |
| 134 | if (dataDir !== "") await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 }); | |
| 135 | }); | |
| 136 | }, 30_000); | |
| 113 | 137 | |
| 114 | 138 | test("an issue names at most two real teams", () => { |
| 115 | 139 | const o = openIssue(app, alice, repo, { title: "Chart" }); |
new above
offline