url.test.ts 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { describe, expect, test, vi } from "vitest";
  2. vi.mock("@/lib/logger", () => ({
  3. logger: {
  4. debug: vi.fn(),
  5. error: vi.fn(),
  6. info: vi.fn(),
  7. warn: vi.fn(),
  8. },
  9. }));
  10. import { buildProxyUrl } from "@/app/v1/_lib/url";
  11. describe("buildProxyUrl", () => {
  12. test("标准拼接:baseUrl 无路径时使用 requestPath + search", () => {
  13. const out = buildProxyUrl(
  14. "https://api.example.com",
  15. new URL("https://dummy.com/v1/messages?x=1")
  16. );
  17. expect(out).toBe("https://api.example.com/v1/messages?x=1");
  18. });
  19. test("避免重复拼接:baseUrl 已包含 /responses 时不追加 /v1/responses", () => {
  20. const out = buildProxyUrl(
  21. "https://example.com/openai/responses",
  22. new URL("https://dummy.com/v1/responses?x=1")
  23. );
  24. expect(out).toBe("https://example.com/openai/responses?x=1");
  25. });
  26. test("避免重复拼接:baseUrl 已包含 /embeddings 时不追加 /v1/embeddings", () => {
  27. const out = buildProxyUrl(
  28. "https://example.com/openai/embeddings",
  29. new URL("https://dummy.com/v1/embeddings?x=1")
  30. );
  31. expect(out).toBe("https://example.com/openai/embeddings?x=1");
  32. });
  33. test("子路径不丢失:baseUrl=/v1/messages + request=/v1/messages/count_tokens", () => {
  34. const out = buildProxyUrl(
  35. "https://api.example.com/v1/messages",
  36. new URL("https://dummy.com/v1/messages/count_tokens")
  37. );
  38. expect(out).toBe("https://api.example.com/v1/messages/count_tokens");
  39. });
  40. test("带前缀路径的 baseUrl:/openai/messages + /v1/messages/count_tokens", () => {
  41. const out = buildProxyUrl(
  42. "https://example.com/openai/messages",
  43. new URL("https://dummy.com/v1/messages/count_tokens")
  44. );
  45. expect(out).toBe("https://example.com/openai/messages/count_tokens");
  46. });
  47. test("query 以 requestUrl 为准(覆盖 baseUrl 自带 query)", () => {
  48. const out = buildProxyUrl(
  49. "https://api.example.com/v1/messages?from=base",
  50. new URL("https://dummy.com/v1/messages?from=request")
  51. );
  52. expect(out).toBe("https://api.example.com/v1/messages?from=request");
  53. });
  54. test("baseUrl 以 /models 结尾时去除请求中的版本前缀", () => {
  55. const out = buildProxyUrl(
  56. "https://api.example.com/gemini/models",
  57. new URL("https://dummy.com/v1beta/models/gemini-1.5-pro:streamGenerateContent")
  58. );
  59. expect(out).toBe("https://api.example.com/gemini/models/gemini-1.5-pro:streamGenerateContent");
  60. });
  61. test("支持 v1internal 版本前缀", () => {
  62. const out = buildProxyUrl(
  63. "https://example.com/gemini/models",
  64. new URL("https://dummy.com/v1internal/models/gemini-2.5-flash:generateContent")
  65. );
  66. expect(out).toBe("https://example.com/gemini/models/gemini-2.5-flash:generateContent");
  67. });
  68. test("支持未来的版本前缀如 v2", () => {
  69. const out = buildProxyUrl(
  70. "https://example.com/api/models",
  71. new URL("https://dummy.com/v2/models/some-model:action")
  72. );
  73. expect(out).toBe("https://example.com/api/models/some-model:action");
  74. });
  75. });