File
Blob: tests/worker/webhooks/private/lifecycle.test.ts
| 1 | import { describe, expect, it } from "vitest"; |
| 2 | |
| 3 | import { readProjectDoRows } from "../../../helpers/runtime"; |
| 4 | import { registerWorkerRuntimeHooks } from "../../../helpers/worker-hooks"; |
| 5 | |
| 6 | import { createOwnedProjectContext, deleteWebhook, getWebhooks, putWebhook, rotateWebhookSecret } from "../helpers"; |
| 7 | |
| 8 | describe("webhook private routes", () => { |
| 9 | registerWorkerRuntimeHooks(); |
| 10 | |
| 11 | describe("provider lifecycle", () => { |
| 12 | it("rotates webhook secrets and deletes provider config", async () => { |
| 13 | const context = await createOwnedProjectContext({ |
| 14 | user: { |
| 15 | email: "webhook-private-rotate@example.com", |
| 16 | slug: "webhook-private-rotate", |
| 17 | }, |
| 18 | project: { |
| 19 | projectSlug: "rotate-project", |
| 20 | repoUrl: "https://gitea.example.com:8443/git/example/rotate-project", |
| 21 | }, |
| 22 | }); |
| 23 | |
| 24 | const created = await putWebhook(context.sessionId, context.project.id, "gitea", { |
| 25 | enabled: true, |
| 26 | config: { |
| 27 | instanceUrl: "https://gitea.example.com:8443/git", |
| 28 | }, |
| 29 | secret: "initial-gitea-secret", |
| 30 | }); |
| 31 | expect(created.status).toBe(201); |
| 32 | |
| 33 | const beforeRotate = await readProjectDoRows(context.project.id); |
| 34 | expect(beforeRotate.webhooks).toHaveLength(1); |
| 35 | |
| 36 | const rotated = await rotateWebhookSecret(context.sessionId, context.project.id, "gitea"); |
| 37 | expect(rotated.status).toBe(200); |
| 38 | expect(rotated.body?.secret).toEqual(expect.any(String)); |
| 39 | expect(rotated.body?.secret).not.toBe("initial-gitea-secret"); |
| 40 | |
| 41 | const afterRotate = await readProjectDoRows(context.project.id); |
| 42 | expect(afterRotate.webhooks).toHaveLength(1); |
| 43 | expect(afterRotate.webhooks[0]?.secretCiphertext).not.toEqual(beforeRotate.webhooks[0]?.secretCiphertext); |
| 44 | expect(afterRotate.webhooks[0]?.secretNonce).not.toEqual(beforeRotate.webhooks[0]?.secretNonce); |
| 45 | |
| 46 | const deleted = await deleteWebhook(context.sessionId, context.project.id, "gitea"); |
| 47 | expect(deleted.status).toBe(204); |
| 48 | |
| 49 | const listed = await getWebhooks(context.sessionId, context.project.id); |
| 50 | expect(listed.status).toBe(200); |
| 51 | expect(listed.body?.webhooks).toEqual([]); |
| 52 | |
| 53 | const afterDelete = await readProjectDoRows(context.project.id); |
| 54 | expect(afterDelete.webhooks).toEqual([]); |
| 55 | }); |
| 56 | |
| 57 | it("manages multiple providers on the same project independently", async () => { |
| 58 | const context = await createOwnedProjectContext({ |
| 59 | user: { |
| 60 | email: "webhook-private-multi-provider@example.com", |
| 61 | slug: "webhook-private-multi-provider", |
| 62 | }, |
| 63 | project: { |
| 64 | projectSlug: "multi-provider-project", |
| 65 | repoUrl: "https://gitlab.example.com/example/multi-provider-project", |
| 66 | }, |
| 67 | }); |
| 68 | |
| 69 | const gitlabCreated = await putWebhook(context.sessionId, context.project.id, "gitlab", { |
| 70 | enabled: true, |
| 71 | config: { instanceUrl: "https://gitlab.example.com" }, |
| 72 | secret: "gitlab-multi-secret", |
| 73 | }); |
| 74 | expect(gitlabCreated.status).toBe(201); |
| 75 | |
| 76 | const giteaCreated = await putWebhook(context.sessionId, context.project.id, "gitea", { |
| 77 | enabled: true, |
| 78 | config: { instanceUrl: "https://gitlab.example.com" }, |
| 79 | secret: "gitea-multi-secret", |
| 80 | }); |
| 81 | expect(giteaCreated.status).toBe(201); |
| 82 | |
| 83 | const listed = await getWebhooks(context.sessionId, context.project.id); |
| 84 | expect(listed.status).toBe(200); |
| 85 | expect(listed.body?.webhooks).toHaveLength(2); |
| 86 | const providers = listed.body!.webhooks.map((w) => w.provider).sort(); |
| 87 | expect(providers).toEqual(["gitea", "gitlab"]); |
| 88 | |
| 89 | await deleteWebhook(context.sessionId, context.project.id, "gitlab"); |
| 90 | const afterDelete = await getWebhooks(context.sessionId, context.project.id); |
| 91 | expect(afterDelete.status).toBe(200); |
| 92 | expect(afterDelete.body?.webhooks).toHaveLength(1); |
| 93 | expect(afterDelete.body?.webhooks[0]?.provider).toBe("gitea"); |
| 94 | |
| 95 | const rows = await readProjectDoRows(context.project.id); |
| 96 | expect(rows.webhooks).toHaveLength(1); |
| 97 | expect(rows.webhooks[0]?.provider).toBe("gitea"); |
| 98 | }); |
| 99 | }); |
| 100 | }); |