|
|
@@ -16,8 +16,9 @@ import { HttpRecorder } from "@opencode-ai/http-recorder"
|
|
|
## Quickstart
|
|
|
|
|
|
Provide `cassetteLayer(name)` in place of (or layered over) your `HttpClient`.
|
|
|
-The first run records to `test/fixtures/recordings/<name>.json`; subsequent
|
|
|
-runs replay from it.
|
|
|
+By default the layer records on first run and replays on subsequent runs —
|
|
|
+no env-var ternary at the call site, and `CI=true` forces strict replay so
|
|
|
+missing cassettes fail loudly in CI rather than silently re-recording.
|
|
|
|
|
|
```ts
|
|
|
import { Effect } from "effect"
|
|
|
@@ -30,28 +31,22 @@ const program = Effect.gen(function* () {
|
|
|
return yield* response.json
|
|
|
})
|
|
|
|
|
|
-// Replay (default). Fails if the cassette is missing.
|
|
|
+// Records if the cassette is missing, replays if it exists.
|
|
|
+// In CI (CI=true) always replays — fails loudly on missing fixtures.
|
|
|
Effect.runPromise(program.pipe(Effect.provide(HttpRecorder.cassetteLayer("users/get-one"))))
|
|
|
|
|
|
-// Record. Hits the upstream and writes the cassette.
|
|
|
+// Force a refresh — always hits upstream and overwrites.
|
|
|
Effect.runPromise(program.pipe(Effect.provide(HttpRecorder.cassetteLayer("users/get-one", { mode: "record" }))))
|
|
|
```
|
|
|
|
|
|
-Set the mode from the environment in your test setup:
|
|
|
-
|
|
|
-```ts
|
|
|
-HttpRecorder.cassetteLayer("users/get-one", {
|
|
|
- mode: process.env.RECORD === "true" ? "record" : "replay",
|
|
|
-})
|
|
|
-```
|
|
|
-
|
|
|
## Modes
|
|
|
|
|
|
-| Mode | Behavior |
|
|
|
-| ------------- | -------------------------------------------------------------------- |
|
|
|
-| `replay` | Default. Match the request to a recorded interaction; error if none. |
|
|
|
-| `record` | Execute upstream, append the interaction, write the cassette. |
|
|
|
-| `passthrough` | Bypass the recorder entirely — just call upstream. |
|
|
|
+| Mode | Behavior |
|
|
|
+| ------------- | ----------------------------------------------------------------------------------- |
|
|
|
+| `auto` | Default. Replay if the cassette exists; record if missing. `CI=true` forces replay. |
|
|
|
+| `replay` | Strict — match the request to a recorded interaction; error if none. |
|
|
|
+| `record` | Execute upstream, append the interaction, write the cassette. |
|
|
|
+| `passthrough` | Bypass the recorder entirely — just call upstream. |
|
|
|
|
|
|
## Cassette format
|
|
|
|
|
|
@@ -101,7 +96,6 @@ secrets escape. Redaction is configured by composing a `Redactor`:
|
|
|
import { HttpRecorder, Redactor } from "@opencode-ai/http-recorder"
|
|
|
|
|
|
HttpRecorder.cassetteLayer("anthropic/messages", {
|
|
|
- mode: process.env.RECORD === "true" ? "record" : "replay",
|
|
|
redactor: Redactor.defaults({
|
|
|
requestHeaders: { allow: ["content-type", "anthropic-version"] },
|
|
|
url: { transform: (url) => url.replace(/\/accounts\/[^/]+/, "/accounts/{account}") },
|
|
|
@@ -157,7 +151,6 @@ const program = Effect.gen(function* () {
|
|
|
const cassette = yield* HttpRecorder.Cassette.Service
|
|
|
const executor = yield* HttpRecorder.makeWebSocketExecutor({
|
|
|
name: "ws/subscribe",
|
|
|
- mode: process.env.RECORD === "true" ? "record" : "replay",
|
|
|
cassette,
|
|
|
live: liveExecutor,
|
|
|
})
|
|
|
@@ -188,7 +181,7 @@ const audit = Effect.gen(function* () {
|
|
|
|
|
|
```ts
|
|
|
type RecordReplayOptions = {
|
|
|
- mode?: "record" | "replay" | "passthrough" // default: "replay"
|
|
|
+ mode?: "auto" | "replay" | "record" | "passthrough" // default: "auto" (CI=true forces "replay")
|
|
|
directory?: string // default: <cwd>/test/fixtures/recordings
|
|
|
metadata?: Record<string, unknown> // merged into cassette.metadata
|
|
|
redactor?: Redactor // default: Redactor.defaults()
|