diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 544e04c536..5215b094ab 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -87,10 +87,6 @@ async function getModel(providerID: ProviderID, modelID: ModelID, ctx: InstanceC return run(ctx, (provider) => provider.getModel(providerID, modelID)) } -async function getLanguage(model: Provider.Model, ctx: InstanceContext) { - return run(ctx, (provider) => provider.getLanguage(model)) -} - async function closest(providerID: ProviderID, query: string[], ctx: InstanceContext) { return run(ctx, (provider) => provider.closest(providerID, query)) } @@ -99,10 +95,6 @@ async function getSmallModel(providerID: ProviderID, ctx: InstanceContext) { return run(ctx, (provider) => provider.getSmallModel(providerID)) } -async function defaultModel(ctx: InstanceContext) { - return run(ctx, (provider) => provider.defaultModel()) -} - function paid(providers: Awaited>) { const item = providers[ProviderID.make("opencode")] expect(item).toBeDefined() @@ -361,62 +353,42 @@ it.instance( }, ) -test("env variable takes precedence, config merges options", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - provider: { - anthropic: { - options: { - timeout: 60000, - chunkTimeout: 15000, - }, - }, +it.instance( + "env variable takes precedence, config merges options", + Effect.gen(function* () { + yield* setProcessEnv("ANTHROPIC_API_KEY", "env-api-key") + const providers = yield* Provider.Service.use((provider) => provider.list()) + expect(providers[ProviderID.anthropic]).toBeDefined() + // Config options should be merged + expect(providers[ProviderID.anthropic].options.timeout).toBe(60000) + expect(providers[ProviderID.anthropic].options.chunkTimeout).toBe(15000) + }), + { + config: { + provider: { + anthropic: { + options: { + timeout: 60000, + chunkTimeout: 15000, }, - }), - ) + }, + }, }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - await set(ctx, "ANTHROPIC_API_KEY", "env-api-key") - const providers = await list(ctx) - expect(providers[ProviderID.anthropic]).toBeDefined() - // Config options should be merged - expect(providers[ProviderID.anthropic].options.timeout).toBe(60000) - expect(providers[ProviderID.anthropic].options.chunkTimeout).toBe(15000) - }, - }) -}) + }, +) -test("getModel returns model for valid provider/model", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - await set(ctx, "ANTHROPIC_API_KEY", "test-api-key") - const model = await getModel(ProviderID.anthropic, ModelID.make("claude-sonnet-4-20250514"), ctx) - expect(model).toBeDefined() - expect(String(model.providerID)).toBe("anthropic") - expect(String(model.id)).toBe("claude-sonnet-4-20250514") - const language = await getLanguage(model, ctx) - expect(language).toBeDefined() - }, - }) -}) +it.instance("getModel returns model for valid provider/model", () => + Effect.gen(function* () { + yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key") + const provider = yield* Provider.Service + const model = yield* provider.getModel(ProviderID.anthropic, ModelID.make("claude-sonnet-4-20250514")) + expect(model).toBeDefined() + expect(String(model.providerID)).toBe("anthropic") + expect(String(model.id)).toBe("claude-sonnet-4-20250514") + const language = yield* provider.getLanguage(model) + expect(language).toBeDefined() + }), +) test("getModel throws ModelNotFoundError for invalid model", async () => { await using tmp = await tmpdir({ @@ -469,50 +441,25 @@ test("parseModel handles model IDs with slashes", () => { expect(String(result.modelID)).toBe("anthropic/claude-3-opus") }) -test("defaultModel returns first available model when no config set", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - await set(ctx, "ANTHROPIC_API_KEY", "test-api-key") - const model = await defaultModel(ctx) - expect(model.providerID).toBeDefined() - expect(model.modelID).toBeDefined() - }, - }) -}) +it.instance("defaultModel returns first available model when no config set", () => + Effect.gen(function* () { + yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key") + const model = yield* Provider.Service.use((provider) => provider.defaultModel()) + expect(model.providerID).toBeDefined() + expect(model.modelID).toBeDefined() + }), +) -test("defaultModel respects config model setting", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - model: "anthropic/claude-sonnet-4-20250514", - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - await set(ctx, "ANTHROPIC_API_KEY", "test-api-key") - const model = await defaultModel(ctx) - expect(String(model.providerID)).toBe("anthropic") - expect(String(model.modelID)).toBe("claude-sonnet-4-20250514") - }, - }) -}) +it.instance( + "defaultModel respects config model setting", + Effect.gen(function* () { + yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key") + const model = yield* Provider.Service.use((provider) => provider.defaultModel()) + expect(String(model.providerID)).toBe("anthropic") + expect(String(model.modelID)).toBe("claude-sonnet-4-20250514") + }), + { config: { model: "anthropic/claude-sonnet-4-20250514" } }, +) it.instance( "provider with baseURL from config", diff --git a/perf/test-suite.md b/perf/test-suite.md index 65f35bf57b..f6f70dc57b 100644 --- a/perf/test-suite.md +++ b/perf/test-suite.md @@ -70,6 +70,7 @@ Repeated setup work, long sleeps/timeouts, serial integration tests, filesystem/ | File watcher readiness can write before async native subscriptions are active | Retried short readiness writes and accepted symlink-realpath HEAD events | failed | 4.62s | keep | Three sequential focused watcher runs passed: 4.62s, 4.57s, 4.64s; full suite no longer failed in `watcher.test.ts`. | | First provider config/env/filtering block can use Effect-aware instance fixtures | Migrated six `tmpdir` + `withTestInstance` cases to `it.instance` | 6.06s | 6.07s | keep | Neutral timing, but removes manual config file writes and instance plumbing; use as the pattern for later provider slices. | | Custom provider/model config cases can use Effect-aware instance fixtures | Migrated three more config-heavy provider cases to `it.instance` | 6.07s | 6.12s | keep | Neutral timing within noise, but continues removing manual config file writes on top of the first provider fixture PR. | +| Provider env precedence and model lookup cases can use Effect-aware instance fixtures | Migrated four more provider lookup/default-model cases to `it.instance` | 6.12s | 6.36s | keep | Noisy 5-run median; kept as a small stacked cleanup slice but do not claim speedup from this migration. | ## Profiling Results