export * as TestWebSearch from "./websearch" import { Context, Deferred, Effect, Layer } from "effect" import { HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder" import { LayerNode } from "@opencode/util/effect/layer-node" import { Bus } from "@opencode/core/bus" import { KV } from "@opencode/core/kv" import { WebSearch } from "@opencode/core/websearch" import type { Session } from "@opencode/schema/session" export interface Interface extends WebSearch.Interface { readonly queries: readonly WebSearch.Input[] readonly sessionIDs: readonly (Session.ID | undefined)[] /** Waits for query arrivals, not provider execution or query completion. */ readonly wait: (count: number) => Effect.Effect } export class Service extends Context.Service()("test/WebSearch") {} export function httpError(status = 429, retryAfter?: string, url = "https://search.example.com") { const request = HttpClientRequest.post(url) return new HttpClientError.HttpClientError({ reason: new HttpClientError.StatusCodeError({ request, response: HttpClientResponse.fromWeb( request, new Response(null, { status, headers: retryAfter === undefined ? {} : { "Retry-After": retryAfter } }), ), }), }) } // No providers are installed: tests register local executors through transform. // The normal Bus and KV implementations use the default in-memory database. export const layer = Layer.effectContext( Effect.gen(function* () { const context = yield* Layer.build(AppNodeBuilder.build(LayerNode.group([WebSearch.node, Bus.node, KV.node]))) const websearch = Context.get(context, WebSearch.Service) const queries: WebSearch.Input[] = [] const sessionIDs: (Session.ID | undefined)[] = [] let started = yield* Deferred.make() const wait = (count: number): Effect.Effect => Effect.suspend(() => queries.length >= count ? Effect.void : Deferred.await(started).pipe(Effect.andThen(() => wait(count))), ) const test = Service.of({ ...websearch, queries, sessionIDs, wait, query: Effect.fnUntraced(function* (input, options) { queries.push({ ...input }) sessionIDs.push(options?.sessionID) const previous = started started = yield* Deferred.make() yield* Deferred.succeed(previous, undefined) return yield* websearch.query(input, options) }), }) return Context.add(context, WebSearch.Service, test).pipe(Context.add(Service, test)) }), )