import { describe, expect, it } from "bun:test";
import { readFileSync } from "fs";
const SRC = readFileSync("src/routes/api-v2.ts", "utf8");
describe("api-v2 repo privacy gate", () => {
it("is mounted on the whole /repos/:owner/:repo subtree", () => {
expect(SRC).toContain('apiv2.use("/repos/:owner/:repo", repoPrivacyGate)');
expect(SRC).toContain('apiv2.use("/repos/:owner/:repo/*", repoPrivacyGate)');
});
it("denies with 404, never 403 — a private repo must not confirm it exists", () => {
const gate = SRC.slice(
SRC.indexOf("async function repoPrivacyGate"),
SRC.indexOf("// ─── Helper")
);
expect(gate).toContain('access === "none"');
expect(gate).toContain('c.json({ error: "Not found" }, 404)');
expect(gate).not.toMatch(/,\s*403\s*\)/);
});
it("uses resolveRepoAccess, not an ownerId comparison", () => {
const gate = SRC.slice(
SRC.indexOf("async function repoPrivacyGate"),
SRC.indexOf("// ─── Helper")
);
expect(gate).toContain("resolveRepoAccess");
expect(gate).not.toMatch(/user\.id\s*!==\s*\w*[Oo]wner/);
});
it("runs after apiAuth so the viewer identity is populated", () => {
expect(SRC.indexOf('apiv2.use("*", apiAuth)')).toBeLessThan(
SRC.indexOf('apiv2.use("/repos/:owner/:repo", repoPrivacyGate)')
);
});
it("lets unknown repos fall through to each handler's own 404", () => {
const gate = SRC.slice(
SRC.indexOf("async function repoPrivacyGate"),
SRC.indexOf("// ─── Helper")
);
expect(gate).toContain("if (!resolved) return next()");
});
});
describe("repoExists is not a privacy check", () => {
it("never consults the repositories table", () => {
const repo = readFileSync("src/git/repository.ts", "utf8");
const fn = repo.slice(
repo.indexOf("export async function repoExists"),
repo.indexOf("export async function repoExists") + 400
);
expect(fn).not.toContain("isPrivate");
expect(fn).not.toContain("repositories");
});
});
|