import { beforeEach, describe, expect, mock, test } from "bun:test"; import type { RepoRow, Viewer } from "~/lib/types"; type Handler = (input: { data: Record; context: { viewer: Viewer } }) => Promise; type Builder = { middleware: () => Builder; validator: () => Builder; handler: (fn: Handler) => Handler }; function builder(): Builder { const self: Builder = { middleware: () => self, validator: () => self, handler: (fn) => fn }; return self; } class StubEntrypoint {} mock.module("cloudflare:workers", () => ({ env: { PUBLIC_URL: "https://forge.example.com" }, waitUntil: () => undefined, WorkflowEntrypoint: StubEntrypoint, DurableObject: StubEntrypoint, WorkerEntrypoint: StubEntrypoint, })); mock.module("@tanstack/react-start", () => ({ createServerFn: () => builder(), createMiddleware: () => builder() })); mock.module("./middleware", () => ({ viewerMiddleware: {}, requireViewer: (viewer: Viewer) => viewer })); const repo = { id: "repo-1", owner_id: "u-owner", owner_name: "owner", name: "app", is_archived: 1 } as RepoRow; const actualRepos = await import("~/server/repos"); mock.module("~/server/repos", () => ({ ...actualRepos, requireRepoAccess: async () => ({ repo, permission: "admin" }), })); const actualLimits = await import("~/server/limits"); mock.module("~/server/limits", () => ({ ...actualLimits, limitWrite: async () => undefined, limitAccountWrite: async () => undefined, })); const calls: string[] = []; const actualTokenWrite = await import("~/server/token-write"); mock.module("~/server/token-write", () => ({ ...actualTokenWrite, createDeployToken: async () => { calls.push("mint"); return { token: "t" }; }, revokeDeployToken: async () => { calls.push("revoke"); }, })); const actualCollaborators = await import("~/server/collaborators"); mock.module("~/server/collaborators", () => ({ ...actualCollaborators, setCollaborator: async (_repo: RepoRow, _user: string, permission: string) => { calls.push(`collaborator:${permission}`); }, })); const { createDeployToken, revokeDeployToken } = await import("./tokens"); const { setCollaborator } = await import("./repos"); const { requireIntegrationScope } = await import("~/server/integrations/scope"); const owner: Viewer = { id: "u-owner", name: "owner", displayName: null, email: "o@x", isAdmin: false, emailVerified: true, }; /** Server fns are plain handlers under the builder stub above. */ function invoke(fn: unknown, data: Record): Promise { return (fn as Handler)({ data, context: { viewer: owner } }); } async function status(promise: Promise): Promise { try { await promise; return "ok"; } catch (error) { return (error as { status: number }).status; } } beforeEach(() => { calls.length = 0; }); describe("archived repositories in web server fns", () => { test("deploy tokens cannot be minted, but can still be revoked", async () => { const base = { owner: "owner", repo: "app" }; const mint = { ...base, name: "ci", permission: "read", expiresInDays: 30 }; expect(await status(invoke(createDeployToken, mint))).toBe(403); expect(await status(invoke(revokeDeployToken, { ...base, id: "d-1" }))).toBe("ok"); expect(calls).toEqual(["revoke"]); }); test("collaborators cannot be granted, but can still be removed", async () => { const base = { owner: "owner", repo: "app", username: "bob" }; expect(await status(invoke(setCollaborator, { ...base, permission: "write" }))).toBe(403); expect(await status(invoke(setCollaborator, { ...base, permission: "none" }))).toBe("ok"); expect(calls).toEqual(["collaborator:none"]); }); test("integrations cannot be added or changed, but list and delete use read scope", async () => { const ref = { owner: "owner", repo: "app" }; expect(await status(requireIntegrationScope(ref, owner))).toBe(403); expect(await status(requireIntegrationScope(ref, owner, "read"))).toBe("ok"); }); });