Public: anyone can read this; the crew can change it.
A test pins removing a secret that is not there: `no_such_secret`, and nothing else in that scope changes #7108
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/7102-a-test-pins-removing-a-secret-that-is-no → main 1 commit fork 1 thread · 1 unresolved
1 file
changed +88
−0
test/secret-remove-missing.test.ts added +88
| @@ -0,0 +1,88 @@ | ||
| 1 | /** Removing a secret that is not there is `no_such_secret`, changes nothing, and is only ever said to a forge admin (#7102). */ | |
| 2 | import { afterAll, beforeAll, expect, test } from "bun:test"; | |
| 3 | import { mkdtemp, rm } from "node:fs/promises"; | |
| 4 | import { tmpdir } from "node:os"; | |
| 5 | import { join } from "node:path"; | |
| 6 | import { createApp, type App } from "../src/app.ts"; | |
| 7 | import { removeSecret } from "../src/actions/secrets.ts"; | |
| 8 | import { createRepo } from "../src/actions/repos.ts"; | |
| 9 | import { createOrg } from "../src/core/orgs.ts"; | |
| 10 | import { listSecretNames, setSecret, type SecretScope } from "../src/core/secret_store.ts"; | |
| 11 | import { createUser, type User } from "../src/core/users.ts"; | |
| 12 | import { seedAdmin } from "./helpers/admin.ts"; | |
| 13 | ||
| 14 | const MISSING = "NOT_THERE"; | |
| 15 | const KEPT = "KEPT_TOKEN"; | |
| 16 | ||
| 17 | let dataDir = ""; | |
| 18 | let app: App; | |
| 19 | let admin: User, bob: User; | |
| 20 | let scopes: { scope: SecretScope; ref: string; id: string }[] = []; | |
| 21 | ||
| 22 | const names = (scope: SecretScope, id: string) => listSecretNames(app.db, scope, id).map((s) => s.name); | |
| 23 | ||
| 24 | beforeAll(async () => { | |
| 25 | dataDir = await mkdtemp(join(tmpdir(), "forge-secret-missing-")); | |
| 26 | app = createApp({ dataDir, port: 0 }); | |
| 27 | const a = await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" }); | |
| 28 | if (!a.ok) throw new Error(a.error); | |
| 29 | admin = a.value; | |
| 30 | const b = await createUser(app.db, { | |
| 31 | origin: "seed", trusted: true, username: "bob", email: "[email protected]", password: "correct-horse-battery", | |
| 32 | }); | |
| 33 | if (!b.ok) throw new Error(b.error); | |
| 34 | bob = b.value; | |
| 35 | const org = createOrg(app.db, "acme", admin.id); | |
| 36 | if (!org.ok) throw new Error(org.error); | |
| 37 | const repo = await createRepo(app, admin, { name: "yard", visibility: "public" }); | |
| 38 | if (!repo.ok) throw new Error(repo.error); | |
| 39 | scopes = [ | |
| 40 | { scope: "forge", ref: "", id: "" }, | |
| 41 | { scope: "org", ref: "acme", id: org.value.id }, | |
| 42 | { scope: "repo", ref: `${admin.username}/yard`, id: repo.value.id }, | |
| 43 | ]; | |
| 44 | for (const s of scopes) { | |
| 45 | const set = setSecret(app.db, dataDir, s.scope, s.id, KEPT, "value-" + s.scope); | |
| 46 | if (!set.ok) throw new Error(set.error); | |
| 47 | } | |
| 48 | }); | |
| 49 | ||
| 50 | afterAll(async () => { | |
| 51 | app.close(); | |
| 52 | await rm(dataDir, { recursive: true, force: true }); | |
| 53 | }); | |
| 54 | ||
| 55 | test("a forge admin removing an unknown name at every scope gets no_such_secret and loses nothing", () => { | |
| 56 | for (const s of scopes) { | |
| 57 | const r = removeSecret(app, admin, s.scope, s.ref, MISSING); | |
| 58 | expect(r).toEqual({ ok: false, error: "no_such_secret" }); | |
| 59 | expect(names(s.scope, s.id)).toEqual([KEPT]); | |
| 60 | } | |
| 61 | }); | |
| 62 | ||
| 63 | test("the same name at another scope is not removed", () => { | |
| 64 | for (const s of scopes) { | |
| 65 | const set = setSecret(app.db, dataDir, s.scope, s.id, "SHARED", "v"); | |
| 66 | if (!set.ok) throw new Error(set.error); | |
| 67 | } | |
| 68 | const [forge, org, repo] = scopes as [(typeof scopes)[0], (typeof scopes)[0], (typeof scopes)[0]]; | |
| 69 | expect(removeSecret(app, admin, "org", org.ref, "SHARED").ok).toBe(true); | |
| 70 | expect(names("org", org.id)).toEqual([KEPT]); | |
| 71 | expect(names("forge", forge.id)).toEqual([KEPT, "SHARED"]); | |
| 72 | expect(names("repo", repo.id)).toEqual([KEPT, "SHARED"]); | |
| 73 | // Gone at org now; removing it there again is a miss, and the others still stand. | |
| 74 | expect(removeSecret(app, admin, "org", org.ref, "SHARED")).toEqual({ ok: false, error: "no_such_secret" }); | |
| 75 | expect(names("forge", forge.id)).toEqual([KEPT, "SHARED"]); | |
| 76 | expect(names("repo", repo.id)).toEqual([KEPT, "SHARED"]); | |
| 77 | }); | |
| 78 | ||
| 79 | test("a non-admin and a signed-out caller get not_found, never no_such_secret", () => { | |
| 80 | for (const actor of [bob, undefined]) { | |
| 81 | for (const s of scopes) { | |
| 82 | for (const name of [MISSING, KEPT]) { | |
| 83 | expect(removeSecret(app, actor, s.scope, s.ref, name)).toEqual({ ok: false, error: "not_found" }); | |
| 84 | } | |
| 85 | expect(names(s.scope, s.id)).toContain(KEPT); | |
| 86 | } | |
| 87 | } | |
| 88 | }); |
new above
offline
Non-blocking: "changes nothing" covers the event bus too.
removeSecretappendssecret.deletedonly after a successful delete. If a later refactor emitted the event before callingdeleteSecret, web viewers would see a removal that never happened, and this test would still pass. A one-line check here would pin it: nosecret.deletedevent was appended forNOT_THERE.