auth.test.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { describe, expect } from "bun:test"
  2. import { ConfigProvider, Effect } from "effect"
  3. import { Headers } from "effect/unstable/http"
  4. import { LLM } from "../src"
  5. import { Auth } from "../src/route/auth"
  6. import { it } from "./lib/effect"
  7. const request = LLM.request({
  8. id: "req_auth",
  9. model: LLM.model({ id: "fake-model", provider: "fake", route: "fake", baseURL: "https://fake.local" }),
  10. prompt: "hello",
  11. })
  12. const input = {
  13. request,
  14. method: "POST" as const,
  15. url: "https://example.test/v1/chat",
  16. body: "{}",
  17. headers: Headers.fromInput({ "x-existing": "yes" }),
  18. }
  19. const withEnv = (env: Record<string, string>) => Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env })))
  20. describe("Auth", () => {
  21. it.effect("renders a config credential as bearer auth", () =>
  22. Effect.gen(function* () {
  23. const headers = yield* Auth.config("OPENAI_API_KEY")
  24. .bearer()
  25. .apply(input)
  26. .pipe(withEnv({ OPENAI_API_KEY: "sk-test" }))
  27. expect(headers.authorization).toBe("Bearer sk-test")
  28. expect(headers["x-existing"]).toBe("yes")
  29. }),
  30. )
  31. it.effect("falls back between credential sources before rendering", () =>
  32. Effect.gen(function* () {
  33. const headers = yield* Auth.config("PRIMARY_KEY")
  34. .orElse(Auth.value("fallback-key"))
  35. .pipe(Auth.header("x-api-key"))
  36. .apply(input)
  37. .pipe(withEnv({}))
  38. expect(headers["x-api-key"]).toBe("fallback-key")
  39. expect(headers["x-existing"]).toBe("yes")
  40. }),
  41. )
  42. it.effect("composes header auth in sequence", () =>
  43. Effect.gen(function* () {
  44. const headers = yield* Auth.headers({ "x-tenant-id": "tenant-1" })
  45. .andThen(Auth.bearer("gateway-token"))
  46. .apply(input)
  47. expect(headers["x-tenant-id"]).toBe("tenant-1")
  48. expect(headers.authorization).toBe("Bearer gateway-token")
  49. expect(headers["x-existing"]).toBe("yes")
  50. }),
  51. )
  52. it.effect("renders a direct secret as a custom header", () =>
  53. Effect.gen(function* () {
  54. const headers = yield* Auth.header("api-key", "direct-key").apply(input)
  55. expect(headers["api-key"]).toBe("direct-key")
  56. expect(headers["x-existing"]).toBe("yes")
  57. }),
  58. )
  59. it.effect("renders bearer auth into a custom header", () =>
  60. Effect.gen(function* () {
  61. const headers = yield* Auth.bearerHeader("cf-aig-authorization", "gateway-token").apply(input)
  62. expect(headers["cf-aig-authorization"]).toBe("Bearer gateway-token")
  63. expect(headers["x-existing"]).toBe("yes")
  64. }),
  65. )
  66. it.effect("falls back between full auth values", () =>
  67. Effect.gen(function* () {
  68. const headers = yield* Auth.config("OPENAI_API_KEY")
  69. .bearer()
  70. .orElse(Auth.headers({ authorization: "Bearer supplied" }))
  71. .apply(input)
  72. .pipe(withEnv({}))
  73. expect(headers.authorization).toBe("Bearer supplied")
  74. expect(headers["x-existing"]).toBe("yes")
  75. }),
  76. )
  77. it.effect("can intentionally leave auth untouched", () =>
  78. Effect.gen(function* () {
  79. const headers = yield* Auth.none.apply(input)
  80. expect(headers.authorization).toBeUndefined()
  81. expect(headers["x-existing"]).toBe("yes")
  82. }),
  83. )
  84. })