amazon-bedrock.test.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { test, expect, mock } from "bun:test"
  2. import path from "path"
  3. // === Mocks ===
  4. // These mocks are required because Provider.list() triggers:
  5. // 1. BunProc.install("@aws-sdk/credential-providers") - in bedrock custom loader
  6. // 2. Plugin.list() which calls BunProc.install() for default plugins
  7. // Without mocks, these would attempt real package installations that timeout in tests.
  8. mock.module("../../src/bun/index", () => ({
  9. BunProc: {
  10. install: async (pkg: string, _version?: string) => {
  11. // Return package name without version for mocking
  12. const lastAtIndex = pkg.lastIndexOf("@")
  13. return lastAtIndex > 0 ? pkg.substring(0, lastAtIndex) : pkg
  14. },
  15. run: async () => {
  16. throw new Error("BunProc.run should not be called in tests")
  17. },
  18. which: () => process.execPath,
  19. InstallFailedError: class extends Error {},
  20. },
  21. }))
  22. mock.module("@aws-sdk/credential-providers", () => ({
  23. fromNodeProviderChain: () => async () => ({
  24. accessKeyId: "mock-access-key-id",
  25. secretAccessKey: "mock-secret-access-key",
  26. }),
  27. }))
  28. const mockPlugin = () => ({})
  29. mock.module("opencode-copilot-auth", () => ({ default: mockPlugin }))
  30. mock.module("opencode-anthropic-auth", () => ({ default: mockPlugin }))
  31. mock.module("@gitlab/opencode-gitlab-auth", () => ({ default: mockPlugin }))
  32. // Import after mocks are set up
  33. const { tmpdir } = await import("../fixture/fixture")
  34. const { Instance } = await import("../../src/project/instance")
  35. const { Provider } = await import("../../src/provider/provider")
  36. const { Env } = await import("../../src/env")
  37. const { Global } = await import("../../src/global")
  38. test("Bedrock: config region takes precedence over AWS_REGION env var", async () => {
  39. await using tmp = await tmpdir({
  40. init: async (dir) => {
  41. await Bun.write(
  42. path.join(dir, "opencode.json"),
  43. JSON.stringify({
  44. $schema: "https://opencode.ai/config.json",
  45. provider: {
  46. "amazon-bedrock": {
  47. options: {
  48. region: "eu-west-1",
  49. },
  50. },
  51. },
  52. }),
  53. )
  54. },
  55. })
  56. await Instance.provide({
  57. directory: tmp.path,
  58. init: async () => {
  59. Env.set("AWS_REGION", "us-east-1")
  60. Env.set("AWS_PROFILE", "default")
  61. },
  62. fn: async () => {
  63. const providers = await Provider.list()
  64. expect(providers["amazon-bedrock"]).toBeDefined()
  65. expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
  66. },
  67. })
  68. })
  69. test("Bedrock: falls back to AWS_REGION env var when no config region", async () => {
  70. await using tmp = await tmpdir({
  71. init: async (dir) => {
  72. await Bun.write(
  73. path.join(dir, "opencode.json"),
  74. JSON.stringify({
  75. $schema: "https://opencode.ai/config.json",
  76. }),
  77. )
  78. },
  79. })
  80. await Instance.provide({
  81. directory: tmp.path,
  82. init: async () => {
  83. Env.set("AWS_REGION", "eu-west-1")
  84. Env.set("AWS_PROFILE", "default")
  85. },
  86. fn: async () => {
  87. const providers = await Provider.list()
  88. expect(providers["amazon-bedrock"]).toBeDefined()
  89. expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
  90. },
  91. })
  92. })
  93. test("Bedrock: loads when bearer token from auth.json is present", async () => {
  94. await using tmp = await tmpdir({
  95. init: async (dir) => {
  96. await Bun.write(
  97. path.join(dir, "opencode.json"),
  98. JSON.stringify({
  99. $schema: "https://opencode.ai/config.json",
  100. provider: {
  101. "amazon-bedrock": {
  102. options: {
  103. region: "eu-west-1",
  104. },
  105. },
  106. },
  107. }),
  108. )
  109. },
  110. })
  111. const authPath = path.join(Global.Path.data, "auth.json")
  112. await Bun.write(
  113. authPath,
  114. JSON.stringify({
  115. "amazon-bedrock": {
  116. type: "api",
  117. key: "test-bearer-token",
  118. },
  119. }),
  120. )
  121. await Instance.provide({
  122. directory: tmp.path,
  123. init: async () => {
  124. Env.set("AWS_PROFILE", "")
  125. Env.set("AWS_ACCESS_KEY_ID", "")
  126. Env.set("AWS_BEARER_TOKEN_BEDROCK", "")
  127. },
  128. fn: async () => {
  129. const providers = await Provider.list()
  130. expect(providers["amazon-bedrock"]).toBeDefined()
  131. expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
  132. },
  133. })
  134. })
  135. test("Bedrock: config profile takes precedence over AWS_PROFILE env var", async () => {
  136. await using tmp = await tmpdir({
  137. init: async (dir) => {
  138. await Bun.write(
  139. path.join(dir, "opencode.json"),
  140. JSON.stringify({
  141. $schema: "https://opencode.ai/config.json",
  142. provider: {
  143. "amazon-bedrock": {
  144. options: {
  145. profile: "my-custom-profile",
  146. region: "us-east-1",
  147. },
  148. },
  149. },
  150. }),
  151. )
  152. },
  153. })
  154. await Instance.provide({
  155. directory: tmp.path,
  156. init: async () => {
  157. Env.set("AWS_PROFILE", "default")
  158. Env.set("AWS_ACCESS_KEY_ID", "test-key-id")
  159. },
  160. fn: async () => {
  161. const providers = await Provider.list()
  162. expect(providers["amazon-bedrock"]).toBeDefined()
  163. expect(providers["amazon-bedrock"].options?.region).toBe("us-east-1")
  164. },
  165. })
  166. })
  167. test("Bedrock: includes custom endpoint in options when specified", async () => {
  168. await using tmp = await tmpdir({
  169. init: async (dir) => {
  170. await Bun.write(
  171. path.join(dir, "opencode.json"),
  172. JSON.stringify({
  173. $schema: "https://opencode.ai/config.json",
  174. provider: {
  175. "amazon-bedrock": {
  176. options: {
  177. endpoint: "https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com",
  178. },
  179. },
  180. },
  181. }),
  182. )
  183. },
  184. })
  185. await Instance.provide({
  186. directory: tmp.path,
  187. init: async () => {
  188. Env.set("AWS_PROFILE", "default")
  189. },
  190. fn: async () => {
  191. const providers = await Provider.list()
  192. expect(providers["amazon-bedrock"]).toBeDefined()
  193. expect(providers["amazon-bedrock"].options?.endpoint).toBe(
  194. "https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com",
  195. )
  196. },
  197. })
  198. })