Skip to content

testing: owned-fake stub HttpClient seam (comms-e5vm.1) - #8

Merged
GraemeF merged 1 commit into
mainfrom
comms-e5vm.1
Jun 13, 2026
Merged

GraemeF merged 1 commit into
mainfrom
comms-e5vm.1

Conversation

@GraemeF

@GraemeF GraemeF commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

What

Adds a reusable owned-fake HttpClient for tests, built on @effect/platform's HttpClient.make(req => Effect<response>). It fakes the HttpClient.HttpClient port the zulip adapter declares in R (http.ts:338, adapter.ts:370) — no socket, no Bun.serve, no real FetchHttpClient.

Today every zulip test provides the real FetchHttpClient.layer pointed at a fake Bun.serve. That fakes Zulip's wire (a thing we don't own) over a real socket — not the port we own. This stub closes that gap.

How

packages/testing/stub-http-client.ts exposes makeStubHttpClient: Effect<StubHttpClient>:

  • Responses keyed by method + path. respond(method, path, res) sets a sticky default; respondSequence(method, path, [...]) queues responses consumed one-per-request, falling back to the sticky default once drained — the seam the event-pump needs (a GET /events chain advancing last_event_id).
  • Real wire shapes. Responses are built as web Responses and wrapped with HttpClientResponse.fromWeb, so they round-trip .status / .text / .arrayBuffer / .headers exactly as a fetched response would. Bodies pin to Zulip envelope shapes (cross-checked against the vendored Zulip source).
  • Request capture. Requests are serialized to a web Request the same way FetchHttpClient serializes them, then captured — so a form-urlencoded or multipart body is recorded byte-for-byte as the wire would have seen it. captured exposes url / method / headers / body.
  • Drop-in. Effect.provideService(HttpClient.HttpClient, stub.client) replaces FetchHttpClient.layer with no other change.

Effect-native throughout: Ref-backed registry (HashMap with a Data.struct route key) + capture log, register/inspect as Effects.

Scope

Strictly the seam (comms-e5vm.1). Not the effectTest harness (comms-30hq), not migrating any tests onto the stub (comms-e5vm.2, which this unblocks). No adapter changes.

13 focused tests cover keying, sequence + drain-fallback, capture (method/url/headers/form-body/query-string), status passthrough, unregistered-route 404, Uint8Array download bodies, the no-socket guarantee (unroutable host still resolves), and drop-in service provision.

Build a reusable test HttpClient on @effect/platform
HttpClient.make(req => Effect<response>) that fakes the HttpClient PORT
the zulip adapter declares in R — no socket, no Bun.serve, no real
FetchHttpClient.

Responses are keyed by method+path with sticky defaults and one-per-
request sequence queues (the event-pump GET /events chain), built as web
Responses wrapped via HttpClientResponse.fromWeb so they round-trip
.status/.text/.arrayBuffer/.headers. Requests are serialized to a web
Request the same way FetchHttpClient serializes them, then captured for
url/method/headers/body assertions.

Drop-in for Effect.provideService(HttpClient.HttpClient, stub.client).
@GraemeF
GraemeF merged commit 4f923db into main Jun 13, 2026
2 checks passed
@GraemeF
GraemeF deleted the comms-e5vm.1 branch June 13, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant