Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343import { describe, expect, it, vi } from "vitest";import { createBobbinClient } from "$lib/api/client";import { TYPE_MINIDOC } from "$lib/api/enrich";import { fetchPeoplePage, fetchPinned, fetchProfileActivity, fetchReposPage, fetchStarredPage} from "$lib/components/profile/pages";const enriched = () => Response.json({ output: { items: [], hits: [] }, data: {} });
const requestBody = (fetchMock: ReturnType<typeof vi.fn>, index = 0) => { const init = fetchMock.mock.calls[index][1] as RequestInit; return JSON.parse(String(init.body)) as { xrpc: string; enrich: { source: string; type: string; targets?: string[] }[]; };};
describe("fetchStarredPage", () => { it("only requests minidocs for string URI subjects", async () => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( Response.json({ output: { items: [] }, data: {} }) ); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
await fetchStarredPage(ctx, { did: "did:plc:alice" });
const body = requestBody(fetchMock); expect(body.enrich[0].targets).toEqual(["items[].value.subject.uri"]); });
it("targets repo stats and owner minidocs independently", async () => { const fetchMock = vi .fn<typeof globalThis.fetch>() .mockResolvedValueOnce( Response.json({ output: { items: [ { uri: "at://did:plc:alice/sh.tangled.feed.star/one", value: { subject: { did: "did:plc:repo" }, createdAt: "2026-08-01T00:00:00Z" } } ] }, data: {} }) ) .mockResolvedValueOnce(Response.json({ output: { items: [] }, data: {} })); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
await fetchStarredPage(ctx, { did: "did:plc:alice" });
const body = requestBody(fetchMock, 1); expect(body.enrich).toEqual([ { source: "sh.tangled.feed.star:subject", type: "sh.tangled.query.enrichResponse#count", targets: ["items[].value.repoDid"] }, { source: "sh.tangled.repo:.repo", type: "blue.microcosm.identity.resolveMiniDoc", targets: ["items[].uri"] } ]); });
it.each([ [undefined, "items[].value.repoDid"], ["needle", "hits[].value.repoDid"] ])("targets repo stats for list/search results", async (q, expectedTarget) => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue(enriched()); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
await fetchReposPage(ctx, { did: "did:plc:alice", handle: "alice.test", viewerDid: "did:plc:viewer", q });
const body = requestBody(fetchMock); expect(body.enrich).toHaveLength(2); expect(body.enrich.every((descriptor) => descriptor.targets?.[0] === expectedTarget)).toBe( true ); });
it.each([ ["followers" as const, "items[].uri"], ["following" as const, "items[].value.subject"] ])("targets %s identities and stats", async (direction, expectedTarget) => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue(enriched()); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
await fetchPeoplePage(ctx, { did: "did:plc:alice", viewerDid: "did:plc:viewer", direction });
const body = requestBody(fetchMock); expect(body.enrich).toHaveLength(4); expect(body.enrich.every((descriptor) => descriptor.targets?.[0] === expectedTarget)).toBe( true ); });
it("does not retry missing enriched identities", async () => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( Response.json({ output: { items: [ { uri: "at://did:plc:bob/sh.tangled.graph.follow/one", value: { subject: "did:plc:alice", createdAt: "2026-08-01T00:00:00Z" } } ] }, data: {} }) ); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
const page = await fetchPeoplePage(ctx, { did: "did:plc:alice", direction: "followers" });
expect(fetchMock).toHaveBeenCalledTimes(2); expect(page.items[0].handle).toBe("handle.invalid"); });
it("targets repo dids for both pinned repo queries", async () => { const fetchMock = vi .fn<typeof globalThis.fetch>() .mockImplementation(async () => enriched()); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
await fetchPinned(ctx, { keys: ["did:plc:repo", "at://did:plc:alice/sh.tangled.repo/example"], did: "did:plc:alice", handle: "alice.test", viewerDid: "did:plc:viewer" });
const bodies = fetchMock.mock.calls.map((_, index) => requestBody(fetchMock, index)); expect(bodies.map((body) => body.xrpc).sort()).toEqual([ "sh.tangled.repo.getRepos", "sh.tangled.repo.getReposByRepoDids" ]); expect( bodies.every((body) => body.enrich.every((descriptor) => descriptor.type === TYPE_MINIDOC ? descriptor.targets?.[0] === "items[].uri" : descriptor.targets?.[0] === "items[].value.repoDid" ) ) ).toBe(true); });
it("deduplicates repos returned by fetchReposPage by repoKey", async () => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( Response.json({ output: { items: [ { uri: "at://did:plc:alice/sh.tangled.repo/repo-one", value: { repoDid: "did:plc:repo-one", name: "repo-one", createdAt: "2026-08-01T00:00:00Z" } }, { uri: "at://did:plc:alice/sh.tangled.repo/repo-one-dup", value: { repoDid: "did:plc:repo-one", name: "repo-one-dup", createdAt: "2026-08-02T00:00:00Z" } }, { uri: "at://did:plc:alice/sh.tangled.repo/repo-two", value: { repoDid: "did:plc:repo-two", name: "repo-two", createdAt: "2026-08-03T00:00:00Z" } } ] }, data: {} }) ); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
const result = await fetchReposPage(ctx, { did: "did:plc:alice", handle: "alice.test", viewerDid: "did:plc:viewer" });
expect(result.items).toHaveLength(2); expect(result.items[0].repoDid).toBe("did:plc:repo-one"); expect(result.items[0].name).toBe("repo-one-dup"); expect(result.items[1].repoDid).toBe("did:plc:repo-two"); });});
describe("fetchProfileActivity", () => { it("maps one hydrated Bobbin response into the profile view model", async () => { const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( Response.json({ months: [ { start: "2026-09-01T00:00:00Z", commits: 3, repos: [ { kind: "created", repo: { uri: "at://did:plc:alice/sh.tangled.repo/current", did: "did:plc:current", ownerDid: "did:plc:alice", ownerHandle: "handle.invalid", name: "current" } } ], issues: [ { uri: "at://did:plc:alice/sh.tangled.repo.issue/open", title: "Open issue", state: "open", repo: { uri: "at://did:plc:bob/sh.tangled.repo/target", did: "did:plc:target", ownerDid: "did:plc:bob", ownerHandle: "handle.invalid", name: "target" } } ], pulls: [ { uri: "at://did:plc:alice/sh.tangled.repo.pull/merged", title: "Merged pull", state: "merged", repo: { uri: "at://did:plc:bob/sh.tangled.repo/target", did: "did:plc:target", ownerDid: "did:plc:bob", ownerHandle: "bob.test", name: "target" } } ] }, { start: "2026-08-01T00:00:00Z", commits: 0, repos: [ { kind: "forked", repo: { uri: "at://did:plc:alice/sh.tangled.repo/fork", did: "did:plc:fork", ownerDid: "did:plc:alice", ownerHandle: "alice.test", name: "fork" } } ], issues: [], pulls: [] }, { start: "2026-03-01T00:00:00Z", commits: 0, repos: [], issues: [ { uri: "at://did:plc:alice/sh.tangled.repo.issue/closed", title: "Closed issue", state: "closed", repo: { uri: "at://did:plc:bob/sh.tangled.repo/target", did: "did:plc:target", ownerDid: "did:plc:bob", ownerHandle: "bob.test", name: "target" } } ], pulls: [] } ], truncated: true }) ); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock });
const activity = await fetchProfileActivity(ctx, { did: "did:plc:alice", handle: "alice.test", now: new Date("2026-09-01T12:00:00Z") });
expect(fetchMock).toHaveBeenCalledTimes(1); const request = fetchMock.mock.calls[0][0]; const requestUrl = new URL(request instanceof Request ? request.url : String(request)); expect(requestUrl.pathname).toBe("/xrpc/sh.tangled.actor.getProfileActivity"); expect(requestUrl.searchParams.get("actor")).toBe("did:plc:alice"); expect(requestUrl.searchParams.get("months")).toBe("7"); expect(activity.months.map((month) => month.monthsAgo)).toEqual([0, 1, 6]); expect(activity.truncated).toBe(true); expect(activity.months[0].commits).toBe(3); expect(activity.months[0].repos).toMatchObject([ { name: "current", ownerHandle: "alice.test", forked: false } ]); expect(activity.months[1].repos).toMatchObject([{ name: "fork", forked: true }]); expect(activity.months[0].issues).toMatchObject([ { rkey: "open", state: "open", repo: { repoDid: "did:plc:target", ownerHandle: "did:plc:bob", name: "target" } } ]); expect(activity.months[0].pulls).toMatchObject([{ rkey: "merged", state: "merged" }]); expect(activity.months[2].issues).toMatchObject([{ rkey: "closed", state: "closed" }]); });});