Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229import { isRedirect } from "@sveltejs/kit";import type { ResolveOptions } from "@sveltejs/kit/hooks";import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";import { handle } from "./hooks.server";
const config = vi.hoisted(() => ({ sitemapUrl: "", bobbinUrl: "https://bobbin.test", avatarUrl: "https://avatar.test", camoUrl: "https://camo.test"}));vi.mock("#lib/server/config.ts", () => ({ getConfig: () => config }));const repos = vi.hoisted(() => ({ resolveRepoByRepoDid: vi.fn() }));vi.mock("#lib/api/repo.ts", () => repos);
const run = ( path: string, binding?: (request: Request) => Promise<Response>, routeId: string | null = null) => { const resolve = vi.fn(async () => new Response("page")); const url = new URL(`https://next.tangled.org${path}`); const event = { url, route: { id: routeId }, fetch: vi.fn(), request: new Request(url), platform: binding ? { env: { SITEMAP: { fetch: binding } } } : undefined }; return { res: handle({ event, resolve } as unknown as Parameters<typeof handle>[0]), resolve };};
beforeEach(() => { config.sitemapUrl = ""; repos.resolveRepoByRepoDid.mockReset().mockResolvedValue(null); vi.spyOn(console, "warn").mockImplementation(() => {});});afterEach(() => vi.unstubAllGlobals());
describe("handle: legacy redirects", () => { it.each([ ["/@alice.test", 302, "/alice.test"], ["/@alice.test/core/tree/main?x=1", 302, "/alice.test/core/tree/main?x=1"], ["/%40alice.test/core", 302, "/alice.test/core"], [ "/@tangled.org/core/info/refs?service=git-upload-pack", 302, "/tangled.org/core/info/refs?service=git-upload-pack" ], ["/tangled.sh", 301, "/tangled.org"], ["/@tangled.sh", 301, "/tangled.org"], ["/tangled.sh/core/issues?state=open", 301, "/tangled.org/core/issues?state=open"], ["/@tangled.sh/core", 301, "/tangled.org/core"] ])("sends %s to %s %s", async (path, status, location) => { const { res, resolve } = run(path); const out = await res; expect([out.status, out.headers.get("location")]).toEqual([status, location]); expect(resolve).not.toHaveBeenCalled(); });
it.each([ "/tangled.shop/core", "/tangled.org/core", "/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/info/refs", "/@did:plc:j5hmlfdrwkvtxm7cjmu7j2is", "/@", "/@not a handle", "/settings", "/login", "/alice.test/@core", "/%E0%A4%A/core", "/%zz" ])("leaves %s to the routes", async (path) => { const { res, resolve } = run(path); await res; expect(resolve).toHaveBeenCalled(); });});
describe("handle: repo did permalinks", () => { const repoDid = "did:plc:j5hmlfdrwkvtxm7cjmu7j2is"; const redirected = async (res: ReturnType<typeof handle>) => { try { await res; } catch (thrown) { if (isRedirect(thrown)) return [thrown.status, thrown.location]; throw thrown; } return null; };
beforeEach(() => { repos.resolveRepoByRepoDid.mockImplementation(async (_, did: string) => did === repoDid ? { owner: "tangled.org", name: "core" } : null ); });
it.each([ ["", "/tangled.org/core"], ["/issues", "/tangled.org/core/issues"], ["/issues/12", "/tangled.org/core/issues/12"], ["/tree/main/web/src?x=1", "/tangled.org/core/tree/main/web/src?x=1"] ])("sends the repo did%s to the repo", async (rest, location) => { const { res, resolve } = run(`/${repoDid}${rest}`); expect(await redirected(res)).toEqual([307, location]); expect(resolve).not.toHaveBeenCalled(); });
it.each(["/did:plc:aaaaaaaaaaaaaaaaaaaaaaaa", "/did:plc:aaaaaaaaaaaaaaaaaaaaaaaa/core"])( "leaves %s, a did that isn't a repo, to the routes", async (path) => { const { res, resolve } = run(path); expect(await redirected(res)).toBeNull(); expect(resolve).toHaveBeenCalled(); } );
it("leaves git's info/refs at the permalink to its route without a lookup", async () => { const { res, resolve } = run(`/${repoDid}/info/refs`, undefined, "/[handle]/info/refs"); expect(await redirected(res)).toBeNull(); expect(resolve).toHaveBeenCalled(); expect(repos.resolveRepoByRepoDid).not.toHaveBeenCalled(); });
it("doesn't look up a handle", async () => { const { res } = run("/tangled.org/core"); await res; expect(repos.resolveRepoByRepoDid).not.toHaveBeenCalled(); });});
describe("handle: sitemaps", () => { it("prefers the worker binding", async () => { config.sitemapUrl = "https://sitemap.test"; const binding = vi.fn(async () => new Response("bound")); const network = vi.fn(); vi.stubGlobal("fetch", network); const { res } = run("/sitemap.xml", binding); await expect((await res).text()).resolves.toBe("bound"); expect(network).not.toHaveBeenCalled(); });
it.each(["/sitemap.xml", "/sitemaps/repos-3.xml?v=2"])( "fetches %s from SITEMAP_URL without a binding, dropping the decoded encoding", async (path) => { config.sitemapUrl = "https://sitemap.test"; const network = vi.fn( async (_: string) => new Response("<urlset/>", { headers: { "content-type": "application/xml", "content-encoding": "gzip", "content-length": "9" } }) ); vi.stubGlobal("fetch", network); const { res, resolve } = run(path); const out = await res; expect(network).toHaveBeenCalledWith(`https://sitemap.test${path}`); expect(resolve).not.toHaveBeenCalled(); await expect(out.text()).resolves.toBe("<urlset/>"); expect(out.headers.get("content-type")).toBe("application/xml"); expect(out.headers.get("content-encoding")).toBeNull(); expect(out.headers.get("content-length")).toBeNull(); } );
it("falls through to the app with neither", async () => { const network = vi.fn(); vi.stubGlobal("fetch", network); const { res, resolve } = run("/sitemap.xml"); await expect((await res).text()).resolves.toBe("page"); expect(resolve).toHaveBeenCalledOnce(); expect(network).not.toHaveBeenCalled(); });});
describe("handle: security headers", () => { const page = async (response: Response) => { const url = new URL("https://next.tangled.org/someone/repo"); const event = { url, request: new Request(url), platform: undefined }; const resolve = vi.fn(async () => response); return handle({ event, resolve } as unknown as Parameters<typeof handle>[0]); };
it("adds them to a rendered response", async () => { const res = await page(new Response("page")); expect(Object.fromEntries(res.headers)).toMatchObject({ "x-content-type-options": "nosniff", "referrer-policy": "strict-origin-when-cross-origin", "x-frame-options": "DENY", "content-security-policy": "frame-ancestors 'none'" }); await expect(res.text()).resolves.toBe("page"); });
it("keeps a header the route set itself", async () => { const own = "sandbox; default-src 'none'"; const res = await page( new Response("raw", { headers: { "content-security-policy": own } }) ); expect(res.headers.get("content-security-policy")).toBe(own); expect(res.headers.get("x-frame-options")).toBe("DENY"); });
it("copies a response whose headers can't be changed", async () => { const res = await page(Response.redirect("https://cdn.example.com/a", 302)); expect(res.status).toBe(302); expect(res.headers.get("location")).toBe("https://cdn.example.com/a"); expect(res.headers.get("x-content-type-options")).toBe("nosniff"); });});
describe("handle: page head", () => { it("preconnects to the configured avatar and camo services", async () => { const url = new URL("https://next.tangled.org/someone/repo"); const event = { url, request: new Request(url), platform: undefined }; const resolve = vi.fn(async (_: unknown, opts: ResolveOptions) => { const html = '<link href="%tangled.avatar%" /><link href="%tangled.camo%" />'; return new Response(await opts.transformPageChunk?.({ html, done: true })); }); const res = await handle({ event, resolve } as unknown as Parameters<typeof handle>[0]); await expect(res.text()).resolves.toBe( '<link href="https://avatar.test" /><link href="https://camo.test" />' ); });});