analyze-batch-settings.test.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import { describe, expect, it } from "vitest";
  2. import type { ProviderDisplay } from "@/types/provider";
  3. import { analyzeBatchProviderSettings } from "@/app/[locale]/settings/providers/_components/batch-edit/analyze-batch-settings";
  4. describe("analyzeBatchProviderSettings", () => {
  5. describe("空列表", () => {
  6. it("应该返回所有字段为 empty 状态", () => {
  7. const result = analyzeBatchProviderSettings([]);
  8. expect(result.routing.priority.status).toBe("empty");
  9. expect(result.routing.weight.status).toBe("empty");
  10. expect(result.rateLimit.limit5hUsd.status).toBe("empty");
  11. });
  12. });
  13. describe("uniform 值", () => {
  14. it("应该识别所有供应商有相同的基本类型值", () => {
  15. const providers: ProviderDisplay[] = [
  16. {
  17. priority: 10,
  18. weight: 5,
  19. costMultiplier: 1.5,
  20. disableSessionReuse: true,
  21. } as ProviderDisplay,
  22. {
  23. priority: 10,
  24. weight: 5,
  25. costMultiplier: 1.5,
  26. disableSessionReuse: true,
  27. } as ProviderDisplay,
  28. {
  29. priority: 10,
  30. weight: 5,
  31. costMultiplier: 1.5,
  32. disableSessionReuse: true,
  33. } as ProviderDisplay,
  34. ];
  35. const result = analyzeBatchProviderSettings(providers);
  36. expect(result.routing.priority).toEqual({ status: "uniform", value: 10 });
  37. expect(result.routing.weight).toEqual({ status: "uniform", value: 5 });
  38. expect(result.routing.costMultiplier).toEqual({ status: "uniform", value: 1.5 });
  39. expect(result.routing.disableSessionReuse).toEqual({ status: "uniform", value: true });
  40. });
  41. it("应该识别所有供应商有相同的对象值", () => {
  42. const providers: ProviderDisplay[] = [
  43. {
  44. modelRedirects: [{ matchType: "exact", source: "model-a", target: "model-b" }],
  45. } as ProviderDisplay,
  46. {
  47. modelRedirects: [{ matchType: "exact", source: "model-a", target: "model-b" }],
  48. } as ProviderDisplay,
  49. ];
  50. const result = analyzeBatchProviderSettings(providers);
  51. expect(result.routing.modelRedirects).toEqual({
  52. status: "uniform",
  53. value: [{ matchType: "exact", source: "model-a", target: "model-b" }],
  54. });
  55. });
  56. it("应该识别所有供应商有相同的数组值", () => {
  57. const providers: ProviderDisplay[] = [
  58. { allowedModels: ["model-1", "model-2"] } as ProviderDisplay,
  59. { allowedModels: ["model-1", "model-2"] } as ProviderDisplay,
  60. ];
  61. const result = analyzeBatchProviderSettings(providers);
  62. expect(result.routing.allowedModels).toEqual({
  63. status: "uniform",
  64. value: [
  65. { matchType: "exact", pattern: "model-1" },
  66. { matchType: "exact", pattern: "model-2" },
  67. ],
  68. });
  69. });
  70. it("应该识别所有供应商都为 null 的字段", () => {
  71. const providers: ProviderDisplay[] = [
  72. { limit5hUsd: null } as ProviderDisplay,
  73. { limit5hUsd: null } as ProviderDisplay,
  74. ];
  75. const result = analyzeBatchProviderSettings(providers);
  76. expect(result.rateLimit.limit5hUsd.status).toBe("empty");
  77. });
  78. });
  79. describe("mixed 值", () => {
  80. it("应该识别供应商有不同的基本类型值", () => {
  81. const providers: ProviderDisplay[] = [
  82. { priority: 10, disableSessionReuse: false } as ProviderDisplay,
  83. { priority: 20, disableSessionReuse: true } as ProviderDisplay,
  84. { priority: 30, disableSessionReuse: false } as ProviderDisplay,
  85. ];
  86. const result = analyzeBatchProviderSettings(providers);
  87. expect(result.routing.priority.status).toBe("mixed");
  88. if (result.routing.priority.status === "mixed") {
  89. expect(result.routing.priority.values).toEqual([10, 20, 30]);
  90. }
  91. expect(result.routing.disableSessionReuse.status).toBe("mixed");
  92. if (result.routing.disableSessionReuse.status === "mixed") {
  93. expect(result.routing.disableSessionReuse.values).toEqual([false, true]);
  94. }
  95. });
  96. it("应该识别供应商有不同的对象值", () => {
  97. const providers: ProviderDisplay[] = [
  98. {
  99. modelRedirects: [{ matchType: "exact", source: "model-a", target: "model-b" }],
  100. } as ProviderDisplay,
  101. {
  102. modelRedirects: [{ matchType: "exact", source: "model-c", target: "model-d" }],
  103. } as ProviderDisplay,
  104. ];
  105. const result = analyzeBatchProviderSettings(providers);
  106. expect(result.routing.modelRedirects.status).toBe("mixed");
  107. if (result.routing.modelRedirects.status === "mixed") {
  108. expect(result.routing.modelRedirects.values).toEqual([
  109. [{ matchType: "exact", source: "model-a", target: "model-b" }],
  110. [{ matchType: "exact", source: "model-c", target: "model-d" }],
  111. ]);
  112. }
  113. });
  114. it("应该去重 mixed 值", () => {
  115. const providers: ProviderDisplay[] = [
  116. { priority: 10 } as ProviderDisplay,
  117. { priority: 20 } as ProviderDisplay,
  118. { priority: 10 } as ProviderDisplay, // 重复
  119. { priority: 20 } as ProviderDisplay, // 重复
  120. ];
  121. const result = analyzeBatchProviderSettings(providers);
  122. expect(result.routing.priority.status).toBe("mixed");
  123. if (result.routing.priority.status === "mixed") {
  124. expect(result.routing.priority.values).toEqual([10, 20]);
  125. }
  126. });
  127. });
  128. describe("复杂字段", () => {
  129. it("应该正确处理 groupTag 字段(字符串转数组)", () => {
  130. const providers: ProviderDisplay[] = [
  131. { groupTag: "tag1, tag2" } as ProviderDisplay,
  132. { groupTag: "tag1, tag2" } as ProviderDisplay,
  133. ];
  134. const result = analyzeBatchProviderSettings(providers);
  135. expect(result.routing.groupTag).toEqual({
  136. status: "uniform",
  137. value: ["tag1", "tag2"],
  138. });
  139. });
  140. it("应该正确处理空 groupTag", () => {
  141. const providers: ProviderDisplay[] = [
  142. { groupTag: null } as ProviderDisplay,
  143. { groupTag: "" } as ProviderDisplay,
  144. ];
  145. const result = analyzeBatchProviderSettings(providers);
  146. expect(result.routing.groupTag).toEqual({
  147. status: "uniform",
  148. value: [],
  149. });
  150. });
  151. it("应该正确处理 circuitBreaker 时间单位转换(ms -> minutes)", () => {
  152. const providers: ProviderDisplay[] = [
  153. { circuitBreakerOpenDuration: 300000 } as ProviderDisplay, // 5 分钟
  154. { circuitBreakerOpenDuration: 300000 } as ProviderDisplay,
  155. ];
  156. const result = analyzeBatchProviderSettings(providers);
  157. expect(result.circuitBreaker.openDurationMinutes).toEqual({
  158. status: "uniform",
  159. value: 5,
  160. });
  161. });
  162. it("应该正确处理 network 时间单位转换(ms -> seconds)", () => {
  163. const providers: ProviderDisplay[] = [
  164. { firstByteTimeoutStreamingMs: 30000 } as ProviderDisplay, // 30 秒
  165. { firstByteTimeoutStreamingMs: 30000 } as ProviderDisplay,
  166. ];
  167. const result = analyzeBatchProviderSettings(providers);
  168. expect(result.network.firstByteTimeoutStreamingSeconds).toEqual({
  169. status: "uniform",
  170. value: 30,
  171. });
  172. });
  173. it("应该正确处理 anthropicAdaptiveThinking 复杂对象", () => {
  174. const config = {
  175. effort: "high" as const,
  176. modelMatchMode: "specific" as const,
  177. models: ["claude-opus-4-6"],
  178. };
  179. const providers: ProviderDisplay[] = [
  180. { anthropicAdaptiveThinking: config } as ProviderDisplay,
  181. { anthropicAdaptiveThinking: config } as ProviderDisplay,
  182. ];
  183. const result = analyzeBatchProviderSettings(providers);
  184. expect(result.routing.anthropicAdaptiveThinking).toEqual({
  185. status: "uniform",
  186. value: config,
  187. });
  188. });
  189. });
  190. describe("默认值处理", () => {
  191. it("应该为未设置的字段使用默认值", () => {
  192. const providers: ProviderDisplay[] = [
  193. {
  194. preserveClientIp: false,
  195. cacheTtlPreference: "inherit",
  196. dailyResetMode: "fixed",
  197. } as ProviderDisplay,
  198. {
  199. preserveClientIp: false,
  200. cacheTtlPreference: "inherit",
  201. dailyResetMode: "fixed",
  202. } as ProviderDisplay,
  203. ];
  204. const result = analyzeBatchProviderSettings(providers);
  205. // 检查一些有默认值的字段
  206. expect(result.routing.cacheTtlPreference).toEqual({
  207. status: "uniform",
  208. value: "inherit",
  209. });
  210. expect(result.routing.preserveClientIp).toEqual({
  211. status: "uniform",
  212. value: false,
  213. });
  214. expect(result.rateLimit.dailyResetMode).toEqual({
  215. status: "uniform",
  216. value: "fixed",
  217. });
  218. });
  219. });
  220. });