Compare commits
5 Commits
dev
...
fix-webfet
| Author | SHA1 | Date |
|---|---|---|
|
|
78fb003379 | |
|
|
4230584c93 | |
|
|
84d741d339 | |
|
|
26d70b02ed | |
|
|
141fcde76a |
|
|
@ -17,58 +17,25 @@ const ctx = {
|
||||||
ask: async () => {},
|
ask: async () => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimerID = ReturnType<typeof setTimeout>
|
async function withFetch(fetch: (req: Request) => Response | Promise<Response>, fn: (url: URL) => Promise<void>) {
|
||||||
|
using server = Bun.serve({ port: 0, fetch })
|
||||||
async function withFetch(
|
await fn(server.url)
|
||||||
mockFetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>,
|
|
||||||
fn: () => Promise<void>,
|
|
||||||
) {
|
|
||||||
const originalFetch = globalThis.fetch
|
|
||||||
globalThis.fetch = mockFetch as unknown as typeof fetch
|
|
||||||
try {
|
|
||||||
await fn()
|
|
||||||
} finally {
|
|
||||||
globalThis.fetch = originalFetch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withTimers(fn: (state: { ids: TimerID[]; cleared: TimerID[] }) => Promise<void>) {
|
|
||||||
const set = globalThis.setTimeout
|
|
||||||
const clear = globalThis.clearTimeout
|
|
||||||
const ids: TimerID[] = []
|
|
||||||
const cleared: TimerID[] = []
|
|
||||||
|
|
||||||
globalThis.setTimeout = ((...args: Parameters<typeof setTimeout>) => {
|
|
||||||
const id = set(...args)
|
|
||||||
ids.push(id)
|
|
||||||
return id
|
|
||||||
}) as typeof setTimeout
|
|
||||||
|
|
||||||
globalThis.clearTimeout = ((id?: TimerID) => {
|
|
||||||
if (id !== undefined) cleared.push(id)
|
|
||||||
return clear(id)
|
|
||||||
}) as typeof clearTimeout
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fn({ ids, cleared })
|
|
||||||
} finally {
|
|
||||||
ids.forEach(clear)
|
|
||||||
globalThis.setTimeout = set
|
|
||||||
globalThis.clearTimeout = clear
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("tool.webfetch", () => {
|
describe("tool.webfetch", () => {
|
||||||
test("returns image responses as file attachments", async () => {
|
test("returns image responses as file attachments", async () => {
|
||||||
const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
|
const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
|
||||||
await withFetch(
|
await withFetch(
|
||||||
async () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }),
|
() => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }),
|
||||||
async () => {
|
async (url) => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: projectRoot,
|
directory: projectRoot,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const webfetch = await WebFetchTool.init()
|
const webfetch = await WebFetchTool.init()
|
||||||
const result = await webfetch.execute({ url: "https://example.com/image.png", format: "markdown" }, ctx)
|
const result = await webfetch.execute(
|
||||||
|
{ url: new URL("/image.png", url).toString(), format: "markdown" },
|
||||||
|
ctx,
|
||||||
|
)
|
||||||
expect(result.output).toBe("Image fetched successfully")
|
expect(result.output).toBe("Image fetched successfully")
|
||||||
expect(result.attachments).toBeDefined()
|
expect(result.attachments).toBeDefined()
|
||||||
expect(result.attachments?.length).toBe(1)
|
expect(result.attachments?.length).toBe(1)
|
||||||
|
|
@ -87,17 +54,17 @@ describe("tool.webfetch", () => {
|
||||||
test("keeps svg as text output", async () => {
|
test("keeps svg as text output", async () => {
|
||||||
const svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>'
|
const svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>'
|
||||||
await withFetch(
|
await withFetch(
|
||||||
async () =>
|
() =>
|
||||||
new Response(svg, {
|
new Response(svg, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { "content-type": "image/svg+xml; charset=UTF-8" },
|
headers: { "content-type": "image/svg+xml; charset=UTF-8" },
|
||||||
}),
|
}),
|
||||||
async () => {
|
async (url) => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: projectRoot,
|
directory: projectRoot,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const webfetch = await WebFetchTool.init()
|
const webfetch = await WebFetchTool.init()
|
||||||
const result = await webfetch.execute({ url: "https://example.com/image.svg", format: "html" }, ctx)
|
const result = await webfetch.execute({ url: new URL("/image.svg", url).toString(), format: "html" }, ctx)
|
||||||
expect(result.output).toContain("<svg")
|
expect(result.output).toContain("<svg")
|
||||||
expect(result.attachments).toBeUndefined()
|
expect(result.attachments).toBeUndefined()
|
||||||
},
|
},
|
||||||
|
|
@ -108,17 +75,17 @@ describe("tool.webfetch", () => {
|
||||||
|
|
||||||
test("keeps text responses as text output", async () => {
|
test("keeps text responses as text output", async () => {
|
||||||
await withFetch(
|
await withFetch(
|
||||||
async () =>
|
() =>
|
||||||
new Response("hello from webfetch", {
|
new Response("hello from webfetch", {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { "content-type": "text/plain; charset=utf-8" },
|
headers: { "content-type": "text/plain; charset=utf-8" },
|
||||||
}),
|
}),
|
||||||
async () => {
|
async (url) => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: projectRoot,
|
directory: projectRoot,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const webfetch = await WebFetchTool.init()
|
const webfetch = await WebFetchTool.init()
|
||||||
const result = await webfetch.execute({ url: "https://example.com/file.txt", format: "text" }, ctx)
|
const result = await webfetch.execute({ url: new URL("/file.txt", url).toString(), format: "text" }, ctx)
|
||||||
expect(result.output).toBe("hello from webfetch")
|
expect(result.output).toBe("hello from webfetch")
|
||||||
expect(result.attachments).toBeUndefined()
|
expect(result.attachments).toBeUndefined()
|
||||||
},
|
},
|
||||||
|
|
@ -126,28 +93,4 @@ describe("tool.webfetch", () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("clears timeout when fetch rejects", async () => {
|
|
||||||
await withTimers(async ({ ids, cleared }) => {
|
|
||||||
await withFetch(
|
|
||||||
async () => {
|
|
||||||
throw new Error("boom")
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
await Instance.provide({
|
|
||||||
directory: projectRoot,
|
|
||||||
fn: async () => {
|
|
||||||
const webfetch = await WebFetchTool.init()
|
|
||||||
await expect(
|
|
||||||
webfetch.execute({ url: "https://example.com/file.txt", format: "text" }, ctx),
|
|
||||||
).rejects.toThrow("boom")
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(ids).toHaveLength(1)
|
|
||||||
expect(cleared).toContain(ids[0])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue