The tmpdir function in fixture/fixture.ts creates temporary directories for tests with automatic cleanup.
import { tmpdir } from "./fixture/fixture"
test("example", async () => {
await using tmp = await tmpdir()
// tmp.path is the temp directory path
// automatically cleaned up when test ends
})
git?: boolean - Initialize a git repo with a root commitconfig?: Partial<Config.Info> - Write an opencode.json config fileinit?: (dir: string) => Promise<T> - Custom setup function, returns value accessible as tmp.extradispose?: (dir: string) => Promise<T> - Custom cleanup functionGit repository:
await using tmp = await tmpdir({ git: true })
With config file:
await using tmp = await tmpdir({
config: { model: "test/model", username: "testuser" },
})
Custom initialization (returns extra data):
await using tmp = await tmpdir<string>({
init: async (dir) => {
await Bun.write(path.join(dir, "file.txt"), "content")
return "extra data"
},
})
// Access extra data via tmp.extra
console.log(tmp.extra) // "extra data"
With cleanup:
await using tmp = await tmpdir({
init: async (dir) => {
const specialDir = path.join(dir, "special")
await fs.mkdir(specialDir)
return specialDir
},
dispose: async (dir) => {
// Custom cleanup logic
await fs.rm(path.join(dir, "special"), { recursive: true })
},
})
path: string - Absolute path to the temp directory (realpath resolved)extra: T - Value returned by the init function[Symbol.asyncDispose] - Enables automatic cleanup via await usingopencode-test-await using for automatic cleanup when the variable goes out of scopeUse testEffect(...) from test/lib/effect.ts for tests that exercise Effect services or Effect-based workflows.
import { describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const it = testEffect(Layer.mergeAll(MyService.defaultLayer))
describe("my service", () => {
it.live("does the thing", () =>
provideTmpdirInstance(() =>
Effect.gen(function* () {
const svc = yield* MyService.Service
const out = yield* svc.run()
expect(out).toEqual("ok")
}),
),
)
})
it.effect vs it.liveit.effect(...) when the test should run with TestClock and TestConsole.it.live(...) when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.it.live(...).Prefer the Effect-aware helpers from fixture/fixture.ts instead of building a manual runtime in each test.
tmpdirScoped(options?) creates a scoped temp directory and cleans it up when the Effect scope closes.provideInstance(dir)(effect) is the low-level helper. It does not create a directory; it just runs an Effect with Instance.current bound to dir.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.provideTmpdirServer((input) => effect, options?) does the same, but also provides the test LLM server.Use provideTmpdirInstance(...) by default when a test only needs one temp instance. Use tmpdirScoped() plus provideInstance(...) when a test needs multiple directories, custom setup before binding, or needs to switch instance context within one test.
const it = testEffect(...) near the top of the file.Effect.gen(function* () { ... }).yield* MyService.Service or yield* MyTool.ManagedRuntime, attach(...), or ad hoc run(...) wrappers when testEffect(...) already provides the runtime.provideTmpdirInstance(...) or provideInstance(...) over manual Instance.provide(...) inside Promise-style tests.