local-attachment.test.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { describe, expect, test } from "bun:test"
  2. import { parsePastedFilepaths, readLocalAttachmentWith } from "../../src/component/prompt/local-attachment"
  3. import type { LocalFiles } from "../../src/component/prompt/local-attachment"
  4. function files(input: { mime: string; text?: string; bytes?: Uint8Array }): LocalFiles {
  5. return {
  6. mime: async () => input.mime,
  7. readText: async () => input.text ?? "",
  8. readBytes: async () => input.bytes ?? new Uint8Array(),
  9. }
  10. }
  11. describe("prompt local attachments", () => {
  12. test("parses multi-file drops from POSIX, URI-list, and Windows terminals", () => {
  13. expect(parsePastedFilepaths("'/tmp/one image.png' /tmp/two\\ image.webp", "linux")).toEqual([
  14. "/tmp/one image.png",
  15. "/tmp/two image.webp",
  16. ])
  17. expect(parsePastedFilepaths("file:///tmp/one%20image.png\r\nfile:///tmp/two.webp", "linux")).toEqual([
  18. "/tmp/one image.png",
  19. "/tmp/two.webp",
  20. ])
  21. expect(parsePastedFilepaths("# dropped files\nfile:///tmp/one.png\nfile:///tmp/two.webp", "linux")).toEqual([
  22. "/tmp/one.png",
  23. "/tmp/two.webp",
  24. ])
  25. expect(parsePastedFilepaths("/tmp/one\\\\image.png /tmp/two.webp", "linux")).toEqual([
  26. "/tmp/one\\image.png",
  27. "/tmp/two.webp",
  28. ])
  29. expect(parsePastedFilepaths('"C:\\one image.png" "C:\\two.webp"', "win32")).toEqual([
  30. "C:\\one image.png",
  31. "C:\\two.webp",
  32. ])
  33. expect(parsePastedFilepaths("file:///C:/one%20image.png\r\nfile://server/share/two.webp", "win32")).toEqual([
  34. "C:\\one image.png",
  35. "\\\\server\\share\\two.webp",
  36. ])
  37. expect(parsePastedFilepaths('"/tmp/O\'Brien.png" /tmp/two.webp', "linux")).toEqual([
  38. "/tmp/O'Brien.png",
  39. "/tmp/two.webp",
  40. ])
  41. })
  42. test("rejects unbounded and malformed multi-file drops", () => {
  43. expect(parsePastedFilepaths("'/tmp/one.png /tmp/two.png", "linux")).toEqual([])
  44. expect(
  45. parsePastedFilepaths(Array.from({ length: 33 }, (_, index) => `/tmp/${index}.png`).join(" "), "linux"),
  46. ).toEqual([])
  47. })
  48. test("reads SVG attachments as text", async () => {
  49. expect(await readLocalAttachmentWith(files({ mime: "image/svg+xml", text: "<svg />" }), "/tmp/image.svg")).toEqual({
  50. type: "text",
  51. mime: "image/svg+xml",
  52. content: "<svg />",
  53. })
  54. })
  55. test("reads image and PDF attachments as bytes", async () => {
  56. const content = new Uint8Array([1, 2, 3])
  57. expect(await readLocalAttachmentWith(files({ mime: "application/pdf", bytes: content }), "/tmp/file.pdf")).toEqual({
  58. type: "binary",
  59. mime: "application/pdf",
  60. content,
  61. })
  62. })
  63. test("ignores unsupported and unreadable local files", async () => {
  64. expect(await readLocalAttachmentWith(files({ mime: "text/plain" }), "/tmp/file.txt")).toBeUndefined()
  65. expect(
  66. await readLocalAttachmentWith(
  67. {
  68. ...files({ mime: "image/png" }),
  69. readBytes: async () => Promise.reject(new Error("missing")),
  70. },
  71. "/tmp/missing.png",
  72. ),
  73. ).toBeUndefined()
  74. expect(
  75. await readLocalAttachmentWith(files({ mime: "image/png", bytes: new Uint8Array(2) }), "/tmp/large.png", 1),
  76. ).toBeUndefined()
  77. })
  78. })