available-models-gemini-key.test.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { describe, expect, test, vi } from "vitest";
  2. import type { Provider } from "@/types/provider";
  3. const undiciRequestMock = vi.fn(async () => {
  4. return {
  5. statusCode: 200,
  6. body: {
  7. json: async () => ({
  8. models: [{ name: "models/gemini-1.5-pro", displayName: "Gemini 1.5 Pro" }],
  9. }),
  10. text: async () => "",
  11. },
  12. };
  13. });
  14. vi.mock("undici", () => {
  15. return {
  16. request: undiciRequestMock,
  17. };
  18. });
  19. vi.mock("@/repository/key", () => {
  20. return {
  21. validateApiKeyAndGetUser: vi.fn(async () => ({
  22. user: { id: 1, providerGroup: null, isEnabled: true, expiresAt: null },
  23. key: { providerGroup: null, name: "test-key" },
  24. })),
  25. };
  26. });
  27. vi.mock("@/lib/proxy-agent", () => {
  28. return {
  29. createProxyAgentForProvider: vi.fn(() => null),
  30. };
  31. });
  32. vi.mock("@/lib/utils/timezone", () => ({
  33. resolveSystemTimezone: vi.fn().mockResolvedValue("UTC"),
  34. }));
  35. vi.mock("@/lib/utils/provider-schedule", () => ({
  36. isProviderActiveNow: vi.fn().mockReturnValue(true),
  37. }));
  38. vi.mock("@/app/v1/_lib/proxy/provider-selector", () => ({
  39. checkProviderGroupMatch: vi.fn().mockReturnValue(true),
  40. }));
  41. vi.mock("@/repository/provider", () => ({
  42. findAllProviders: vi.fn().mockResolvedValue([]),
  43. }));
  44. describe("handleAvailableModels - Gemini key 传参", () => {
  45. test("Gemini 上游请求不应在 URL query 携带 key,应使用 x-goog-api-key 头", async () => {
  46. const geminiProvider = {
  47. id: 1,
  48. name: "gemini",
  49. providerType: "gemini",
  50. url: "https://generativelanguage.googleapis.com/v1beta",
  51. key: "upstream-api-key",
  52. preserveClientIp: false,
  53. allowedModels: null,
  54. isEnabled: true,
  55. activeTimeStart: null,
  56. activeTimeEnd: null,
  57. groupTag: null,
  58. } as unknown as Provider;
  59. const { findAllProviders } = await import("@/repository/provider");
  60. vi.mocked(findAllProviders).mockResolvedValue([geminiProvider]);
  61. const { handleAvailableModels } = await import("@/app/v1/_lib/models/available-models");
  62. const c = {
  63. req: {
  64. path: "/v1/models",
  65. header: (name: string) => {
  66. if (name.toLowerCase() === "x-goog-api-key") return "user-api-key";
  67. return undefined;
  68. },
  69. query: () => undefined,
  70. },
  71. json: (body: unknown, status?: number) => {
  72. return new Response(JSON.stringify(body), {
  73. status: status ?? 200,
  74. headers: { "content-type": "application/json" },
  75. });
  76. },
  77. } as any;
  78. await handleAvailableModels(c);
  79. expect(undiciRequestMock).toHaveBeenCalledTimes(1);
  80. const [url, options] = undiciRequestMock.mock.calls[0] ?? [];
  81. expect(String(url)).not.toContain("key=");
  82. expect(options?.headers?.["x-goog-api-key"]).toBe("upstream-api-key");
  83. });
  84. });