opencode/packages/core/test/session-runner-retry.test.ts

47 lines
1.9 KiB
TypeScript

import { describe, expect } from "bun:test"
import { AIError, ProviderInternalError, TransportError } from "@opencode/ai/effect"
import { Agent } from "@opencode/schema/agent"
import { Model } from "@opencode/schema/model"
import { Provider } from "@opencode/core/provider"
import { SessionSchema } from "@opencode/core/session/schema"
import { SessionRunnerRetry } from "@opencode/core/session/runner/retry"
import { toSessionError } from "@opencode/core/session/to-session-error"
import { Effect } from "effect"
import { it } from "./lib/effect"
const timeout = new AIError({
reason: new TransportError({ message: "Timed out", transport: "http", operation: "request", code: "Timeout" }),
})
const internal = new AIError({ reason: new ProviderInternalError({ message: "internal" }) })
const input = (cause: AIError) => ({
cause,
error: toSessionError(cause),
agent: Agent.ID.make("build"),
model: Model.Ref.make({ id: Model.ID.make("model"), providerID: Provider.ID.make("provider") }),
hook: () => Effect.void,
retry: SessionRunnerRetry.isRetryable(cause),
})
// Decisions for `count` consecutive failures of the same cause within one step.
const decisions = (cause: AIError, count: number) =>
Effect.gen(function* () {
const policy = yield* SessionRunnerRetry.policy(SessionSchema.ID.make("ses_retry"))
const results: boolean[] = []
for (let i = 0; i < count; i++) results.push((yield* policy(input(cause))).retry)
return results
})
describe("SessionRunnerRetry.policy", () => {
it.effect("stops retrying transport timeouts after three attempts", () =>
Effect.gen(function* () {
expect(yield* decisions(timeout, 4)).toEqual([true, true, true, false])
}),
)
it.effect("keeps the general allowance for other transient failures", () =>
Effect.gen(function* () {
expect(yield* decisions(internal, 11)).toEqual([...Array(10).fill(true), false])
}),
)
})