Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184import { isHttpError } from "@sveltejs/kit";import { beforeEach, describe, expect, it, vi } from "vitest";import { ClientResponseError } from "#lib/api/client.ts";import { redirectRepoDidToKnot, redirectToKnot } from "#lib/server/gitHttp.ts";
const REPO_DID = "did:plc:j5hmlfdrwkvtxm7cjmu7j2is";const OWNER_DID = "did:plc:wshs7t2adsemcrrd4snkeqli";
const mocks = vi.hoisted(() => ({ resolveRepoByName: vi.fn(), getRepoByRepoDid: vi.fn()}));vi.mock("#lib/server/config.ts", () => ({ getConfig: () => ({ bobbinUrl: "https://bobbin.test", posthogApiKey: "phc_test", posthogHost: "https://posthog.test", posthogEnv: "test" })}));vi.mock("#lib/api/identity.ts", () => ({ resolveMiniDoc: async () => ({ did: OWNER_DID, handle: "tangled.org" })}));vi.mock("#lib/api/repo.ts", () => ({ resolveRepoByName: mocks.resolveRepoByName }));vi.mock("#lib/api/records.ts", () => ({ getRepoByRepoDid: mocks.getRepoByRepoDid }));
const repoView = (knot?: string) => ({ uri: `at://${REPO_DID}/org.tangled.repo.manifest/self`, knot, value: { declaration: { owner: OWNER_DID, name: "core" } }});
const event = <P>(params: P, path: string) => ({ params, url: new URL(`https://tangled.test${path}`), fetch: vi.fn<typeof fetch>()});
const statusOf = (promise: Promise<unknown>) => promise.then( () => "resolved", (cause) => (isHttpError(cause) ? cause.status : cause) );
const captured = vi.fn<typeof fetch>();
beforeEach(() => { mocks.resolveRepoByName.mockReset().mockResolvedValue(repoView("knot1.tangled.sh")); mocks.getRepoByRepoDid.mockReset().mockResolvedValue(repoView("knot1.tangled.sh")); captured.mockReset().mockResolvedValue(new Response("{}")); vi.stubGlobal("fetch", captured);});
const sentEvents = () => captured.mock.calls.map( ([, init]) => JSON.parse(String(init?.body)) as Record<string, unknown> );
describe("clone tracking", () => { it.each([ [ "by name", () => redirectToKnot("info/refs")( event( { handle: "tangled.org", repo: "core" }, "/tangled.org/core/info/refs?service=git-upload-pack" ) ) ], [ "by repo did", () => redirectRepoDidToKnot("info/refs")( event({ handle: REPO_DID }, `/${REPO_DID}/info/refs?service=git-upload-pack`) ) ] ])("counts an upload-pack ref advertisement %s against the owner", async (_, run) => { await run(); expect(sentEvents()).toEqual([ expect.objectContaining({ event: "clone", distinct_id: OWNER_DID, properties: { repo: "core", repo_at: `at://${REPO_DID}/org.tangled.repo.manifest/self`, environment: "test" } }) ]); });
it.each([ ["a push", "info/refs", "?service=git-receive-pack"], ["the pack itself", "git-upload-pack", ""], ["an archive", "info/refs", "?service=git-upload-archive"] ])("leaves %s uncounted", async (_, service, search) => { await redirectToKnot(service)( event({ handle: "tangled.org", repo: "core" }, `/tangled.org/core/${service}${search}`) ); expect(captured).not.toHaveBeenCalled(); });});
describe("redirectToKnot", () => { it.each([ ["info/refs", "?service=git-upload-pack"], ["info/refs", "?service=git-receive-pack"], ["git-upload-pack", ""], ["git-receive-pack", ""] ])("sends %s%s to the repo did on its knot", async (service, search) => { const res = await redirectToKnot(service)( event({ handle: "tangled.org", repo: "core" }, `/tangled.org/core/${service}${search}`) ); expect(res.status).toBe(307); expect(res.headers.get("location")).toBe( `https://knot1.tangled.sh/${REPO_DID}/${service}${search}` ); });
it("drops a .git suffix from the repo name", async () => { await redirectToKnot("info/refs")( event({ handle: "tangled.org", repo: "core.git" }, "/tangled.org/core.git/info/refs") ); expect(mocks.resolveRepoByName).toHaveBeenCalledWith(expect.anything(), OWNER_DID, "core"); });
it("follows a did:web knot to its host", async () => { mocks.resolveRepoByName.mockResolvedValue(repoView("did:web:knot.example.com")); const res = await redirectToKnot("info/refs")( event({ handle: "tangled.org", repo: "core" }, "/tangled.org/core/info/refs") ); expect(res.headers.get("location")).toBe(`https://knot.example.com/${REPO_DID}/info/refs`); });
it.each([ ["the repo does not exist", null], ["the repo has no knot", repoView()] ])("answers 404 when %s", async (_, view) => { mocks.resolveRepoByName.mockResolvedValue(view); const handler = redirectToKnot("info/refs"); await expect( statusOf( handler( event({ handle: "tangled.org", repo: "nope" }, "/tangled.org/nope/info/refs") ) ) ).resolves.toBe(404); });});
describe("redirectRepoDidToKnot", () => { it("sends the repo did permalink to its knot", async () => { const res = await redirectRepoDidToKnot("info/refs")( event({ handle: REPO_DID }, `/${REPO_DID}/info/refs?service=git-upload-pack`) ); expect(res.status).toBe(307); expect(res.headers.get("location")).toBe( `https://knot1.tangled.sh/${REPO_DID}/info/refs?service=git-upload-pack` ); });
it("answers 404 for a handle without looking it up", async () => { const handler = redirectRepoDidToKnot("info/refs"); await expect( statusOf(handler(event({ handle: "tangled.org" }, "/tangled.org/info/refs"))) ).resolves.toBe(404); expect(mocks.getRepoByRepoDid).not.toHaveBeenCalled(); });
it.each([ [404, "RepoNotFound"], [503, "Overloaded"] ])("answers %i when looking up the did fails with %s", async (status, error) => { mocks.getRepoByRepoDid.mockRejectedValue( new ClientResponseError({ status, data: { error } }) ); const handler = redirectRepoDidToKnot("info/refs"); await expect( statusOf(handler(event({ handle: OWNER_DID }, `/${OWNER_DID}/info/refs`))) ).resolves.toBe(status); });});