Public: anyone can read this; the crew can change it.
A test pins stopping a mirror: only the repository's admin can, anyone else is told not found, and the target stays until then #6845
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/6826-a-test-pins-stopping-a-mirror-only-the-r → main 3 commits fork
1 file
changed +99
−0
test/mirror-target.test.ts added +99
| @@ -0,0 +1,99 @@ | ||
| 1 | /** | |
| 2 | * Stopping a mirror (#3408): only the repository's admin can, anyone else is | |
| 3 | * told not found, and the target stays until then. | |
| 4 | */ | |
| 5 | ||
| 6 | import { afterAll, beforeAll, describe, expect, test } from "bun:test"; | |
| 7 | import { mkdtemp, rm } from "node:fs/promises"; | |
| 8 | import { tmpdir } from "node:os"; | |
| 9 | import { join } from "node:path"; | |
| 10 | import { stopMirroring } from "../src/actions/mirror_target.ts"; | |
| 11 | import { createApp, type App } from "../src/app.ts"; | |
| 12 | import { getMirrorTarget, setMirrorTarget } from "../src/core/migrate_mirror.ts"; | |
| 13 | import { addTeamMember, createOrg, createTeam } from "../src/core/orgs.ts"; | |
| 14 | import { createRepo, type Repo } from "../src/core/repos.ts"; | |
| 15 | import { createUser, type User } from "../src/core/users.ts"; | |
| 16 | ||
| 17 | describe("stopping a mirror", () => { | |
| 18 | let dataDir: string; | |
| 19 | let app: App; | |
| 20 | let repo: Repo; | |
| 21 | let siteAdmin: User; | |
| 22 | let admin: User; | |
| 23 | let writer: User; | |
| 24 | let stranger: User; | |
| 25 | ||
| 26 | const person = async (username: string, adminIfFirst = false): Promise<User> => { | |
| 27 | const made = await createUser(app.db, { | |
| 28 | origin: "seed", | |
| 29 | trusted: true, | |
| 30 | adminIfFirst, | |
| 31 | username, | |
| 32 | email: `${username}@forge.test`, | |
| 33 | password: "mirrors-are-one-way", | |
| 34 | }); | |
| 35 | if (!made.ok) throw new Error(String(made.error)); | |
| 36 | return made.value; | |
| 37 | }; | |
| 38 | ||
| 39 | const target = () => getMirrorTarget(app.db, repo.id); | |
| 40 | const arm = () => { | |
| 41 | app.db.run("PRAGMA foreign_keys = OFF"); | |
| 42 | setMirrorTarget(app.db, { | |
| 43 | repoId: repo.id, | |
| 44 | originId: "origin-none", | |
| 45 | fullName: "octo/gizmo", | |
| 46 | cloneUrl: "https://example.test/octo/gizmo.git", | |
| 47 | tips: {}, | |
| 48 | }); | |
| 49 | }; | |
| 50 | ||
| 51 | beforeAll(async () => { | |
| 52 | dataDir = await mkdtemp(join(tmpdir(), "forge-mirror-target-")); | |
| 53 | app = createApp({ dataDir, port: 0 }); | |
| 54 | // The first account is the site admin; none of the actors below is it. | |
| 55 | siteAdmin = await person("siteowner", true); | |
| 56 | admin = await person("alice"); | |
| 57 | writer = await person("bob"); | |
| 58 | stranger = await person("carol"); | |
| 59 | const org = createOrg(app.db, "acme", admin.id); | |
| 60 | if (!org.ok) throw new Error(org.error); | |
| 61 | const team = createTeam(app.db, org.value.id, "Writers", "write"); | |
| 62 | if (!team.ok) throw new Error(team.error); | |
| 63 | addTeamMember(app.db, team.value, writer.id); | |
| 64 | const made = await createRepo(app.db, app.reposRoot, { | |
| 65 | ownerId: org.value.id, | |
| 66 | name: "gizmo", | |
| 67 | visibility: "private", | |
| 68 | }); | |
| 69 | if (!made.ok) throw new Error(String(made.error)); | |
| 70 | repo = made.value; | |
| 71 | arm(); | |
| 72 | }); | |
| 73 | ||
| 74 | afterAll(async () => { | |
| 75 | app.close(); | |
| 76 | await rm(dataDir, { recursive: true, force: true, maxRetries: 20, retryDelay: 100 }); | |
| 77 | }); | |
| 78 | ||
| 79 | test("a writer who is not admin, a stranger and a guest are told not found, and the target stays", () => { | |
| 80 | for (const actor of [writer, stranger, undefined]) { | |
| 81 | const answer = stopMirroring(app, actor, repo); | |
| 82 | expect(answer.ok).toBe(false); | |
| 83 | if (!answer.ok) expect(answer.error).toBe("not_found"); | |
| 84 | expect(target()?.fullName).toBe("octo/gizmo"); | |
| 85 | } | |
| 86 | }); | |
| 87 | ||
| 88 | test("the admin stops it: the target is gone, and a second stop answers false", () => { | |
| 89 | expect(stopMirroring(app, admin, repo)).toEqual({ ok: true, value: true }); | |
| 90 | expect(target()).toBeUndefined(); | |
| 91 | expect(stopMirroring(app, admin, repo)).toEqual({ ok: true, value: false }); | |
| 92 | }); | |
| 93 | ||
| 94 | test("the site admin may stop it too", () => { | |
| 95 | arm(); | |
| 96 | expect(stopMirroring(app, siteAdmin, repo)).toEqual({ ok: true, value: true }); | |
| 97 | expect(target()).toBeUndefined(); | |
| 98 | }); | |
| 99 | }); |
new above
offline