73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { clampLimit, getHomeFeedPage } from "@/lib/feed";
|
|
import { createSession } from "@/lib/session-store";
|
|
|
|
const json = (body: unknown, status = 200) =>
|
|
new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
|
|
let sid: string;
|
|
beforeEach(() => {
|
|
sid = createSession({
|
|
profileAccessToken: "p",
|
|
profileAccessTokenExpiresAt: null,
|
|
profileRefreshToken: "r",
|
|
feedAccessToken: "FEEDTOK",
|
|
feedAccessTokenExpiresAt: null,
|
|
person: { userId: "u", name: "Ann", email: "a@b.co", pictureUrl: null },
|
|
createdAt: Date.now(),
|
|
});
|
|
});
|
|
|
|
describe("getHomeFeedPage", () => {
|
|
it("no session is sign_in and nothing is fetched", async () => {
|
|
const f = vi.fn();
|
|
expect(await getHomeFeedPage(undefined, null, 20, { fetchImpl: f as unknown as typeof fetch }))
|
|
.toEqual({ ok: false, status: 401, error: "sign_in" });
|
|
expect(f).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("first page: items + nextCursor, no cursor upstream, bearer token, home path", async () => {
|
|
const f = vi.fn(async () => json({ data: [{ id: 1 }], metadata: { nextCursor: "C1" } }));
|
|
const r = await getHomeFeedPage(sid, null, 20, { fetchImpl: f as unknown as typeof fetch });
|
|
expect(r).toEqual({ ok: true, items: [{ id: 1 }], nextCursor: "C1" });
|
|
const [url, init] = f.mock.calls[0] as unknown as [string, RequestInit];
|
|
const u = new URL(url);
|
|
expect(u.pathname).toBe("/api/v1/feeds/home/items");
|
|
expect(u.searchParams.get("limit")).toBe("20");
|
|
expect(u.searchParams.has("cursor")).toBe(false);
|
|
expect((init.headers as Record<string, string>).Authorization).toBe("Bearer FEEDTOK");
|
|
});
|
|
|
|
it("forwards the cursor on a later page; null nextCursor at the end", async () => {
|
|
const f = vi.fn(async () => json({ data: [], metadata: { nextCursor: null } }));
|
|
const r = await getHomeFeedPage(sid, "X", 20, { fetchImpl: f as unknown as typeof fetch });
|
|
expect(r).toEqual({ ok: true, items: [], nextCursor: null });
|
|
const u = new URL((f.mock.calls[0] as unknown as [string])[0]);
|
|
expect(u.searchParams.get("cursor")).toBe("X");
|
|
});
|
|
|
|
it("feed 401 twice is sign_in", async () => {
|
|
const f = vi.fn(async (url: string) =>
|
|
String(url).includes("/feeds/home/items") ? json({}, 401) : json({}, 500),
|
|
);
|
|
const r = await getHomeFeedPage(sid, null, 20, { fetchImpl: f as unknown as typeof fetch });
|
|
expect(r).toEqual({ ok: false, status: 401, error: "sign_in" });
|
|
});
|
|
|
|
it("upstream 500 is feed_unavailable", async () => {
|
|
const f = vi.fn(async () => json({}, 500));
|
|
const r = await getHomeFeedPage(sid, null, 20, { fetchImpl: f as unknown as typeof fetch });
|
|
expect(r).toEqual({ ok: false, status: 502, error: "feed_unavailable" });
|
|
});
|
|
});
|
|
|
|
describe("clampLimit", () => {
|
|
it("defaults to 20, caps at 50", () => {
|
|
expect(clampLimit(null)).toBe(20);
|
|
expect(clampLimit("abc")).toBe(20);
|
|
expect(clampLimit("999")).toBe(50);
|
|
expect(clampLimit("10")).toBe(10);
|
|
expect(clampLimit("0")).toBe(20);
|
|
});
|
|
});
|