analyze-batch-settings.test.ts 7.5 KB

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