Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283import { describe, expect, it, vi } from "vitest";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { ClientResponseError } from "@atcute/client";import oauthMetadata from "$lib/oauth-client-metadata";import { missingPermissions } from "$lib/auth/scopes";import type { Did, Nsid } from "@atcute/lexicons/syntax";import type * as Invite from "$lib/api/invite";
const agent = { sub: "did:plc:limpet" } as unknown as OAuthUserAgent;const KNOT = "knot.oyster.cafe";const CODE = "8f14e45fceea167a5a36dedd4bea2543";const NSID = "org.tangled.temp.server.redeemInviteCode";
const load = async (respond: () => Response) => { vi.resetModules(); const minted: { aud: string; lxm: string }[] = []; vi.doMock("$lib/auth/agent", () => ({ mintServiceAuth: async (_agent: unknown, { aud, lxm }: { aud: string; lxm: string }) => { minted.push({ aud, lxm }); return `token-for-${aud}`; }, serviceDidForHost: (host: string) => `did:web:${host.replace(/:/g, "%3A")}` })); const invite = (await import("$lib/api/invite")) as typeof Invite; const calls: { url: string; init: RequestInit }[] = []; const fetch = (async (input: URL | RequestInfo, init?: RequestInit) => { calls.push({ url: String(input), init: init ?? {} }); return respond(); }) as typeof globalThis.fetch; return { invite, minted, calls, fetch };};
const ok = () => new Response(JSON.stringify({}), { status: 200 });
describe("redeemInviteCode presents the code and the identity together", () => { it("posts the code to the knot with a token minted for that knot and method", async () => { const { invite, minted, calls, fetch } = await load(ok);
await invite.redeemInviteCode(agent, KNOT, CODE, fetch);
expect(minted).toEqual([{ aud: `did:web:${KNOT}`, lxm: NSID }]); expect(calls[0].url).toBe(`https://${KNOT}/xrpc/${NSID}`); expect(calls[0].init.method).toBe("POST"); expect(JSON.parse(String(calls[0].init.body))).toEqual({ code: CODE }); expect(new Headers(calls[0].init.headers).get("authorization")).toBe( `Bearer token-for-did:web:${KNOT}` ); });
it("keeps a port on the knot host and drops any path", async () => { const { invite, calls, minted, fetch } = await load(ok);
await invite.redeemInviteCode(agent, `${KNOT}:8443`, CODE, fetch);
expect(calls[0].url).toBe(`https://${KNOT}:8443/xrpc/${NSID}`); expect(minted[0].aud).toBe(`did:web:${KNOT}%3A8443`); });
it("a spent code surfaces the knot's own error", async () => { const { invite, fetch } = await load( () => new Response( JSON.stringify({ error: "InviteNotFound", message: "already spent" }), { status: 400, headers: { "content-type": "application/json" } } ) );
const thrown = await invite.redeemInviteCode(agent, KNOT, CODE, fetch).catch((e) => e); expect(thrown).toBeInstanceOf(ClientResponseError); expect((thrown as ClientResponseError).error).toBe("InviteNotFound"); });});
describe("the client metadata asks for what the invite flow spends", () => { it("grants the redeem method against any knot", () => { const wanted = [ { resource: "rpc", lxm: NSID as Nsid, aud: `did:web:${KNOT}` as Did } ] as const;
expect(missingPermissions(oauthMetadata.scope, wanted)).toEqual([]); });});