webfetch.test.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { describe, expect, test } from "bun:test"
  2. import path from "path"
  3. import { Instance } from "../../src/project/instance"
  4. import { WebFetchTool } from "../../src/tool/webfetch"
  5. const projectRoot = path.join(import.meta.dir, "../..")
  6. const ctx = {
  7. sessionID: "test",
  8. messageID: "message",
  9. callID: "",
  10. agent: "build",
  11. abort: AbortSignal.any([]),
  12. messages: [],
  13. metadata: () => {},
  14. ask: async () => {},
  15. }
  16. async function withFetch(
  17. mockFetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>,
  18. fn: () => Promise<void>,
  19. ) {
  20. const originalFetch = globalThis.fetch
  21. globalThis.fetch = mockFetch as unknown as typeof fetch
  22. try {
  23. await fn()
  24. } finally {
  25. globalThis.fetch = originalFetch
  26. }
  27. }
  28. describe("tool.webfetch", () => {
  29. test("returns image responses as file attachments", async () => {
  30. const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
  31. await withFetch(
  32. async () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }),
  33. async () => {
  34. await Instance.provide({
  35. directory: projectRoot,
  36. fn: async () => {
  37. const webfetch = await WebFetchTool.init()
  38. const result = await webfetch.execute({ url: "https://example.com/image.png", format: "markdown" }, ctx)
  39. expect(result.output).toBe("Image fetched successfully")
  40. expect(result.attachments).toBeDefined()
  41. expect(result.attachments?.length).toBe(1)
  42. expect(result.attachments?.[0].type).toBe("file")
  43. expect(result.attachments?.[0].mime).toBe("image/png")
  44. expect(result.attachments?.[0].url.startsWith("data:image/png;base64,")).toBe(true)
  45. },
  46. })
  47. },
  48. )
  49. })
  50. test("keeps svg as text output", async () => {
  51. const svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>'
  52. await withFetch(
  53. async () =>
  54. new Response(svg, {
  55. status: 200,
  56. headers: { "content-type": "image/svg+xml; charset=UTF-8" },
  57. }),
  58. async () => {
  59. await Instance.provide({
  60. directory: projectRoot,
  61. fn: async () => {
  62. const webfetch = await WebFetchTool.init()
  63. const result = await webfetch.execute({ url: "https://example.com/image.svg", format: "html" }, ctx)
  64. expect(result.output).toContain("<svg")
  65. expect(result.attachments).toBeUndefined()
  66. },
  67. })
  68. },
  69. )
  70. })
  71. test("keeps text responses as text output", async () => {
  72. await withFetch(
  73. async () =>
  74. new Response("hello from webfetch", {
  75. status: 200,
  76. headers: { "content-type": "text/plain; charset=utf-8" },
  77. }),
  78. async () => {
  79. await Instance.provide({
  80. directory: projectRoot,
  81. fn: async () => {
  82. const webfetch = await WebFetchTool.init()
  83. const result = await webfetch.execute({ url: "https://example.com/file.txt", format: "text" }, ctx)
  84. expect(result.output).toBe("hello from webfetch")
  85. expect(result.attachments).toBeUndefined()
  86. },
  87. })
  88. },
  89. )
  90. })
  91. })