79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import { Effect, Layer } from "effect"
|
|
import { FetchHttpClient } from "effect/unstable/http"
|
|
import { Models, ModelsDevError } from "../src/effect.js"
|
|
|
|
function stub(data: unknown, init?: ResponseInit) {
|
|
const requests: Request[] = []
|
|
const fetch = (async (input: Parameters<typeof globalThis.fetch>[0], requestInit?: RequestInit) => {
|
|
requests.push(new Request(input instanceof URL ? input.href : (input as string), requestInit))
|
|
return new Response(JSON.stringify(data), {
|
|
headers: { "content-type": "application/json" },
|
|
...init,
|
|
})
|
|
}) as typeof globalThis.fetch
|
|
const layer = FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch)(fetch)))
|
|
return { requests, layer }
|
|
}
|
|
|
|
test("providers() succeeds through an injected transport", async () => {
|
|
const { requests, layer } = stub({ anthropic: { id: "anthropic" } })
|
|
const program = Effect.gen(function* () {
|
|
const client = yield* Models.make()
|
|
return yield* client.providers()
|
|
})
|
|
const result = await program.pipe(Effect.provide(layer), Effect.runPromise)
|
|
expect(result["anthropic"]?.id).toBe("anthropic")
|
|
expect(requests[0]?.url).toBe("https://models.dev/api.json")
|
|
expect(requests[0]?.headers.get("user-agent")).toBeNull()
|
|
})
|
|
|
|
test("models() and catalog() hit their endpoints, baseUrl subpath preserved", async () => {
|
|
const { requests, layer } = stub({})
|
|
const program = Effect.gen(function* () {
|
|
const client = yield* Models.make({ baseUrl: "https://example.com/mirror" })
|
|
yield* client.models()
|
|
yield* client.catalog()
|
|
})
|
|
await program.pipe(Effect.provide(layer), Effect.runPromise)
|
|
expect(requests.map((request) => request.url)).toEqual([
|
|
"https://example.com/mirror/models.json",
|
|
"https://example.com/mirror/catalog.json",
|
|
])
|
|
})
|
|
|
|
test("custom headers are sent", async () => {
|
|
const { requests, layer } = stub({})
|
|
const program = Effect.gen(function* () {
|
|
const client = yield* Models.make({ headers: { "x-custom": "yes" } })
|
|
yield* client.providers()
|
|
})
|
|
await program.pipe(Effect.provide(layer), Effect.runPromise)
|
|
expect(requests[0]?.headers.get("x-custom")).toBe("yes")
|
|
})
|
|
|
|
test("non-2xx fails with ModelsDevError in the error channel", async () => {
|
|
const { layer } = stub({ error: "down" }, { status: 503 })
|
|
const program = Effect.gen(function* () {
|
|
const client = yield* Models.make()
|
|
return yield* client.providers()
|
|
})
|
|
const error = await program.pipe(Effect.flip, Effect.provide(layer), Effect.runPromise)
|
|
expect(error).toBeInstanceOf(ModelsDevError)
|
|
expect(error._tag).toBe("ModelsDevError")
|
|
})
|
|
|
|
test("Service and layer provide a shared client", async () => {
|
|
const { requests, layer } = stub({ "openai/gpt-oss-120b": { id: "openai/gpt-oss-120b" } })
|
|
const program = Effect.gen(function* () {
|
|
const client = yield* Models.Service
|
|
return yield* client.models()
|
|
})
|
|
const result = await program.pipe(
|
|
Effect.provide(Models.layer().pipe(Layer.provide(layer))),
|
|
Effect.runPromise,
|
|
)
|
|
expect(result["openai/gpt-oss-120b"]?.id).toBe("openai/gpt-oss-120b")
|
|
expect(requests.length).toBe(1)
|
|
})
|