test(core): simplify provider test setup (#45635)

This commit is contained in:
Kit Langton 2026-08-28 13:25:53 -04:00 committed by GitHub
parent e50c89834e
commit 374d317412
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 71 additions and 267 deletions

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Money } from "@opencode-ai/schema/money"
import { Document, Info, type Entry } from "@opencode-ai/schema/config"
import { Effect, Schema, Stream } from "effect"
import { Effect, Schema } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Config } from "@opencode-ai/core/config"
import { ConfigProviderPlugin } from "@opencode-ai/core/config/plugin/provider"
@ -12,6 +12,7 @@ import { ModelResolver } from "@opencode-ai/core/model-resolver"
import { Plugin } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { Provider } from "@opencode-ai/core/provider"
import { withEnv } from "../fixture/env"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "../plugin/fixture"
@ -28,27 +29,6 @@ function required<T>(value: T | undefined): T {
return value
}
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
Object.entries(vars).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
effect,
(previous) =>
Effect.sync(() =>
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}),
),
)
}
const decode = Schema.decodeUnknownSync(Info)
describe("ConfigProviderPlugin.Plugin", () => {

View file

@ -0,0 +1,22 @@
import { Effect } from "effect"
export function withEnv<A, E, R>(variables: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(variables).map((key) => [key, process.env[key]]))
Object.entries(variables).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
effect,
(previous) =>
Effect.sync(() => {
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
}),
)
}

View file

@ -14,6 +14,7 @@ import { ModelsDevPlugin } from "@opencode-ai/core/plugin/models-dev"
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
import { Provider } from "@opencode-ai/core/provider"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { withEnv } from "../fixture/env"
import { location } from "../fixture/location"
import { testEffect } from "../lib/effect"
import { catalogHost, host, integrationHost } from "./host"
@ -29,27 +30,6 @@ const it = testEffect(layer)
const models = (file: string) =>
AppNodeBuilder.build(ModelsDev.node, [[ModelsDev.node, ModelsDev.configured({ file, fetch: false })]])
function withEnv<A, E, R>(variables: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(variables).map((key) => [key, process.env[key]]))
Object.entries(variables).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
effect,
(previous) =>
Effect.sync(() => {
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
}),
)
}
describe("ModelsDevPlugin", () => {
it.effect("projects normalized models.dev snapshots into the catalog", () =>
Effect.gen(function* () {

View file

@ -7,6 +7,7 @@ import { Plugin } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { CloudflareAIGatewayPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-ai-gateway"
import { Provider } from "@opencode-ai/core/provider"
import { withEnv } from "../fixture/env"
import { Integration } from "@opencode-ai/core/integration"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
@ -15,32 +16,10 @@ const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const host = yield* PluginHost.make(plugin)
yield* CloudflareAIGatewayPlugin.effect(host)
})
function withEnv<A, E, R>(vars: Record<string, string | undefined>, fx: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
Object.entries(vars).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
fx,
(previous) =>
Effect.sync(() => {
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
}),
)
}
const aiGatewayCalls: Record<string, unknown>[] = []
const unifiedCalls: string[] = []
const gatewayModelCalls: unknown[] = []
@ -108,9 +87,8 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv({ CLOUDFLARE_ACCOUNT_ID: undefined, CLOUDFLARE_GATEWAY_ID: undefined }, () =>
Effect.gen(function* () {
yield* addPlugin()
expect(
(yield* (yield* Integration.Service).get(Integration.ID.make("cloudflare-ai-gateway")))?.methods,
).toContainEqual({
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("cloudflare-ai-gateway")))?.methods).toContainEqual({
type: "key",
label: "Gateway API token",
form: [
@ -132,7 +110,6 @@ describe("CloudflareAIGatewayPlugin", () => {
},
() =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
@ -153,7 +130,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv(), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -198,7 +174,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv(), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -228,7 +203,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv(), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -266,7 +240,6 @@ describe("CloudflareAIGatewayPlugin", () => {
() =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -298,7 +271,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv({ CLOUDFLARE_API_TOKEN: undefined, CF_AIG_TOKEN: "cf-aig-token" }), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -321,7 +293,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv({ CLOUDFLARE_ACCOUNT_ID: undefined, CLOUDFLARE_GATEWAY_ID: undefined }), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -345,7 +316,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv({ CLOUDFLARE_API_TOKEN: undefined, CF_AIG_TOKEN: undefined }), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -375,7 +345,6 @@ describe("CloudflareAIGatewayPlugin", () => {
() =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
@ -384,9 +353,11 @@ describe("CloudflareAIGatewayPlugin", () => {
}),
)
yield* addPlugin()
expect(
(yield* (yield* Integration.Service).get(Integration.ID.make("cloudflare-ai-gateway")))?.methods,
).toContainEqual({ type: "key", label: "Gateway API token" })
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("cloudflare-ai-gateway")))?.methods).toContainEqual({
type: "key",
label: "Gateway API token",
})
const result = yield* aisdk.runSDK({
model: Model.Info.make({
@ -408,7 +379,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv(), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
@ -440,7 +410,6 @@ describe("CloudflareAIGatewayPlugin", () => {
withEnv(cloudflareEnv(), () =>
Effect.gen(function* () {
resetCalls()
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()

View file

@ -1,12 +1,12 @@
import { AISDK } from "@opencode-ai/core/aisdk"
import { describe, expect, mock } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Model } from "@opencode-ai/core/model"
import { Plugin } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { GitLabPlugin } from "@opencode-ai/core/plugin/provider/gitlab"
import { Provider } from "@opencode-ai/core/provider"
import { withEnv } from "../fixture/env"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
@ -15,32 +15,10 @@ const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const host = yield* PluginHost.make(plugin)
yield* GitLabPlugin.effect(host)
})
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
Object.entries(vars).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
effect,
(previous) =>
Effect.sync(() =>
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}),
),
)
}
void mock.module("gitlab-ai-provider", () => ({
VERSION: "test-version",
createGitLab: (options: Record<string, unknown>) => {
@ -64,7 +42,6 @@ describe("GitLabPlugin", () => {
() =>
Effect.gen(function* () {
gitlabSDKOptions.length = 0
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
@ -102,7 +79,6 @@ describe("GitLabPlugin", () => {
() =>
Effect.gen(function* () {
gitlabSDKOptions.length = 0
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
@ -128,7 +104,6 @@ describe("GitLabPlugin", () => {
() =>
Effect.gen(function* () {
gitlabSDKOptions.length = 0
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
@ -170,7 +145,6 @@ describe("GitLabPlugin", () => {
it.effect("ignores non-GitLab SDK packages", () =>
Effect.gen(function* () {
gitlabSDKOptions.length = 0
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
@ -189,7 +163,6 @@ describe("GitLabPlugin", () => {
it.effect("uses workflowChat for duo workflow models and preserves selectedModelRef", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: [string, unknown][] = []
yield* addPlugin()
@ -223,7 +196,6 @@ describe("GitLabPlugin", () => {
it.effect("uses exact static workflow model ids when the provider recognizes them", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: [string, unknown][] = []
yield* addPlugin()
@ -251,7 +223,6 @@ describe("GitLabPlugin", () => {
it.effect("uses provider feature flags instead of model settings feature flags", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: [string, unknown][] = []
yield* addPlugin()
@ -278,7 +249,6 @@ describe("GitLabPlugin", () => {
it.effect("uses agenticChat with provider aiGatewayHeaders and feature flags for normal models", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: [string, unknown][] = []
yield* addPlugin()

View file

@ -3,13 +3,13 @@ import { Money } from "@opencode-ai/schema/money"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Credential } from "@opencode-ai/core/credential"
import { Bus } from "@opencode-ai/core/bus"
import { Integration } from "@opencode-ai/core/integration"
import { Model } from "@opencode-ai/core/model"
import { Plugin } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode"
import { Provider } from "@opencode-ai/core/provider"
import { withEnv } from "../fixture/env"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
@ -18,12 +18,7 @@ const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const host = yield* PluginHost.make(plugin)
const bus = yield* Bus.Service
const integration = yield* Integration.Service
yield* OpencodePlugin.effect(host).pipe(
Effect.provideService(Bus.Service, bus),
Effect.provideService(Integration.Service, integration),
)
yield* OpencodePlugin.effect(host)
})
function required<T>(value: T | undefined): T {
@ -45,27 +40,6 @@ function eventually<A>(
})
}
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
Object.entries(vars).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
})
return previous
}),
effect,
(previous) =>
Effect.sync(() =>
Object.entries(previous).forEach(([key, value]) => {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}),
),
)
}
const cost = (input: number, output = 0) => [
{
input: Money.USDPerMillionTokens.make(input),
@ -81,7 +55,8 @@ describe("OpencodePlugin", () => {
it.effect("registers account and service account methods", () =>
Effect.gen(function* () {
yield* addPlugin()
expect((yield* (yield* Integration.Service).get(Integration.ID.make("opencode")))?.methods).toEqual([
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("opencode")))?.methods).toEqual([
{
id: Integration.MethodID.make("device"),
type: "oauth",
@ -140,7 +115,8 @@ describe("OpencodePlugin", () => {
expect(requests).toContain("POST /console/auth/device/token")
expect(requests).toContain("GET /console/api/user")
expect(requests).toContain("GET /console/api/orgs")
expect((yield* (yield* Credential.Service).list(Integration.ID.make("opencode")))[0]?.value).toMatchObject({
const credentials = yield* Credential.Service
expect((yield* credentials.list(Integration.ID.make("opencode")))[0]?.value).toMatchObject({
metadata: { server: `${server.url.origin}/console` },
})
}),
@ -166,7 +142,8 @@ describe("OpencodePlugin", () => {
(server) =>
Effect.gen(function* () {
yield* addPlugin()
const error = yield* (yield* Integration.Service).oauth
const integrations = yield* Integration.Service
const error = yield* integrations.oauth
.connect({
integrationID: Integration.ID.make("opencode"),
methodID: Integration.MethodID.make("device"),
@ -183,7 +160,8 @@ describe("OpencodePlugin", () => {
it.effect("rejects non-HTTP OpenCode servers", () =>
Effect.gen(function* () {
yield* addPlugin()
const error = yield* (yield* Integration.Service).oauth
const integrations = yield* Integration.Service
const error = yield* integrations.oauth
.connect({
integrationID: Integration.ID.make("opencode"),
methodID: Integration.MethodID.make("device"),
@ -198,7 +176,8 @@ describe("OpencodePlugin", () => {
it.effect("rejects non-string OpenCode servers", () =>
Effect.gen(function* () {
yield* addPlugin()
const error = yield* (yield* Integration.Service).oauth
const integrations = yield* Integration.Service
const error = yield* integrations.oauth
.connect({
integrationID: Integration.ID.make("opencode"),
methodID: Integration.MethodID.make("device"),
@ -299,7 +278,8 @@ describe("OpencodePlugin", () => {
settings: { baseURL: `${server.url.origin}/v1`, custom: "value" },
headers: { "x-org-id": "org" },
})
expect(yield* (yield* Integration.Service).get(Integration.ID.make("remote"))).toBeUndefined()
const integrations = yield* Integration.Service
expect(yield* integrations.get(Integration.ID.make("remote"))).toBeUndefined()
const model = required(yield* catalog.model.get(Provider.ID.make("remote"), Model.ID.make("model")))
expect(model).toMatchObject({
@ -365,19 +345,9 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.opencode, () => {})
catalog.model.update(Provider.ID.opencode, Model.ID.make("paid"), (draft) => {
draft.cost = cost(1)
})
})
yield* addPlugin()
@ -392,19 +362,9 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("free")),
modelID: Model.ID.make("free"),
package: Provider.aisdk("test-provider"),
cost: cost(0),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.opencode, () => {})
catalog.model.update(Provider.ID.opencode, Model.ID.make("free"), (draft) => {
draft.cost = cost(0)
})
})
yield* addPlugin()
@ -421,19 +381,9 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("output-only")),
modelID: Model.ID.make("output-only"),
package: Provider.aisdk("test-provider"),
cost: cost(0, 1),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.opencode, () => {})
catalog.model.update(Provider.ID.opencode, Model.ID.make("output-only"), (draft) => {
draft.cost = cost(0, 1)
})
})
yield* addPlugin()
@ -450,19 +400,9 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.opencode, () => {})
catalog.model.update(Provider.ID.opencode, Model.ID.make("paid"), (draft) => {
draft.cost = cost(1)
})
})
yield* addPlugin()
@ -484,19 +424,9 @@ describe("OpencodePlugin", () => {
})
})
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.opencode, () => {})
catalog.model.update(Provider.ID.opencode, Model.ID.make("paid"), (draft) => {
draft.cost = cost(1)
})
})
yield* addPlugin()
@ -511,23 +441,12 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
settings: { apiKey: "configured" },
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, (draft) => {
draft.package = provider.package
catalog.provider.update(Provider.ID.opencode, (draft) => {
draft.package = Provider.aisdk("test-provider")
draft.settings = { apiKey: "configured" }
})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.model.update(Provider.ID.opencode, Model.ID.make("paid"), (draft) => {
draft.cost = cost(1)
})
})
yield* addPlugin()
@ -542,19 +461,9 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.openai),
package: Provider.aisdk("test-provider"),
})
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
catalog.model.update(provider.id, model.id, (draft) => {
draft.cost = [...model.cost]
catalog.provider.update(Provider.ID.openai, () => {})
catalog.model.update(Provider.ID.openai, Model.ID.make("paid"), (draft) => {
draft.cost = cost(1)
})
})
yield* addPlugin()

View file

@ -7,6 +7,7 @@ import { PluginHost } from "@opencode-ai/core/plugin/host"
import { Npm } from "@opencode-ai/util/npm"
import { SapAICorePlugin } from "@opencode-ai/core/plugin/provider/sap-ai-core"
import { Provider } from "@opencode-ai/core/provider"
import { withEnv } from "../fixture/env"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
@ -20,32 +21,10 @@ const npm = Npm.Service.of({
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const host = yield* PluginHost.make(plugin)
yield* SapAICorePlugin.effect(host).pipe(Effect.provideService(Npm.Service, npm))
})
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
for (const [key, value] of Object.entries(vars)) {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}
return previous
}),
effect,
(previous) =>
Effect.sync(() => {
for (const [key, value] of Object.entries(previous)) {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}
}),
)
}
function model(providerID: string) {
return Model.Info.make({
...Model.Info.default(Provider.ID.make(providerID), Model.ID.make("sap-model")),
@ -60,7 +39,6 @@ describe("SapAICorePlugin", () => {
{ AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" },
() =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdk = yield* aisdk.runSDK({
@ -83,7 +61,6 @@ describe("SapAICorePlugin", () => {
},
() =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdk = yield* aisdk.runSDK({
@ -102,7 +79,6 @@ describe("SapAICorePlugin", () => {
{ AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" },
() =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdk = yield* aisdk.runSDK({
@ -118,7 +94,6 @@ describe("SapAICorePlugin", () => {
it.effect("uses the callable SDK for language selection", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdk = Object.assign((modelID: string) => ({ modelID, provider: "callable" }), {
@ -136,7 +111,6 @@ describe("SapAICorePlugin", () => {
{ AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" },
() =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdk = yield* aisdk.runSDK({