provider-batch-update-advanced-fields.test.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { describe, expect, test, vi } from "vitest";
  2. type BatchUpdateRow = {
  3. id: number;
  4. providerVendorId: number | null;
  5. providerType: string;
  6. url: string;
  7. };
  8. function createDbMock(updatedRows: BatchUpdateRow[]) {
  9. const updateSetPayloads: Array<Record<string, unknown>> = [];
  10. const updateReturningMock = vi.fn(async () => updatedRows);
  11. const updateWhereMock = vi.fn(() => ({ returning: updateReturningMock }));
  12. const updateSetMock = vi.fn((payload: Record<string, unknown>) => {
  13. updateSetPayloads.push(payload);
  14. return { where: updateWhereMock };
  15. });
  16. const updateMock = vi.fn(() => ({ set: updateSetMock }));
  17. const insertReturningMock = vi.fn(async () => []);
  18. const insertOnConflictDoNothingMock = vi.fn(() => ({ returning: insertReturningMock }));
  19. const insertValuesMock = vi.fn(() => ({ onConflictDoNothing: insertOnConflictDoNothingMock }));
  20. const insertMock = vi.fn(() => ({ values: insertValuesMock }));
  21. return {
  22. db: {
  23. update: updateMock,
  24. insert: insertMock,
  25. },
  26. mocks: {
  27. updateMock,
  28. updateSetPayloads,
  29. insertMock,
  30. },
  31. };
  32. }
  33. async function arrange(updatedRows: BatchUpdateRow[] = []) {
  34. vi.resetModules();
  35. const dbMock = createDbMock(updatedRows);
  36. vi.doMock("@/drizzle/db", () => ({ db: dbMock.db }));
  37. vi.doMock("@/lib/logger", () => ({
  38. logger: {
  39. trace: vi.fn(),
  40. debug: vi.fn(),
  41. info: vi.fn(),
  42. warn: vi.fn(),
  43. error: vi.fn(),
  44. },
  45. }));
  46. const { updateProvidersBatch } = await import("@/repository/provider");
  47. return {
  48. updateProvidersBatch,
  49. ...dbMock.mocks,
  50. };
  51. }
  52. describe("provider repository - updateProvidersBatch advanced fields", () => {
  53. const updatedRows: BatchUpdateRow[] = [
  54. {
  55. id: 11,
  56. providerVendorId: 100,
  57. providerType: "claude",
  58. url: "https://api-one.example.com/v1/messages",
  59. },
  60. {
  61. id: 22,
  62. providerVendorId: 100,
  63. providerType: "claude",
  64. url: "https://api-two.example.com/v1/messages",
  65. },
  66. ];
  67. test("updates modelRedirects for multiple providers", async () => {
  68. const { updateProvidersBatch, updateSetPayloads, updateMock, insertMock } =
  69. await arrange(updatedRows);
  70. const modelRedirects = {
  71. "claude-sonnet-4-5-20250929": "glm-4.6",
  72. };
  73. const result = await updateProvidersBatch([11, 22], { modelRedirects });
  74. expect(result).toBe(2);
  75. expect(updateMock).toHaveBeenCalledTimes(1);
  76. expect(updateSetPayloads[0]).toEqual(
  77. expect.objectContaining({
  78. updatedAt: expect.any(Date),
  79. modelRedirects,
  80. })
  81. );
  82. expect(insertMock).not.toHaveBeenCalled();
  83. });
  84. test("updates allowedModels for multiple providers", async () => {
  85. const { updateProvidersBatch, updateSetPayloads } = await arrange(updatedRows);
  86. const allowedModels = ["claude-sonnet-4-5-20250929", "claude-opus-4-1-20250805"];
  87. const result = await updateProvidersBatch([11, 22], { allowedModels });
  88. expect(result).toBe(2);
  89. expect(updateSetPayloads[0]).toEqual(
  90. expect.objectContaining({
  91. updatedAt: expect.any(Date),
  92. allowedModels,
  93. })
  94. );
  95. });
  96. test("updates anthropicThinkingBudgetPreference for multiple providers", async () => {
  97. const { updateProvidersBatch, updateSetPayloads } = await arrange(updatedRows);
  98. const result = await updateProvidersBatch([11, 22], {
  99. anthropicThinkingBudgetPreference: "4096",
  100. });
  101. expect(result).toBe(2);
  102. expect(updateSetPayloads[0]).toEqual(
  103. expect.objectContaining({
  104. updatedAt: expect.any(Date),
  105. anthropicThinkingBudgetPreference: "4096",
  106. })
  107. );
  108. });
  109. test("updates anthropicAdaptiveThinking for multiple providers", async () => {
  110. const { updateProvidersBatch, updateSetPayloads } = await arrange(updatedRows);
  111. const anthropicAdaptiveThinking = {
  112. effort: "high",
  113. modelMatchMode: "specific",
  114. models: ["claude-sonnet-4-5-20250929"],
  115. };
  116. const result = await updateProvidersBatch([11, 22], {
  117. anthropicAdaptiveThinking,
  118. });
  119. expect(result).toBe(2);
  120. expect(updateSetPayloads[0]).toEqual(
  121. expect.objectContaining({
  122. updatedAt: expect.any(Date),
  123. anthropicAdaptiveThinking,
  124. })
  125. );
  126. });
  127. test("does not include undefined advanced fields in set payload", async () => {
  128. const { updateProvidersBatch, updateSetPayloads } = await arrange(updatedRows);
  129. const result = await updateProvidersBatch([11, 22], {
  130. priority: 3,
  131. modelRedirects: undefined,
  132. allowedModels: undefined,
  133. anthropicThinkingBudgetPreference: undefined,
  134. anthropicAdaptiveThinking: undefined,
  135. });
  136. expect(result).toBe(2);
  137. expect(updateSetPayloads[0]).toEqual(
  138. expect.objectContaining({
  139. updatedAt: expect.any(Date),
  140. priority: 3,
  141. })
  142. );
  143. expect(updateSetPayloads[0]).not.toHaveProperty("modelRedirects");
  144. expect(updateSetPayloads[0]).not.toHaveProperty("allowedModels");
  145. expect(updateSetPayloads[0]).not.toHaveProperty("anthropicThinkingBudgetPreference");
  146. expect(updateSetPayloads[0]).not.toHaveProperty("anthropicAdaptiveThinking");
  147. });
  148. test("writes null advanced values to clear fields", async () => {
  149. const { updateProvidersBatch, updateSetPayloads } = await arrange(updatedRows);
  150. const result = await updateProvidersBatch([11, 22], {
  151. modelRedirects: null,
  152. allowedModels: null,
  153. anthropicThinkingBudgetPreference: null,
  154. anthropicAdaptiveThinking: null,
  155. });
  156. expect(result).toBe(2);
  157. expect(updateSetPayloads[0]).toEqual(
  158. expect.objectContaining({
  159. updatedAt: expect.any(Date),
  160. modelRedirects: null,
  161. allowedModels: null,
  162. anthropicThinkingBudgetPreference: null,
  163. anthropicAdaptiveThinking: null,
  164. })
  165. );
  166. });
  167. });