AGENTS.md
packages/opencode/test/AGENTS.mdAGENTS.md
Quality
81/100
Scores the file, not the repository.Length
1,076 words
18 headings · 9 code blocksRepository
193k
— · pushed 0 days agoLast changed
3 days ago
First indexed 3 days ago.1# Test Fixtures Guide23## Temporary Directory Fixture45The `tmpdir` function in `fixture/fixture.ts` creates temporary directories for tests with automatic cleanup.67### Basic Usage89```typescript10import { tmpdir } from "./fixture/fixture"1112test("example", async () => {13 await using tmp = await tmpdir()14 // tmp.path is the temp directory path15 // automatically cleaned up when test ends16})17```1819### Options2021- `git?: boolean` - Initialize a git repo with a root commit22- `config?: Partial<Config.Info>` - Write an `opencode.json` config file23- `init?: (dir: string) => Promise<T>` - Custom setup function, returns value accessible as `tmp.extra`24- `dispose?: (dir: string) => Promise<T>` - Custom cleanup function2526### Examples2728**Git repository:**2930```typescript31await using tmp = await tmpdir({ git: true })32```3334**With config file:**3536```typescript37await using tmp = await tmpdir({38 config: { model: "test/model", username: "testuser" },39})40```4142**Custom initialization (returns extra data):**4344```typescript45await using tmp = await tmpdir<string>({46 init: async (dir) => {47 await Bun.write(path.join(dir, "file.txt"), "content")48 return "extra data"49 },50})51// Access extra data via tmp.extra52console.log(tmp.extra) // "extra data"53```5455**With cleanup:**5657```typescript58await using tmp = await tmpdir({59 init: async (dir) => {60 const specialDir = path.join(dir, "special")61 await fs.mkdir(specialDir)62 return specialDir63 },64 dispose: async (dir) => {65 // Custom cleanup logic66 await fs.rm(path.join(dir, "special"), { recursive: true })67 },68})69```7071### Returned Object7273- `path: string` - Absolute path to the temp directory (realpath resolved)74- `extra: T` - Value returned by the `init` function75- `[Symbol.asyncDispose]` - Enables automatic cleanup via `await using`7677### Notes7879- Directories are created in the system temp folder with prefix `opencode-test-`80- Use `await using` for automatic cleanup when the variable goes out of scope81- Paths are sanitized to strip null bytes (defensive fix for CI environments)8283## Testing With Effects8485Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.8687### Core Pattern8889```typescript90import { describe, expect } from "bun:test"91import { Effect, Layer } from "effect"92import { testEffect } from "../lib/effect"9394const it = testEffect(Layer.mergeAll(MyService.defaultLayer))9596describe("my service", () => {97 it.instance("does the thing", () =>98 Effect.gen(function* () {99 const svc = yield* MyService.Service100 const out = yield* svc.run()101 expect(out).toEqual("ok")102 }),103 )104})105```106107### `it.effect` vs `it.live`108109- Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`.110- Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.111- Use `it.instance(...)` for live Effect tests that need a scoped temporary directory and instance context.112- Most integration-style tests in this package use `it.live(...)`.113114### Effect Fixtures115116Prefer the Effect-aware helpers from `fixture/fixture.ts` instead of building a manual runtime in each test.117118- `tmpdirScoped(options?)` creates a scoped temp directory and cleans it up when the Effect scope closes.119- `provideInstance(dir)(effect)` is the low-level helper. It does not create a directory; it runs an Effect with `InstanceRef` provided for `dir`.120- `provideTmpdirInstance((dir) => effect, options?)` is the convenience helper. It creates a temp directory, binds it as the active instance, and disposes the instance on cleanup.121- `provideTmpdirServer((input) => effect, options?)` does the same, but also provides the test LLM server.122123Use `it.instance(...)` by default when a test only needs one temp instance. Yield `TestInstance` from `fixture/fixture.ts` when the test needs the temp directory path:124125```typescript126import { TestInstance } from "../fixture/fixture"127128it.instance("uses the temp directory", () =>129 Effect.gen(function* () {130 const test = yield* TestInstance131 expect(test.directory).toContain("opencode-test-")132 }),133)134```135136Use `provideTmpdirInstance(...)` or `tmpdirScoped()` plus `provideInstance(...)` when a test needs multiple directories, custom setup before binding, needs to switch instance context within one test, or explicitly tests instance disposal/reload lifetime.137138### Style139140- Define `const it = testEffect(...)` near the top of the file.141- Keep the test body inside `Effect.gen(function* () { ... })`.142- Yield services directly with `yield* MyService.Service` or `yield* MyTool`.143- Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers when `testEffect(...)` already provides the runtime.144- When a test needs instance-local state, prefer `it.instance(...)` over manual `Instance.provide(...)` inside Promise-style tests.145146### Partial Service Stubs147148When 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.149150```typescript151import { Effect, Layer } from "effect"152import { Account } from "@/account/account"153154const failingAccountLayer = Layer.mock(Account.Service, {155 orgsByAccount: () => Effect.fail(new Account.AccountServiceError({ message: "simulated upstream failure" })),156})157```158159This is much shorter than stubbing every method with `Effect.void` / `Effect.succeed(...)` placeholders, and it keeps the test focused on the behaviour under test.160161## Synchronizing With Concurrent Work162163### The Anti-Pattern164165Using `Effect.sleep(N)` or `setTimeout` as a "wait for the forked fiber to be ready" hack races the scheduler. The forked fiber may not have reached the synchronization point within `N` ms on a slow CI host, and the test fails intermittently. See PR #27622 for a concrete flake that fell out of this exact pattern.166167### The Fix168169Wait on a **published readiness signal**, not wall-clock time. Available affordances:170171- `pollWithTimeout(effect, message, duration?)` from `test/lib/effect.ts` — repeatedly run a predicate effect until it returns a non-`undefined` value, with a timeout.172- `awaitWithTimeout(effect, message, duration?)` from `test/lib/effect.ts` — wrap any effect with `Effect.timeoutOrElse` and a custom error message.173- `llm.wait(n)` from `test/lib/llm-server.ts` — wait until the mock LLM has received `n` HTTP calls.174- `SessionStatus.Service` `.get(sessionID)` — observable per-session state (`{ type: "busy" | "idle" | ... }`).175- `BackgroundJob.wait({ id, timeout })` from `src/background/job.ts` — wait for a background job to complete.176- Bus subscriptions — fork `Stream.runForEach(bus.subscribe(Event), ...)` and open a `Latch` inside the callback to signal first-event readiness.177- `Deferred.await(deferred).pipe(Effect.timeoutOrElse(...))` for one-shot signals.178179### Example180181```ts182// Antipattern — race183yield * prompt.shell({ command: "sleep 30" }).pipe(Effect.forkChild)184yield * Effect.sleep(50)185yield * prompt.cancel(chat.id)186187// Fix — wait for a published readiness signal188yield * prompt.shell({ command: "sleep 30" }).pipe(Effect.forkChild)189yield *190 pollWithTimeout(191 Effect.gen(function* () {192 const s = yield* (yield* SessionStatus.Service).get(chat.id)193 return s.type === "busy" ? (true as const) : undefined194 }),195 "session never became busy",196 )197yield * prompt.cancel(chat.id)198```199200### When Fixed Sleeps Are OK201202- Testing debounce or throttle behavior, where the sleep **is** the test.203- Letting real wall-clock advance past a genuine timestamp resolution boundary (e.g. mtime granularity).204- Simulating network latency in race-regression tests that intentionally exercise ordering.205
Also in anomalyco/opencode
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| anomalyco/opencodepackages/core/src/tool/AGENTS.md · 193k | AGENTS.md | stylesecurity | 58/100 | 3 days ago | |
| anomalyco/opencodepackages/opencode/test/server/AGENTS.md · 193k | AGENTS.md | teststyleapi | 38/100 | 3 days ago | |
| anomalyco/opencodepackages/schema/AGENTS.md · 193k | AGENTS.md | styletypesdatabaseapi+1 | 65/100 | 3 days ago | |
| anomalyco/opencodepackages/opencode/AGENTS.md · 193k | AGENTS.md | styledatabaseapido-not | 81/100 | 3 days ago | |
| anomalyco/opencodeAGENTS.md · 193k | AGENTS.md | testlint-formatstyletypes+3 | 80/100 | 3 days ago | |
| anomalyco/opencodepackages/app/AGENTS.md · 193k | AGENTS.md | style | 56/100 | today | |
| anomalyco/opencodepackages/app/e2e/performance/AGENTS.md · 193k | AGENTS.md | no sections | 16/100 | 3 days ago | |
| anomalyco/opencodepackages/codemode/AGENTS.md · 193k | AGENTS.md | api | 43/100 | 3 days ago | |
| anomalyco/opencodepackages/desktop/AGENTS.md · 193k | AGENTS.md | no sections | 30/100 | today | |
| anomalyco/opencodepackages/effect-drizzle-sqlite/AGENTS.md · 193k | AGENTS.md | database | 38/100 | 3 days ago | |
| anomalyco/opencodepackages/llm/AGENTS.md · 193k | AGENTS.md | stylearchtesting-strategygit+1 | 57/100 | 3 days ago | |
| anomalyco/opencodepackages/opencode/src/server/routes/instance/httpapi/AGENTS.md · 193k | AGENTS.md | styleapi | 40/100 | 3 days ago | |
| anomalyco/opencodepackages/opencode/src/session/llm/AGENTS.md · 193k | AGENTS.md | arch | 61/100 | 3 days ago | |
| anomalyco/opencodepackages/session-ui/AGENTS.md · 193k | AGENTS.md | no sections | 30/100 | today | |
| anomalyco/opencodepackages/ui/AGENTS.md · 193k | AGENTS.md | no sections | 30/100 | today |
Diff against packages/core/src/tool/AGENTS.md Diff against packages/opencode/test/server/AGENTS.md Diff against packages/schema/AGENTS.md Diff against packages/opencode/AGENTS.md Diff against AGENTS.md Diff against packages/app/AGENTS.md Diff against packages/app/e2e/performance/AGENTS.md Diff against packages/codemode/AGENTS.md Diff against packages/desktop/AGENTS.md Diff against packages/effect-drizzle-sqlite/AGENTS.md Diff against packages/llm/AGENTS.md Diff against packages/opencode/src/server/routes/instance/httpapi/AGENTS.md Diff against packages/opencode/src/session/llm/AGENTS.md Diff against packages/session-ui/AGENTS.md Diff against packages/ui/AGENTS.md
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| trick77/agents-md-syncAGENTS.md · 2 | AGENTS.md | setupbuildteststyle+5 | 100/100 | 3 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 3 days ago | |
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 199k | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| aaif-goose/gooseAGENTS.md · 52k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TryGhost/Ghoste2e/AGENTS.md · 55k | AGENTS.md | setupteststylearch+2 | 100/100 | 3 days ago | |
| code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 67k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 2 days ago | |
| elastic/elasticsearchx-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/AGENTS.md · 78k | AGENTS.md | buildtestlint-formatstyle+2 | 100/100 | 3 days ago |
