import { describe, expect, mock, test } from "bun:test"; import { Database } from "bun:sqlite"; import { applyCombinedSchema } from "./test-schema.test-helper"; import type { RepoRow, Viewer } from "~/lib/types"; mock.module("cloudflare:workers", () => ({ env: { PUBLIC_URL: "https://forge.example.com" }, waitUntil: () => undefined, WorkflowEntrypoint: class {}, DurableObject: class {}, WorkerEntrypoint: class {}, })); mock.module("@tanstack/react-start/server", () => ({ getRequestHeader: () => undefined })); const db = new Database(":memory:"); applyCombinedSchema(db); mock.module("./db", () => ({ first: async (sql: string, ...params: unknown[]) => db.query(sql).get(...(params as never[])) ?? null, all: async (sql: string, ...params: unknown[]) => db.query(sql).all(...(params as never[])), run: async () => ({ meta: { changes: 0 } }), batch: async () => [], stmt: (sql: string, ...params: unknown[]) => ({ sql, params }), placeholders: (count: number) => Array(count).fill("?").join(", "), chunks: (items: T[]) => [items], MAX_IN_LIST: 90, })); mock.module("./auth", () => ({ credentialFromRequest: () => null, loadActiveUserById: async () => null, PAT_PREFIX: "gf", toViewer: () => null, viewerFromSessionCookie: async () => null, })); mock.module("./usercontent-grant", () => ({ viewerFromGrant: async () => null })); const { inboxReadableSql, repoPermission, requireRepoAccess, visibleReposClause } = await import("./repos"); const { scopedRepoPermission } = await import("./token-scope"); db.exec(` INSERT INTO accounts (id, name, kind, created_at) VALUES ('owner', 'owner', 'user', 0), ('admin', 'admin', 'user', 0); INSERT INTO users (account_id, email, is_admin) VALUES ('owner', 'o@x', 0), ('admin', 'a@x', 1); INSERT INTO repos (id, owner_id, name, visibility, status, default_branch, artifacts_name, artifacts_remote, created_at, updated_at, hidden_at) VALUES ('shown', 'owner', 'shown', 'public', 'ready', 'main', 'a1', 'r1', 0, 0, NULL), ('hidden', 'owner', 'hidden', 'public', 'ready', 'main', 'a2', 'r2', 0, 0, 1); INSERT INTO notifications (id, user_id, repo_id, reason, title, url, created_at) VALUES ('n1', 'owner', 'shown', 'mention', 'ok', 'u', 0), ('n2', 'owner', 'hidden', 'mention', 'gone', 'u', 0); `); const owner: Viewer = { id: "owner", name: "owner", displayName: null, email: "o@x", isAdmin: false, emailVerified: true, }; const admin: Viewer = { id: "admin", name: "admin", displayName: null, email: "a@x", isAdmin: true, emailVerified: true, }; function row(id: string): RepoRow { return db .query("SELECT r.*, a.name AS owner_name FROM repos r JOIN accounts a ON a.id = r.owner_id WHERE r.id = ?") .get(id) as RepoRow; } describe("hidden repositories", () => { test("web: anonymous visitors and the owner get 404, site admins keep access", async () => { expect(await repoPermission(row("hidden"), null)).toBe("none"); expect(await repoPermission(row("hidden"), owner)).toBe("none"); expect(await repoPermission(row("hidden"), admin)).toBe("admin"); await expect(requireRepoAccess("owner", "hidden", owner, "read")).rejects.toThrow("Repository not found"); expect((await requireRepoAccess("owner", "shown", null, "read")).permission).toBe("read"); }); test("API and git: personal and deploy tokens get nothing", async () => { const pat = { viewer: owner, grant: { kind: "fine" as const, tokenId: "t", allRepos: true, permissions: { contents: "write" as const } }, }; const deploy = { viewer: null, grant: { kind: "deploy" as const, tokenId: "d", repoId: "hidden", permission: "write" as const }, }; expect(await scopedRepoPermission(row("hidden"), pat, "contents")).toBe("none"); expect(await scopedRepoPermission(row("hidden"), deploy, "contents")).toBe("none"); expect(await scopedRepoPermission(row("hidden"), { viewer: null, grant: null }, "contents")).toBe("none"); expect(await scopedRepoPermission(row("shown"), { viewer: null, grant: null }, "contents")).toBe("read"); }); test("listings and the inbox leave hidden repositories out", () => { const { clause, viewerId } = visibleReposClause(owner); const names = db.query(`SELECT r.name FROM repos r WHERE ${clause}`).all(viewerId); expect(names).toEqual([{ name: "shown" }]); const inbox = db .query(`SELECT n.id FROM notifications n WHERE n.user_id = ? AND ${inboxReadableSql("n")}`) .all("owner"); expect(inbox).toEqual([{ id: "n1" }]); }); });