auth.test.ts 3.2 KB

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