import { describe, expect } from "bun:test" import { Effect } from "effect" import { Credential } from "@opencode/core/credential" import { Integration } from "@opencode/core/integration" import { Plugin } from "@opencode/core/plugin" import { PluginHost } from "@opencode/core/plugin/host" import { AmazonBedrockPlugin } from "@opencode/core/plugin/provider/amazon-bedrock" import { Provider } from "@opencode/core/provider" import { testEffect } from "../lib/effect" import { PluginTestLayer } from "./fixture" const it = testEffect(PluginTestLayer) const addPlugin = Effect.fn(function* () { const plugin = yield* Plugin.Service const host = yield* PluginHost.make(plugin) yield* AmazonBedrockPlugin.effect(host) }) function required(value: T | undefined): T { if (value === undefined) throw new Error("Expected value") return value } function withEnv(vars: Record, fx: () => Effect.Effect) { 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 noAmbientAWS = { AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined, AWS_WEB_IDENTITY_TOKEN_FILE: undefined, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: undefined, AWS_CONTAINER_CREDENTIALS_FULL_URI: undefined, AWS_REGION: undefined, AWS_DEFAULT_REGION: undefined, } const seedBedrock = Effect.fn(function* (settings?: Provider.Settings) { const catalog = yield* Provider.Service yield* catalog.transform((catalog) => { catalog.update(Provider.ID.amazonBedrock, (item) => { item.package = "@opencode/ai/providers/amazon-bedrock" if (settings) item.settings = settings }) }) return catalog }) describe("AmazonBedrockPlugin", () => { it.effect("moves endpoint setting to baseURL", () => withEnv(noAmbientAWS, () => Effect.gen(function* () { const catalog = yield* seedBedrock({ endpoint: "https://bedrock.example" }) yield* addPlugin() const result = required(yield* catalog.get(Provider.ID.amazonBedrock)) expect(result.package).toBe("@opencode/ai/providers/amazon-bedrock") expect(result.settings).toEqual({ baseURL: "https://bedrock.example", region: "us-east-1" }) }), ), ) it.effect("gives the package the profile of a stored AWS profile login", () => withEnv(noAmbientAWS, () => Effect.gen(function* () { const catalog = yield* seedBedrock() const credentials = yield* Credential.Service yield* credentials.create({ integrationID: Integration.ID.make(Provider.ID.amazonBedrock), value: Credential.External.make({ type: "external", methodID: Integration.MethodID.make("aws-profile"), metadata: { profile: "work" }, }), }) yield* addPlugin() const result = required(yield* catalog.get(Provider.ID.amazonBedrock)) expect(result.settings).toEqual({ profile: "work", region: "us-east-1" }) expect(result.activation).toBe("enabled") }), ), ) it.effect("keeps an explicit baseURL over endpoint", () => withEnv(noAmbientAWS, () => Effect.gen(function* () { const catalog = yield* seedBedrock({ baseURL: "https://base.example", endpoint: "https://endpoint.example" }) yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).settings).toEqual({ baseURL: "https://base.example", region: "us-east-1", }) }), ), ) it.effect("only treats the bearer token env var as a key credential", () => Effect.gen(function* () { const integrations = yield* Integration.Service const integrationID = Integration.ID.make(Provider.ID.amazonBedrock) yield* integrations.transform((editor) => { editor.method.update({ integrationID, method: { type: "key" } }) editor.method.update({ integrationID, method: { type: "env", names: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_BEARER_TOKEN_BEDROCK"], }, }) }) yield* addPlugin() expect((yield* integrations.get(integrationID))?.methods.filter((method) => method.type === "env")).toEqual([ { type: "env", names: ["AWS_BEARER_TOKEN_BEDROCK"] }, ]) }), ) it.effect("leaves activation on auto without ambient AWS configuration", () => withEnv(noAmbientAWS, () => Effect.gen(function* () { const catalog = yield* seedBedrock() yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).activation).toBe("auto") }), ), ) for (const name of Object.keys(noAmbientAWS).filter((name) => !name.includes("REGION"))) { it.effect(`enables the provider when ${name} is set`, () => withEnv({ ...noAmbientAWS, [name]: "value" }, () => Effect.gen(function* () { const catalog = yield* seedBedrock() yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).activation).toBe("enabled") }), ), ) } it.effect("enables the provider when a profile is configured", () => withEnv(noAmbientAWS, () => Effect.gen(function* () { const catalog = yield* seedBedrock({ profile: "work" }) yield* addPlugin() const result = required(yield* catalog.get(Provider.ID.amazonBedrock)) expect(result.activation).toBe("enabled") expect(result.settings).toEqual({ profile: "work", region: "us-east-1" }) }), ), ) it.effect("does not override a disabled provider", () => withEnv({ ...noAmbientAWS, AWS_PROFILE: "work" }, () => Effect.gen(function* () { const catalog = yield* Provider.Service yield* catalog.transform((catalog) => { catalog.update(Provider.ID.amazonBedrock, (item) => { item.package = "@opencode/ai/providers/amazon-bedrock" item.activation = "disabled" }) }) yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).activation).toBe("disabled") }), ), ) it.effect("fills region from AWS_REGION then AWS_DEFAULT_REGION without overriding config", () => withEnv({ ...noAmbientAWS, AWS_REGION: "eu-west-1", AWS_DEFAULT_REGION: "us-west-2" }, () => Effect.gen(function* () { const catalog = yield* seedBedrock() yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).settings).toEqual({ region: "eu-west-1", }) yield* catalog.transform((catalog) => { catalog.update(Provider.ID.amazonBedrock, (item) => { item.settings = { region: "ap-southeast-2" } }) }) expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).settings).toEqual({ region: "ap-southeast-2", }) }), ), ) it.effect("falls back to AWS_DEFAULT_REGION then us-east-1", () => withEnv({ ...noAmbientAWS, AWS_DEFAULT_REGION: "us-west-2" }, () => Effect.gen(function* () { const catalog = yield* seedBedrock() yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.amazonBedrock)).settings).toEqual({ region: "us-west-2", }) const fallback = yield* Effect.gen(function* () { yield* catalog.reload() return required(yield* catalog.get(Provider.ID.amazonBedrock)).settings }).pipe((fx) => withEnv({ AWS_DEFAULT_REGION: undefined }, () => fx)) expect(fallback).toEqual({ region: "us-east-1" }) }), ), ) it.effect("applies to Mantle and native Bedrock packages", () => withEnv({ ...noAmbientAWS, AWS_PROFILE: "work" }, () => Effect.gen(function* () { const catalog = yield* Provider.Service yield* catalog.transform((catalog) => { catalog.update(Provider.ID.make("mantle"), (item) => { item.package = "@opencode/ai/providers/amazon-bedrock/mantle/responses" }) catalog.update(Provider.ID.make("native"), (item) => { item.package = "@opencode/ai/providers/amazon-bedrock" }) catalog.update(Provider.ID.make("other"), (item) => { item.package = "@opencode/ai/providers/anthropic" }) }) yield* addPlugin() expect(required(yield* catalog.get(Provider.ID.make("mantle"))).activation).toBe("enabled") expect(required(yield* catalog.get(Provider.ID.make("native"))).activation).toBe("enabled") expect(required(yield* catalog.get(Provider.ID.make("other"))).activation).toBe("auto") }), ), ) })