| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- import { describe, expect, test } from "bun:test"
- import { pathToFileURL } from "node:url"
- import { contentBlockToParts, partsToContentChunks, promptContentToParts } from "../../src/acp/content"
- describe("acp content conversion", () => {
- test("plain text block becomes a text part", () => {
- expect(contentBlockToParts({ type: "text", text: "hello" })).toEqual([{ type: "text", text: "hello" }])
- })
- test("assistant-only text audience becomes synthetic", () => {
- expect(
- contentBlockToParts({
- type: "text",
- text: "internal",
- annotations: { audience: ["assistant"] },
- }),
- ).toEqual([{ type: "text", text: "internal", synthetic: true }])
- })
- test("user-only text audience becomes ignored", () => {
- expect(
- contentBlockToParts({
- type: "text",
- text: "visible to user",
- annotations: { audience: ["user"] },
- }),
- ).toEqual([{ type: "text", text: "visible to user", ignored: true }])
- })
- test("image block with base64 data becomes a data URL file part", () => {
- expect(
- contentBlockToParts({
- type: "image",
- data: "AAAA",
- mimeType: "image/png",
- uri: "file:///tmp/screenshot.png",
- }),
- ).toEqual([
- {
- type: "file",
- url: "data:image/png;base64,AAAA",
- filename: "screenshot.png",
- mime: "image/png",
- },
- ])
- })
- test("image block with http URI becomes a file part", () => {
- expect(
- contentBlockToParts({
- type: "image",
- data: "",
- mimeType: "image/jpeg",
- uri: "http://example.com/assets/photo.jpg",
- }),
- ).toEqual([
- {
- type: "file",
- url: "http://example.com/assets/photo.jpg",
- filename: "photo.jpg",
- mime: "image/jpeg",
- },
- ])
- })
- test("resource_link file URL becomes a file part with name and fallback mime", () => {
- expect(
- contentBlockToParts({
- type: "resource_link",
- uri: "file:///tmp/notes.txt",
- name: "client-notes.txt",
- }),
- ).toEqual([
- {
- type: "file",
- url: "file:///tmp/notes.txt",
- filename: "client-notes.txt",
- mime: "text/plain",
- },
- ])
- })
- test("resource_link zed path becomes a file URL part", () => {
- expect(
- contentBlockToParts({
- type: "resource_link",
- uri: "zed://workspace?path=/tmp/project/src/app.ts",
- name: "app.ts",
- mimeType: "text/typescript",
- }),
- ).toEqual([
- {
- type: "file",
- url: pathToFileURL("/tmp/project/src/app.ts").href,
- filename: "app.ts",
- mime: "text/typescript",
- },
- ])
- })
- test("resource with text becomes a sourced text part", () => {
- const result = contentBlockToParts({
- type: "resource",
- resource: {
- uri: "file:///tmp/context.txt#L12-L14",
- mimeType: "text/plain",
- text: "context",
- },
- })
- expect(result).toHaveLength(1)
- expect(result[0]?.type).toBe("text")
- if (result[0]?.type === "text") {
- expect(result[0].text.endsWith("\ncontext")).toBe(true)
- expect(result[0].text.includes("context.txt")).toBe(true)
- expect(result[0].text.includes("12")).toBe(true)
- }
- })
- test("resource with text uses URI fallback for non-file resources", () => {
- expect(
- contentBlockToParts({
- type: "resource",
- resource: {
- uri: "mcp://server/context",
- text: "context",
- },
- }),
- ).toEqual([{ type: "text", text: "[mcp://server/context]\ncontext" }])
- })
- test("resource with text includes file path", () => {
- const result = contentBlockToParts({
- type: "resource",
- resource: {
- uri: "file:///tmp/context.txt",
- mimeType: "text/plain",
- text: "context",
- },
- })
- expect(result).toHaveLength(1)
- expect(result[0]?.type).toBe("text")
- if (result[0]?.type === "text") {
- expect(result[0].text.endsWith("\ncontext")).toBe(true)
- expect(result[0].text.includes("context.txt")).toBe(true)
- }
- })
- test("resource with blob and mimeType becomes a data URL file part", () => {
- expect(
- contentBlockToParts({
- type: "resource",
- resource: {
- uri: "file:///tmp/report.pdf",
- mimeType: "application/pdf",
- blob: "JVBERg==",
- },
- }),
- ).toEqual([
- {
- type: "file",
- url: "data:application/pdf;base64,JVBERg==",
- filename: "report.pdf",
- mime: "application/pdf",
- },
- ])
- })
- test("data URL resource is preserved as a file part", () => {
- expect(
- contentBlockToParts({
- type: "resource",
- resource: {
- uri: "data:text/plain;base64,aGVsbG8=",
- mimeType: "text/plain",
- blob: "ignored",
- },
- }),
- ).toEqual([
- {
- type: "file",
- url: "data:text/plain;base64,aGVsbG8=",
- filename: "file",
- mime: "text/plain",
- },
- ])
- })
- test("unsupported blocks are ignored", () => {
- expect(promptContentToParts([{ type: "audio", data: "AAAA", mimeType: "audio/wav" }])).toEqual([])
- expect(
- promptContentToParts([
- // @ts-expect-error Exercise forward compatibility with an unknown ACP content block.
- { type: "unknown", text: "skip" },
- ]),
- ).toEqual([])
- })
- })
- describe("acp replay conversion", () => {
- test("replays text audience annotations", () => {
- expect(partsToContentChunks([{ type: "text", text: "cached", synthetic: true }])).toEqual([
- {
- content: {
- type: "text",
- text: "cached",
- annotations: { audience: ["assistant"] },
- },
- },
- ])
- })
- test("replays file and data URL parts as ACP content", () => {
- expect(
- partsToContentChunks([
- { type: "file", url: "file:///tmp/readme.md", filename: "readme.md", mime: "text/markdown" },
- { type: "file", url: "data:text/plain;base64,aGVsbG8=", filename: "note.txt", mime: "text/plain" },
- ]),
- ).toEqual([
- {
- content: {
- type: "resource_link",
- uri: "file:///tmp/readme.md",
- name: "readme.md",
- mimeType: "text/markdown",
- },
- },
- {
- content: {
- type: "resource",
- resource: {
- uri: pathToFileURL("note.txt").href,
- mimeType: "text/plain",
- text: "hello",
- },
- },
- },
- ])
- })
- })
|