import { describe, expect, it } from "bun:test"; import type { Agent } from "@atproto/api"; import { createBlueskyPost, createDocument, updateDocument, } from "../src/lib/atproto"; import type { BlogPost, PublisherConfig } from "../src/lib/types"; function createPost(overrides: Partial = {}): BlogPost { return { filePath: "/tmp/hello-world.md", slug: "hello-world", frontmatter: { title: "Hello World", description: "A short description", publishDate: "2025-01-02T03:04:05.000Z", }, content: "Body content", rawContent: "---\ntitle: Hello World\n---\nBody content", rawFrontmatter: {}, ...overrides, }; } function createConfig( overrides: Partial = {}, ): PublisherConfig { return { siteUrl: "https://example.com", contentDir: "./content", publicationUri: "at://did:plc:publisher/site.standard.publication/main", publishContent: false, ...overrides, }; } describe("ATProto document publishing", () => { it("does not persist canonicalUrl on newly created document records", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { createRecord: async (input: { repo: string; collection: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "new-cid", uri: "at://did:plc:publisher/site.standard.document/new", }, }; }, }, }, }, } as Agent; await createDocument(agent, createPost(), createConfig()); expect(capturedRecord).toBeDefined(); expect(capturedRecord?.canonicalUrl).toBeUndefined(); expect(capturedRecord?.path).toBe("/posts/hello-world"); }); it("removes canonicalUrl from updated document records while preserving other fields", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { getRecord: async () => ({ data: { value: { $type: "site.standard.document", title: "Old title", site: "at://did:plc:publisher/site.standard.publication/main", path: "/posts/old", publishedAt: "2024-01-01T00:00:00.000Z", canonicalUrl: "https://example.com/posts/old", bskyPostRef: { uri: "at://did:plc:publisher/app.bsky.feed.post/abc", cid: "bsky-cid", }, location: "keep-me", }, }, }), putRecord: async (input: { repo: string; collection: string; rkey: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "updated-cid", uri: "at://did:plc:publisher/site.standard.document/existing", }, }; }, }, }, }, } as unknown as Agent; await updateDocument( agent, createPost(), "at://did:plc:publisher/site.standard.document/existing", createConfig(), ); expect(capturedRecord).toBeDefined(); expect(capturedRecord?.canonicalUrl).toBeUndefined(); expect(capturedRecord?.path).toBe("/posts/hello-world"); expect(capturedRecord?.location).toBe("keep-me"); expect(capturedRecord?.bskyPostRef).toEqual({ uri: "at://did:plc:publisher/app.bsky.feed.post/abc", cid: "bsky-cid", }); }); it("omits textContent entirely when publishContent is disabled", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { createRecord: async (input: { repo: string; collection: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "new-cid", uri: "at://did:plc:publisher/site.standard.document/new", }, }; }, }, }, }, } as Agent; await createDocument(agent, createPost(), createConfig()); expect(capturedRecord).toBeDefined(); expect("textContent" in capturedRecord!).toBe(false); }); it("includes textContent when publishContent is enabled", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { createRecord: async (input: { repo: string; collection: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "new-cid", uri: "at://did:plc:publisher/site.standard.document/new", }, }; }, }, }, }, } as Agent; await createDocument( agent, createPost(), createConfig({ publishContent: true }), ); expect(capturedRecord?.textContent).toBe("Body content"); }); it("strips existing textContent on update when publishContent is disabled", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { getRecord: async () => ({ data: { value: { $type: "site.standard.document", title: "Old title", site: "at://did:plc:publisher/site.standard.publication/main", path: "/posts/old", publishedAt: "2024-01-01T00:00:00.000Z", textContent: "Previously published body", }, }, }), putRecord: async (input: { repo: string; collection: string; rkey: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "updated-cid", uri: "at://did:plc:publisher/site.standard.document/existing", }, }; }, }, }, }, } as unknown as Agent; await updateDocument( agent, createPost(), "at://did:plc:publisher/site.standard.document/existing", createConfig(), ); expect(capturedRecord).toBeDefined(); expect("textContent" in capturedRecord!).toBe(false); }); it("still uses canonicalUrl for the Bluesky external embed URL", async () => { let capturedRecord: Record | undefined; const agent = { did: "did:plc:publisher", com: { atproto: { repo: { createRecord: async (input: { repo: string; collection: string; record: Record; }) => { capturedRecord = input.record; return { data: { cid: "bsky-cid", uri: "at://did:plc:publisher/app.bsky.feed.post/new", }, }; }, }, }, }, } as Agent; await createBlueskyPost(agent, { title: "Hello World", description: "A short description", canonicalUrl: "https://example.com/posts/hello-world", documentRef: { uri: "at://did:plc:publisher/site.standard.document/hello-world", cid: "doc-cid", }, publicationRef: { uri: "at://did:plc:publisher/site.standard.publication/main", cid: "pub-cid", }, publishedAt: "2025-01-02T03:04:05.000Z", }); expect(capturedRecord).toBeDefined(); expect(capturedRecord?.embed).toMatchObject({ $type: "app.bsky.embed.external", external: { uri: "https://example.com/posts/hello-world", }, }); }); });