import { describe, expect, it, vi } from "vitest";
import {
account,
githubEnv,
landingFrom,
landingHref,
open,
seal
} from "#lib/server/github/index.ts";
const env = {
...githubEnv(),
publicOrigin: "https://github.com",
apiOrigin: "https://api.github.com"
};
const publicRepo = (id: number) => ({
id,
name: "pub",
full_name: "ada/pub",
visibility: "public",
clone_url: "https://github.com/ada/pub.git",
html_url: "https://github.com/ada/pub",
default_branch: "main"
});
const privateRepo = (id: number) => ({
...publicRepo(id),
name: "priv",
full_name: "ada/priv",
visibility: "private",
private: true,
clone_url: "https://github.com/ada/priv.git",
html_url: "https://github.com/ada/priv"
});
const linked = (body: unknown, next?: string) =>
Response.json(body, next ? { headers: { link: `<${next}>; rel="next"` } } : undefined);
describe("github oauth boundaries", () => {
it("rejects tampered sealed state", async () => {
const value = await seal({ exp: Date.now() - 1 }, "test secret");
expect(await open(value, "test secret")).toEqual({ exp: expect.any(Number) });
expect(await open(value + "x", "test secret")).toBeNull();
});
it.each([
["/repo/import/github", "/repo/import/github"],
["/repo/migrate", "/repo/migrate"],
["/repo/migrate?knot=at&github_error=denied", "/repo/migrate?knot=at"],
["/repo/new?import=github", "/repo/new?import=github"],
["/welcome?step=github", "/welcome?step=github"],
["https://evil.example/", "/?github_error=return_unexpected"],
["//evil.example/", "/?github_error=return_unexpected"],
["/\\evil.example", "/?github_error=return_unexpected"],
["/settings/emails", "/?github_error=return_unexpected"],
[null, "/"]
])("lands %s at %s", (raw, expected) => {
expect(landingHref(landingFrom(raw))).toBe(expected);
});
it("carries a failure notice onto the page that asked", () => {
expect(landingHref(landingFrom("/repo/migrate"), "denied")).toBe(
"/repo/migrate?github_error=denied"
);
});
it("authenticates every paginated repository request and follows the link cursor", async () => {
const seen: { path: string; auth: string | null }[] = [];
const fetcher = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const u = new URL(String(input));
seen.push({ path: u.pathname, auth: new Headers(init?.headers).get("authorization") });
const second = u.searchParams.get("cursor") === "2";
if (u.pathname === "/user")
return Response.json({
login: "ada",
avatar_url: "https://avatars.githubusercontent.com/u/1"
});
if (u.pathname === "/user/emails")
return linked([{ primary: true, verified: true, email: "a@example.test" }]);
if (u.pathname === "/user/keys")
return linked([{ id: 1, key: "ssh-ed25519 AAA", title: "x" }]);
if (u.pathname === "/users/ada/repos") {
expect(u.searchParams.get("type")).toBe("owner");
return second
? linked([publicRepo(101)])
: linked(
Array.from({ length: 100 }, (_, i) => publicRepo(i)),
"https://api.github.com/users/ada/repos?type=owner&cursor=2"
);
}
if (u.pathname === "/user/installations") return linked({ installations: [{ id: 7 }] });
if (u.pathname === "/user/installations/7/repositories")
return linked({ repositories: [publicRepo(101), privateRepo(102)] });
return linked([]);
});
const result = await account(env, "secret", fetcher);
expect(result).toMatchObject({ login: "ada", email: "a@example.test", keys: [{ id: 1 }] });
expect(result.repos).toContainEqual(
expect.objectContaining({ id: 101, fullName: "ada/pub", private: false })
);
expect(result.repos).toContainEqual(
expect.objectContaining({ id: 102, private: true, installationId: 7 })
);
expect(result.repos).toHaveLength(102);
expect(seen.find((s) => s.path === "/users/ada/repos")?.auth).toBe("Bearer secret");
expect(seen.find((s) => s.path === "/user/installations")?.auth).toBe("Bearer secret");
expect(fetcher.mock.calls.some(([input]) => String(input).includes("cursor=2"))).toBe(true);
});
it("refuses a link cursor that walks off the api origin", async () => {
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({ login: "ada", avatar_url: "https://a" });
if (u.pathname === "/user/emails")
return linked([], "https://evil.example/user/emails?page=2");
return linked([]);
});
await expect(account(env, "secret", fetcher)).rejects.toThrow(/malformed/);
expect(fetcher.mock.calls.some(([input]) => String(input).includes("evil.example"))).toBe(
false
);
});
it("still reports the account when nothing is installed", async () => {
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({ login: "ada", avatar_url: "https://a" });
if (u.pathname === "/users/ada/repos") return linked([publicRepo(1)]);
if (u.pathname === "/user/installations")
return linked({ total_count: 0, installations: [] });
return linked([]);
});
const result = await account(env, "secret", fetcher);
expect(result.repos).toEqual([expect.objectContaining({ id: 1, private: false })]);
expect(result.repos[0].installationId).toBeUndefined();
expect(result.emails).toEqual([]);
});
it("sorts owned and granted repositories into one list by name", async () => {
const named = (id: number, name: string, base = publicRepo) => ({
...base(id),
name,
full_name: `ada/${name}`
});
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({ login: "ada", avatar_url: "https://a" });
if (u.pathname === "/users/ada/repos")
return linked([named(1, "webring"), named(2, "dots")]);
if (u.pathname === "/user/installations") return linked({ installations: [{ id: 7 }] });
if (u.pathname === "/user/installations/7/repositories")
return linked({ repositories: [named(3, "migtest", privateRepo)] });
return linked([]);
});
const result = await account(env, "secret", fetcher);
expect(result.repos.map((r) => r.name)).toEqual(["dots", "migtest", "webring"]);
});
it("carries the profile fields the welcome form can prefill", async () => {
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({
login: "ada",
avatar_url: "https://a",
bio: "builds things",
blog: "ada.example.test"
});
if (u.pathname === "/user/installations")
return linked({ total_count: 0, installations: [] });
return linked([]);
});
const result = await account(env, "secret", fetcher);
expect([result.bio, result.blog]).toEqual(["builds things", "ada.example.test"]);
});
it("collects every email and skips malformed rows without throwing", async () => {
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({ login: "ada", avatar_url: "https://a" });
if (u.pathname === "/user/emails")
return linked([
{ email: "a@example.test", primary: true, verified: true },
{ email: 42, primary: true, verified: true },
{ email: "b@example.test", verified: "yes", primary: false },
{ primary: true, verified: true },
{ email: "c@example.test" }
]);
if (u.pathname === "/user/installations")
return linked({ total_count: 0, installations: [] });
return linked([]);
});
const result = await account(env, "secret", fetcher);
expect(result.emails).toEqual([
{ address: "a@example.test", primary: true, verified: true },
{ address: "b@example.test", primary: false, verified: false },
{ address: "c@example.test", primary: false, verified: false }
]);
expect(result.email).toBe("a@example.test");
});
});
describe("repo metadata", () => {
const full = {
...publicRepo(1),
language: "Rust",
stargazers_count: 42,
open_issues_count: 3,
forks_count: 7,
size: 21504,
pushed_at: "2026-09-14T12:00:00Z"
};
const accountOf = (owned: unknown[]) => {
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const u = new URL(String(input));
if (u.pathname === "/user")
return Response.json({ login: "ada", avatar_url: "https://a" });
if (u.pathname === "/users/ada/repos") return linked(owned);
if (u.pathname === "/user/installations")
return linked({ total_count: 0, installations: [] });
return linked([]);
});
return account(env, "secret", fetcher).then((a) => a.repos[0]);
};
it("maps repo metadata through when present", async () => {
expect(await accountOf([full])).toMatchObject({
language: "Rust",
stars: 42,
issues: 3,
forks: 7,
sizeKb: 21504,
updatedAt: "2026-09-14T12:00:00Z"
});
});
it("maps absent metadata to nulls without throwing", async () => {
expect(await accountOf([publicRepo(1)])).toMatchObject({
language: null,
stars: null,
issues: null,
forks: null,
updatedAt: null,
sizeKb: null
});
});
it("nulls out wrong-typed metadata instead of throwing", async () => {
expect(await accountOf([{ ...full, stargazers_count: "lots" }])).toMatchObject({
stars: null,
language: "Rust"
});
});
it("prefers pushed_at for updatedAt", async () => {
expect(await accountOf([{ ...full, updated_at: "2026-09-10T00:00:00Z" }])).toMatchObject({
updatedAt: "2026-09-14T12:00:00Z"
});
});
});