Files
kunthawat 2d1be52177 Emdash source with visual editor image upload fix
Fixes:
1. media.ts: wrap placeholder generation in try-catch
2. toolbar.ts: check r.ok, display error message in popover
2026-05-03 10:44:54 +07:00

107 lines
2.9 KiB
TypeScript

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<string, unknown>([
["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");
});
});