test: migrate webfetch tool tests to effect runner (#27171)

This commit is contained in:
Kit Langton
2026-05-12 23:46:23 +00:00
committed by GitHub
parent 2c334d9242
commit ded4da735b
+51 -61
View File
@@ -1,15 +1,14 @@
import { describe, expect, test } from "bun:test" import { describe, expect } from "bun:test"
import path from "path"
import { Effect, Layer } from "effect" import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http" import { FetchHttpClient } from "effect/unstable/http"
import { Agent } from "../../src/agent/agent" import { Agent } from "../../src/agent/agent"
import { Truncate } from "@/tool/truncate" import { Truncate } from "@/tool/truncate"
import { Instance } from "../../src/project/instance"
import { WithInstance } from "../../src/project/with-instance"
import { WebFetchTool } from "../../src/tool/webfetch" import { WebFetchTool } from "../../src/tool/webfetch"
import { SessionID, MessageID } from "../../src/session/schema" import { SessionID, MessageID } from "../../src/session/schema"
import { Tool } from "@/tool/tool"
import { testEffect } from "../lib/effect"
const projectRoot = path.join(import.meta.dir, "../..") const it = testEffect(Layer.mergeAll(FetchHttpClient.layer, Truncate.defaultLayer, Agent.defaultLayer))
const ctx = { const ctx = {
sessionID: SessionID.make("ses_test"), sessionID: SessionID.make("ses_test"),
@@ -22,30 +21,31 @@ const ctx = {
ask: () => Effect.void, ask: () => Effect.void,
} }
async function withFetch(fetch: (req: Request) => Response | Promise<Response>, fn: (url: URL) => Promise<void>) { const withFetch = <A, E, R>(
using server = Bun.serve({ port: 0, fetch }) fetch: (req: Request) => Response | Promise<Response>,
await fn(server.url) fn: (url: URL) => Effect.Effect<A, E, R>,
} ) =>
Effect.acquireUseRelease(
function exec(args: { url: string; format: "text" | "markdown" | "html" }) { Effect.sync(() => Bun.serve({ port: 0, fetch })),
return WebFetchTool.pipe( (server) => fn(server.url),
Effect.flatMap((info) => info.init()), (server) => Effect.sync(() => server.stop(true)),
Effect.flatMap((tool) => tool.execute(args, ctx)),
Effect.provide(Layer.mergeAll(FetchHttpClient.layer, Truncate.defaultLayer, Agent.defaultLayer)),
Effect.runPromise,
) )
}
const exec = Effect.fn("WebFetchToolTest.exec")(function* (args: Tool.InferParameters<typeof WebFetchTool>) {
const info = yield* WebFetchTool
const tool = yield* info.init()
return yield* tool.execute(args, ctx)
})
describe("tool.webfetch", () => { describe("tool.webfetch", () => {
test("returns image responses as file attachments", async () => { it.instance("returns image responses as file attachments", () =>
const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]) Effect.gen(function* () {
await withFetch( const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
() => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }), yield* withFetch(
async (url) => { () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }),
await WithInstance.provide({ (url) =>
directory: projectRoot, Effect.gen(function* () {
fn: async () => { const result = yield* exec({ url: new URL("/image.png", url).toString(), format: "markdown" })
const result = await exec({ url: new URL("/image.png", url).toString(), format: "markdown" })
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)
@@ -55,50 +55,40 @@ describe("tool.webfetch", () => {
expect(result.attachments?.[0]).not.toHaveProperty("id") expect(result.attachments?.[0]).not.toHaveProperty("id")
expect(result.attachments?.[0]).not.toHaveProperty("sessionID") expect(result.attachments?.[0]).not.toHaveProperty("sessionID")
expect(result.attachments?.[0]).not.toHaveProperty("messageID") expect(result.attachments?.[0]).not.toHaveProperty("messageID")
}, }),
}) )
}, }),
) )
})
test("keeps svg as text output", async () => { it.instance("keeps svg as text output", () =>
const svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>' withFetch(
await withFetch(
() => () =>
new Response(svg, { new Response('<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>', {
status: 200, status: 200,
headers: { "content-type": "image/svg+xml; charset=UTF-8" }, headers: { "content-type": "image/svg+xml; charset=UTF-8" },
}), }),
async (url) => { (url) =>
await WithInstance.provide({ Effect.gen(function* () {
directory: projectRoot, const result = yield* exec({ url: new URL("/image.svg", url).toString(), format: "html" })
fn: async () => { expect(result.output).toContain("<svg")
const result = await exec({ url: new URL("/image.svg", url).toString(), format: "html" }) expect(result.attachments).toBeUndefined()
expect(result.output).toContain("<svg") }),
expect(result.attachments).toBeUndefined() ),
}, )
})
},
)
})
test("keeps text responses as text output", async () => { it.instance("keeps text responses as text output", () =>
await withFetch( withFetch(
() => () =>
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 (url) => { (url) =>
await WithInstance.provide({ Effect.gen(function* () {
directory: projectRoot, const result = yield* exec({ url: new URL("/file.txt", url).toString(), format: "text" })
fn: async () => { expect(result.output).toBe("hello from webfetch")
const result = await exec({ url: new URL("/file.txt", url).toString(), format: "text" }) expect(result.attachments).toBeUndefined()
expect(result.output).toBe("hello from webfetch") }),
expect(result.attachments).toBeUndefined() ),
}, )
})
},
)
})
}) })