active-sessions-special-settings.test.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { beforeEach, describe, expect, test, vi } from "vitest";
  2. const getSessionMock = vi.fn();
  3. const getSessionDetailsCacheMock = vi.fn();
  4. const setSessionDetailsCacheMock = vi.fn();
  5. const getSessionRequestCountMock = vi.fn();
  6. const getSessionRequestBodyMock = vi.fn();
  7. const getSessionMessagesMock = vi.fn();
  8. const getSessionResponseMock = vi.fn();
  9. const getSessionRequestHeadersMock = vi.fn();
  10. const getSessionResponseHeadersMock = vi.fn();
  11. const getSessionClientRequestMetaMock = vi.fn();
  12. const getSessionUpstreamRequestMetaMock = vi.fn();
  13. const getSessionUpstreamResponseMetaMock = vi.fn();
  14. const getSessionSpecialSettingsMock = vi.fn();
  15. const aggregateSessionStatsMock = vi.fn();
  16. const findAdjacentRequestSequencesMock = vi.fn();
  17. const findMessageRequestAuditBySessionIdAndSequenceMock = vi.fn();
  18. vi.mock("@/lib/auth", () => ({
  19. getSession: getSessionMock,
  20. }));
  21. vi.mock("@/lib/cache/session-cache", () => ({
  22. getActiveSessionsCache: vi.fn(() => null),
  23. setActiveSessionsCache: vi.fn(),
  24. getSessionDetailsCache: getSessionDetailsCacheMock,
  25. setSessionDetailsCache: setSessionDetailsCacheMock,
  26. clearActiveSessionsCache: vi.fn(),
  27. clearSessionDetailsCache: vi.fn(),
  28. clearAllSessionsCache: vi.fn(),
  29. }));
  30. vi.mock("@/lib/logger", () => ({
  31. logger: {
  32. trace: vi.fn(),
  33. debug: vi.fn(),
  34. info: vi.fn(),
  35. warn: vi.fn(),
  36. error: vi.fn(),
  37. },
  38. }));
  39. vi.mock("@/lib/session-manager", () => ({
  40. SessionManager: {
  41. getSessionRequestCount: getSessionRequestCountMock,
  42. getSessionRequestBody: getSessionRequestBodyMock,
  43. getSessionMessages: getSessionMessagesMock,
  44. getSessionResponse: getSessionResponseMock,
  45. getSessionRequestHeaders: getSessionRequestHeadersMock,
  46. getSessionResponseHeaders: getSessionResponseHeadersMock,
  47. getSessionClientRequestMeta: getSessionClientRequestMetaMock,
  48. getSessionUpstreamRequestMeta: getSessionUpstreamRequestMetaMock,
  49. getSessionUpstreamResponseMeta: getSessionUpstreamResponseMetaMock,
  50. getSessionSpecialSettings: getSessionSpecialSettingsMock,
  51. },
  52. }));
  53. vi.mock("@/repository/message", () => ({
  54. aggregateSessionStats: aggregateSessionStatsMock,
  55. findAdjacentRequestSequences: findAdjacentRequestSequencesMock,
  56. findMessageRequestAuditBySessionIdAndSequence: findMessageRequestAuditBySessionIdAndSequenceMock,
  57. }));
  58. describe("getSessionDetails - unified specialSettings", () => {
  59. beforeEach(() => {
  60. vi.clearAllMocks();
  61. getSessionMock.mockResolvedValue({ user: { id: 1, role: "admin" } });
  62. getSessionDetailsCacheMock.mockReturnValue(null);
  63. aggregateSessionStatsMock.mockResolvedValue({
  64. sessionId: "sess_x",
  65. requestCount: 1,
  66. totalCostUsd: "0",
  67. totalInputTokens: 0,
  68. totalOutputTokens: 0,
  69. totalCacheCreationTokens: 0,
  70. totalCacheReadTokens: 0,
  71. totalDurationMs: 0,
  72. firstRequestAt: new Date(),
  73. lastRequestAt: new Date(),
  74. providers: [],
  75. models: [],
  76. userName: "u",
  77. userId: 1,
  78. keyName: "k",
  79. keyId: 1,
  80. userAgent: null,
  81. apiType: "chat",
  82. cacheTtlApplied: null,
  83. });
  84. findAdjacentRequestSequencesMock.mockResolvedValue({ prevSequence: null, nextSequence: null });
  85. getSessionRequestCountMock.mockResolvedValue(1);
  86. getSessionRequestBodyMock.mockResolvedValue(null);
  87. getSessionMessagesMock.mockResolvedValue(null);
  88. getSessionResponseMock.mockResolvedValue(null);
  89. getSessionRequestHeadersMock.mockResolvedValue(null);
  90. getSessionResponseHeadersMock.mockResolvedValue(null);
  91. getSessionClientRequestMetaMock.mockResolvedValue(null);
  92. getSessionUpstreamRequestMetaMock.mockResolvedValue(null);
  93. getSessionUpstreamResponseMetaMock.mockResolvedValue(null);
  94. });
  95. test("当 Redis specialSettings 为空时,应由 DB 审计字段派生特殊设置", async () => {
  96. getSessionSpecialSettingsMock.mockResolvedValue(null);
  97. findMessageRequestAuditBySessionIdAndSequenceMock.mockResolvedValue({
  98. statusCode: 200,
  99. blockedBy: "warmup",
  100. blockedReason: JSON.stringify({ reason: "anthropic_warmup_intercepted" }),
  101. cacheTtlApplied: "1h",
  102. context1mApplied: true,
  103. specialSettings: null,
  104. });
  105. const { getSessionDetails } = await import("@/actions/active-sessions");
  106. const result = await getSessionDetails("sess_x", 1);
  107. expect(result.ok).toBe(true);
  108. if (!result.ok) return;
  109. const types = (result.data.specialSettings ?? []).map((s) => s.type).sort();
  110. expect(types).toEqual(
  111. [
  112. "anthropic_cache_ttl_header_override",
  113. "anthropic_context_1m_header_override",
  114. "guard_intercept",
  115. ].sort()
  116. );
  117. });
  118. test("当 Redis 与 DB 同时存在 specialSettings 时,应合并并去重", async () => {
  119. getSessionSpecialSettingsMock.mockResolvedValue([
  120. {
  121. type: "provider_parameter_override",
  122. scope: "provider",
  123. providerId: 1,
  124. providerName: "p",
  125. providerType: "codex",
  126. hit: true,
  127. changed: true,
  128. changes: [{ path: "temperature", before: 1, after: 0.2, changed: true }],
  129. },
  130. ]);
  131. findMessageRequestAuditBySessionIdAndSequenceMock.mockResolvedValue({
  132. statusCode: 200,
  133. blockedBy: "warmup",
  134. blockedReason: JSON.stringify({ reason: "anthropic_warmup_intercepted" }),
  135. cacheTtlApplied: null,
  136. context1mApplied: false,
  137. specialSettings: [
  138. {
  139. type: "provider_parameter_override",
  140. scope: "provider",
  141. providerId: 1,
  142. providerName: "p",
  143. providerType: "codex",
  144. hit: true,
  145. changed: true,
  146. changes: [{ path: "temperature", before: 1, after: 0.2, changed: true }],
  147. },
  148. ],
  149. });
  150. const { getSessionDetails } = await import("@/actions/active-sessions");
  151. const result = await getSessionDetails("sess_x", 1);
  152. expect(result.ok).toBe(true);
  153. if (!result.ok) return;
  154. const settings = result.data.specialSettings ?? [];
  155. expect(settings.some((s) => s.type === "provider_parameter_override")).toBe(true);
  156. expect(settings.some((s) => s.type === "guard_intercept")).toBe(true);
  157. expect(settings.filter((s) => s.type === "provider_parameter_override").length).toBe(1);
  158. });
  159. });