feat: add extensive test suite

This commit is contained in:
2026-08-30 11:33:53 +07:00 Unverified
parent 061cce00dc
commit 8359c7272e
131 changed files with 8717 additions and 2200 deletions
+130
View File
@@ -0,0 +1,130 @@
import { describe, expect, mock, test } from "bun:test";
import { createRubgramCallbackHandler } from "./handler";
const config = {
clientId: "client",
clientSecret: "secret",
redirectUri: "https://buzz.test/rubgram/callback",
guildId: "guild",
botToken: "bot",
};
function dependencies() {
const responses = [
Response.json({ access_token: "access" }),
Response.json({ id: "user-1", username: "user", global_name: "Display" }),
new Response(null, { status: 200 }),
];
return {
config,
fetch: mock(
async (_input: RequestInfo | URL, _init?: RequestInit) =>
responses.shift()!,
),
getState: mock((): string | undefined => "expected"),
clearState: mock(() => {}),
setSession: mock((_token: string) => {}),
saveUser: mock(
async (_user: { uid: string; display: string; username: string }) =>
"session",
),
redirect: mock(
(path: string) =>
new Response(null, { status: 307, headers: { Location: path } }),
),
logError: mock((_message: string, _error?: unknown) => {}),
};
}
const request = (query: string) =>
new Request(`https://buzz.test/rubgram/callback?${query}`);
describe("Rubgram Discord callback", () => {
test("redirects an OAuth denial without exchanging a token", async () => {
const deps = dependencies();
const response = await createRubgramCallbackHandler(deps)(
request("error=access_denied&state=expected"),
);
expect(response.status).toBe(307);
expect(deps.clearState).toHaveBeenCalledTimes(1);
expect(deps.fetch).not.toHaveBeenCalled();
});
test("requires an authorization code", async () => {
const deps = dependencies();
const response = await createRubgramCallbackHandler(deps)(
request("state=expected"),
);
expect(response.status).toBe(400);
expect(await response.json()).toEqual({ error: "No code provided" });
});
test("rejects missing, mismatched, and unissued OAuth state", async () => {
for (const [query, expected] of [
["code=abc", "expected"],
["code=abc&state=wrong", "expected"],
["code=abc&state=expected", undefined],
] as const) {
const deps = dependencies();
deps.getState.mockReturnValue(expected);
const response = await createRubgramCallbackHandler(deps)(request(query));
expect(response.status).toBe(400);
expect(await response.json()).toEqual({ error: "Invalid OAuth state" });
expect(deps.clearState).toHaveBeenCalledTimes(1);
expect(deps.fetch).not.toHaveBeenCalled();
}
});
test("exchanges a valid callback, stores the user, and sets the session", async () => {
const deps = dependencies();
const response = await createRubgramCallbackHandler(deps)(
request("code=abc&state=expected"),
);
expect(response.status).toBe(307);
expect(deps.fetch).toHaveBeenCalledTimes(3);
expect(deps.fetch.mock.calls[0]?.[0]).toBe(
"https://discord.com/api/oauth2/token",
);
const tokenInit = deps.fetch.mock.calls[0]?.[1];
expect(tokenInit?.method).toBe("POST");
expect((tokenInit?.body as URLSearchParams).get("code")).toBe("abc");
expect(deps.saveUser).toHaveBeenCalledWith({
uid: "user-1",
username: "user",
display: "Display",
});
expect(deps.setSession).toHaveBeenCalledWith("session");
expect(deps.redirect).toHaveBeenCalledWith("/rubgram");
});
test("adds a user who is not already a guild member", async () => {
const deps = dependencies();
deps.fetch
.mockResolvedValueOnce(Response.json({ access_token: "access" }))
.mockResolvedValueOnce(Response.json({ id: "user-1", username: "user" }))
.mockResolvedValueOnce(new Response(null, { status: 404 }))
.mockResolvedValueOnce(new Response(null, { status: 201 }));
await createRubgramCallbackHandler(deps)(
request("code=abc&state=expected"),
);
expect(deps.fetch).toHaveBeenCalledTimes(4);
expect(deps.fetch.mock.calls[3]?.[0]).toBe(
"https://discord.com/api/guilds/guild/members/user-1",
);
expect(deps.fetch.mock.calls[3]?.[1]?.method).toBe("PUT");
});
test("returns a stable error without storing a user when Discord fails", async () => {
const deps = dependencies();
deps.fetch.mockReset();
deps.fetch.mockResolvedValue(new Response("bad", { status: 401 }));
const response = await createRubgramCallbackHandler(deps)(
request("code=abc&state=expected"),
);
expect(response.status).toBe(500);
expect(await response.json()).toEqual({ error: "Authentication failed" });
expect(deps.saveUser).not.toHaveBeenCalled();
expect(deps.setSession).not.toHaveBeenCalled();
expect(deps.logError).toHaveBeenCalledTimes(1);
});
});