Public: anyone can read this; the crew can change it.
Each test in test/releases-page.test.ts stands alone: no test leaves the repository public or the fixture half-built for the next #4710
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4694-each-test-in-test-releases-page-test-ts → main 1 commit fork
1 file
changed +34
−40
test/releases-page.test.ts +34 −40
| @@ -11,13 +11,13 @@ import { tmpdir } from "node:os"; | ||
| 11 | 11 | import { join } from "node:path"; |
| 12 | 12 | import { createRepo } from "../src/actions/repos.ts"; |
| 13 | 13 | import { createApp, type App } from "../src/app.ts"; |
| 14 | import { markVerified } from "../src/core/verify.ts"; | |
| 14 | 15 | import { createSession } from "../src/core/sessions.ts"; |
| 15 | import { createUser } from "../src/core/users.ts"; | |
| 16 | 16 | import { migrations } from "../src/core/schema.ts"; |
| 17 | 17 | import { serve, type Servers } from "../src/http/server.ts"; |
| 18 | 18 | import { releaseDir } from "../src/release/checkout.ts"; |
| 19 | import { beginSwap, endSwap } from "../src/release/swap.ts"; | |
| 19 | 20 | import { recordRelease } from "../src/release/record.ts"; |
| 20 | import { proveAll } from "./helpers/verified.ts"; | |
| 21 | 21 | import { quietLogs } from "./helpers/globals.ts"; |
| 22 | 22 | |
| 23 | 23 | quietLogs(); |
| @@ -41,6 +41,15 @@ describe("the releases page", () => { | ||
| 41 | 41 | const u = app.db.query<{ id: string }, [string]>("SELECT id FROM user WHERE username = ?").get(username)!; |
| 42 | 42 | return `forge_session=${createSession(app.db, u.id).token}`; |
| 43 | 43 | }; |
| 44 | // Accounts inserted directly: no password hash is made, and none of these tests signs in with one. | |
| 45 | const addUser = (username: string, isAdmin: boolean) => { | |
| 46 | const id = `u-${username}`; | |
| 47 | app.db | |
| 48 | .query("INSERT INTO user (id, username, email, password_hash, is_admin) VALUES (?1, ?2, ?3, 'x', ?4)") | |
| 49 | .run(id, username, `${username}@forge.local`, isAdmin ? 1 : 0); | |
| 50 | markVerified(app.db, id, new Date().toISOString()); | |
| 51 | return id; | |
| 52 | }; | |
| 44 | 53 | const post = (path: string, cookie: string, origin: string) => |
| 45 | 54 | fetch(`${servers.url}${path}`, { |
| 46 | 55 | method: "POST", |
| @@ -52,23 +61,9 @@ describe("the releases page", () => { | ||
| 52 | 61 | process.env["FORGE_COMMIT"] = LIVE; |
| 53 | 62 | root = mkdtempSync(join(tmpdir(), "forge-releases-")); |
| 54 | 63 | app = createApp({ dataDir: root, port: 0, forgeRepo: "alice/forge" }); |
| 55 | for (const [username, password, adminIfFirst] of [ | |
| 56 | ["alice", "a-good-passphrase", true], | |
| 57 | ["bob", "another-good-passphrase", false], | |
| 58 | ] as const) { | |
| 59 | const u = await createUser(app.db, { | |
| 60 | origin: "seed", | |
| 61 | trusted: true, | |
| 62 | username, | |
| 63 | email: `${username}@forge.local`, | |
| 64 | password, | |
| 65 | adminIfFirst, | |
| 66 | }); | |
| 67 | if (!u.ok) throw new Error(u.error); | |
| 68 | } | |
| 69 | proveAll(app); | |
| 70 | const alice = app.db.query<{ id: string }, []>("SELECT id FROM user WHERE username='alice'").get()!; | |
| 71 | const made = await createRepo(app, { id: alice.id, username: "alice" } as never, { name: "forge" }); | |
| 64 | addUser("alice", true); | |
| 65 | addUser("bob", false); | |
| 66 | const made = await createRepo(app, { id: "u-alice", username: "alice" } as never, { name: "forge" }); | |
| 72 | 67 | if (!made.ok) throw new Error(made.error); |
| 73 | 68 | const repoId = app.db.query<{ id: string }, []>("SELECT id FROM repo WHERE name='forge'").get()!.id; |
| 74 | 69 | for (const [s, pull, line] of [ |
| @@ -119,30 +114,25 @@ describe("the releases page", () => { | ||
| 119 | 114 | expect(home).not.toContain("aaaaaaa"); |
| 120 | 115 | expect((await post(`/releases/${KEPT}/rollback`, cookie, servers.url)).status).toBe(403); |
| 121 | 116 | |
| 122 | app.db.query("UPDATE repo SET visibility = 'public' WHERE name = 'forge'").run(); | |
| 123 | const refused = await get("/releases"); | |
| 124 | expect(refused.status).toBe(403); | |
| 125 | const body = await refused.text(); | |
| 126 | expect(body).not.toContain("bbbbbbb"); | |
| 127 | expect(body).not.toContain("/rollback"); | |
| 128 | expect((await post(`/releases/${KEPT}/rollback`, cookie, servers.url)).status).toBe(403); | |
| 129 | expect((await post(`/releases/${KEPT}/rollback`, "", servers.url)).status).toBe(403); | |
| 130 | expect(await (await get("/")).text()).not.toContain("live-release"); | |
| 131 | app.db.query("UPDATE repo SET visibility = 'private' WHERE name = 'forge'").run(); | |
| 117 | try { | |
| 118 | app.db.query("UPDATE repo SET visibility = 'public' WHERE name = 'forge'").run(); | |
| 119 | const refused = await get("/releases"); | |
| 120 | expect(refused.status).toBe(403); | |
| 121 | const body = await refused.text(); | |
| 122 | expect(body).not.toContain("bbbbbbb"); | |
| 123 | expect(body).not.toContain("/rollback"); | |
| 124 | expect((await post(`/releases/${KEPT}/rollback`, cookie, servers.url)).status).toBe(403); | |
| 125 | expect((await post(`/releases/${KEPT}/rollback`, "", servers.url)).status).toBe(403); | |
| 126 | expect(await (await get("/")).text()).not.toContain("live-release"); | |
| 127 | } finally { | |
| 128 | app.db.query("UPDATE repo SET visibility = 'private' WHERE name = 'forge'").run(); | |
| 129 | } | |
| 132 | 130 | }); |
| 133 | 131 | |
| 134 | 132 | test("an agent that can write the repository gets no button and a 403", async () => { |
| 135 | const u = await createUser(app.db, { | |
| 136 | origin: "seed", | |
| 137 | trusted: true, | |
| 138 | username: "crew1", | |
| 139 | email: "[email protected]", | |
| 140 | password: "crew-good-passphrase", | |
| 141 | adminIfFirst: false, | |
| 142 | }); | |
| 143 | if (!u.ok) throw new Error(u.error); | |
| 144 | app.db.query("UPDATE user SET is_agent = 1, is_admin = 1 WHERE username = 'crew1'").run(); | |
| 145 | const cookie = `forge_session=${createSession(app.db, u.value.id).token}`; | |
| 133 | const id = addUser("crew1", true); | |
| 134 | app.db.query("UPDATE user SET is_agent = 1 WHERE id = ?").run(id); | |
| 135 | const cookie = `forge_session=${createSession(app.db, id).token}`; | |
| 146 | 136 | const body = await (await fetch(`${servers.url}/releases`, { headers: { cookie } })).text(); |
| 147 | 137 | expect(body).toContain("bbbbbbb"); |
| 148 | 138 | expect(body).not.toContain("/rollback"); |
| @@ -162,6 +152,10 @@ describe("the releases page", () => { | ||
| 162 | 152 | const flash = (res.headers.getSetCookie() ?? []).map((c) => c.split(";")[0]).join("; "); |
| 163 | 153 | const shown = await (await fetch(`${servers.url}/releases`, { headers: { cookie: `${cookie}; ${flash}` } })).text(); |
| 164 | 154 | expect(shown).toContain("FORGE_SELF_SWAP is off"); |
| 155 | // No swap began: the one turn this process may take is still free. | |
| 156 | const turn = beginSwap(); | |
| 157 | if (turn.ok) endSwap(false); | |
| 158 | else expect(turn.line).not.toContain("already under way"); | |
| 165 | 159 | }); |
| 166 | 160 | |
| 167 | 161 | test("the home links the live release", async () => { |
new above
offline