amazon-bedrock.test.ts 5.8 KB

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