From 7f3e51453bd441117fed899c19991f3fc3aeb6a9 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 9 May 2026 00:05:05 -0400 Subject: [PATCH] test(server): use Layer.mock for partial Account service stub (#26472) --- packages/opencode/test/AGENTS.md | 16 ++++++++++++++ .../httpapi-account-error-mapping.test.ts | 22 +++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/opencode/test/AGENTS.md b/packages/opencode/test/AGENTS.md index 41372b15a0..335c6db622 100644 --- a/packages/opencode/test/AGENTS.md +++ b/packages/opencode/test/AGENTS.md @@ -142,3 +142,19 @@ Use `provideTmpdirInstance(...)` or `tmpdirScoped()` plus `provideInstance(...)` - Yield services directly with `yield* MyService.Service` or `yield* MyTool`. - Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers when `testEffect(...)` already provides the runtime. - When a test needs instance-local state, prefer `it.instance(...)` over manual `Instance.provide(...)` inside Promise-style tests. + +### Partial Service Stubs + +When a test only needs to override one or two methods of a service, prefer `Layer.mock` over a hand-rolled `Layer.succeed(Service, Service.of({ ... }))`. `Layer.mock` lets you supply just the methods that matter — anything else throws an `UnimplementedError` defect if the test accidentally calls it, which is exactly the signal you want. + +```typescript +import { Effect, Layer } from "effect" +import { Account } from "@/account/account" + +const failingAccountLayer = Layer.mock(Account.Service, { + orgsByAccount: () => + Effect.fail(new Account.AccountServiceError({ message: "simulated upstream failure" })), +}) +``` + +This is much shorter than stubbing every method with `Effect.void` / `Effect.succeed(...)` placeholders, and it keeps the test focused on the behaviour under test. diff --git a/packages/opencode/test/server/httpapi-account-error-mapping.test.ts b/packages/opencode/test/server/httpapi-account-error-mapping.test.ts index 784e07f840..77f48caa4d 100644 --- a/packages/opencode/test/server/httpapi-account-error-mapping.test.ts +++ b/packages/opencode/test/server/httpapi-account-error-mapping.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, mock, test } from "bun:test" -import { Effect, Layer, Option } from "effect" +import { Effect, Layer } from "effect" // Account.orgsByAccount() can fail with AccountServiceError when the // upstream Anthropic Console API is unreachable. The HTTP API used to @@ -17,22 +17,10 @@ import { Effect, Layer, Option } from "effect" const ORIG = await import("../../src/account/account") -const failingAccountLayer = Layer.succeed( - ORIG.Service, - ORIG.Service.of({ - active: () => Effect.succeed(Option.none()), - activeOrg: () => Effect.succeed(Option.none()), - list: () => Effect.succeed([]), - orgsByAccount: () => Effect.fail(new ORIG.AccountServiceError({ message: "simulated upstream failure" })), - remove: () => Effect.void, - use: () => Effect.void, - orgs: () => Effect.succeed([]), - config: () => Effect.succeed(Option.none()), - token: () => Effect.succeed(Option.none()), - login: () => Effect.fail(new ORIG.AccountServiceError({ message: "unused" })), - poll: () => Effect.fail(new ORIG.AccountServiceError({ message: "unused" })), - }), -) +const failingAccountLayer = Layer.mock(ORIG.Service, { + orgsByAccount: () => + Effect.fail(new ORIG.AccountServiceError({ message: "simulated upstream failure" })), +}) const mocked = { ...ORIG,