import { describe, it, expect, vi } from "vitest"; import { createRecord, normalizePdsHost, rkeyFromUri } from "../src/atproto.js"; describe("normalizePdsHost", () => { it("defaults to bsky.social", () => { expect(normalizePdsHost(undefined)).toBe("bsky.social"); }); it("accepts host-only values", () => { expect(normalizePdsHost("bsky.social")).toBe("bsky.social"); }); it("accepts full PDS URLs", () => { expect(normalizePdsHost("https://bsky.social")).toBe("bsky.social"); expect(normalizePdsHost("https://example.com/")).toBe("example.com"); }); it("preserves ports for https URLs", () => { expect(normalizePdsHost("https://localhost:2583")).toBe("localhost:2583"); }); it("rejects non-https protocols", () => { expect(() => normalizePdsHost("http://localhost:2583")).toThrow( "Invalid PDS host protocol: http:", ); }); }); describe("rkeyFromUri", () => { it("extracts rkey from a standard AT-URI", () => { const rkey = rkeyFromUri("at://did:plc:abc123/site.standard.document/3lwafzkjqm25s"); expect(rkey).toBe("3lwafzkjqm25s"); }); it("extracts rkey from a Bluesky post URI", () => { const rkey = rkeyFromUri("at://did:plc:abc123/app.bsky.feed.post/3k4duaz5vfs2b"); expect(rkey).toBe("3k4duaz5vfs2b"); }); it("throws on empty URI", () => { expect(() => rkeyFromUri("")).toThrow("Invalid AT-URI"); }); }); describe("createRecord", () => { it("refreshes the session when the PDS returns a 400 ExpiredToken response", async () => { const kv = new Map([ ["settings:pdsHost", "bsky.social"], ["settings:handle", "example.com"], ["settings:appPassword", "app-password"], ["state:accessJwt", "stale-access"], ["state:refreshJwt", "refresh-token"], ["state:did", "did:plc:test"], ]); const fetch = vi .fn() .mockResolvedValueOnce( new Response(JSON.stringify({ error: "ExpiredToken", message: "Token has expired" }), { status: 400, }), ) .mockResolvedValueOnce( new Response( JSON.stringify({ accessJwt: "fresh-access", refreshJwt: "fresh-refresh", did: "did:plc:test", handle: "example.com", }), { status: 200 }, ), ) .mockResolvedValueOnce( new Response( JSON.stringify({ uri: "at://did:plc:test/site.standard.publication/abc", cid: "cid" }), { status: 200, }, ), ); const ctx = { http: { fetch }, kv: { get: vi.fn(async (key: string) => kv.get(key)), set: vi.fn(async (key: string, value: unknown) => { kv.set(key, value); }), }, } as any; const result = await createRecord( ctx, "bsky.social", "stale-access", "did:plc:test", "site.standard.publication", { name: "Example Site" }, ); expect(result).toEqual({ uri: "at://did:plc:test/site.standard.publication/abc", cid: "cid" }); expect(fetch).toHaveBeenCalledTimes(3); expect(kv.get("state:accessJwt")).toBe("fresh-access"); expect(kv.get("state:refreshJwt")).toBe("fresh-refresh"); }); });