my-usage-concurrent-inherit.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { beforeEach, describe, expect, it, vi } from "vitest";
  2. const getSessionMock = vi.fn();
  3. vi.mock("@/lib/auth", () => ({
  4. getSession: getSessionMock,
  5. }));
  6. const getKeySessionCountMock = vi.fn(async () => 2);
  7. vi.mock("@/lib/session-tracker", () => ({
  8. SessionTracker: {
  9. getKeySessionCount: getKeySessionCountMock,
  10. },
  11. }));
  12. const getTimeRangeForPeriodWithModeMock = vi.fn(async () => ({
  13. startTime: new Date("2026-02-11T00:00:00.000Z"),
  14. endTime: new Date("2026-02-12T00:00:00.000Z"),
  15. }));
  16. const getTimeRangeForPeriodMock = vi.fn(async () => ({
  17. startTime: new Date("2026-02-11T00:00:00.000Z"),
  18. endTime: new Date("2026-02-12T00:00:00.000Z"),
  19. }));
  20. vi.mock("@/lib/rate-limit/time-utils", () => ({
  21. getTimeRangeForPeriodWithMode: getTimeRangeForPeriodWithModeMock,
  22. getTimeRangeForPeriod: getTimeRangeForPeriodMock,
  23. }));
  24. const statisticsMock = {
  25. sumUserCostInTimeRange: vi.fn(async () => 0),
  26. sumUserTotalCost: vi.fn(async () => 0),
  27. sumKeyCostInTimeRange: vi.fn(async () => 0),
  28. sumKeyTotalCostById: vi.fn(async () => 0),
  29. };
  30. vi.mock("@/repository/statistics", () => statisticsMock);
  31. const whereMock = vi.fn(async () => [{ id: 1 }]);
  32. const fromMock = vi.fn(() => ({ where: whereMock }));
  33. const selectMock = vi.fn(() => ({ from: fromMock }));
  34. vi.mock("@/drizzle/db", () => ({
  35. db: {
  36. select: selectMock,
  37. },
  38. }));
  39. vi.mock("@/lib/logger", () => ({
  40. logger: {
  41. warn: vi.fn(),
  42. error: vi.fn(),
  43. },
  44. }));
  45. function createSession(params: {
  46. keyLimitConcurrentSessions: number | null;
  47. userLimitConcurrentSessions: number | null;
  48. }) {
  49. return {
  50. key: {
  51. id: 1,
  52. key: "sk-test",
  53. name: "k",
  54. dailyResetTime: "00:00",
  55. dailyResetMode: "fixed",
  56. limit5hUsd: null,
  57. limitDailyUsd: null,
  58. limitWeeklyUsd: null,
  59. limitMonthlyUsd: null,
  60. limitTotalUsd: null,
  61. limitConcurrentSessions: params.keyLimitConcurrentSessions,
  62. providerGroup: null,
  63. isEnabled: true,
  64. expiresAt: null,
  65. },
  66. user: {
  67. id: 10,
  68. name: "u",
  69. dailyResetTime: "00:00",
  70. dailyResetMode: "fixed",
  71. limit5hUsd: null,
  72. dailyQuota: null,
  73. limitWeeklyUsd: null,
  74. limitMonthlyUsd: null,
  75. limitTotalUsd: null,
  76. limitConcurrentSessions: params.userLimitConcurrentSessions,
  77. rpm: null,
  78. providerGroup: null,
  79. isEnabled: true,
  80. expiresAt: null,
  81. allowedModels: [],
  82. allowedClients: [],
  83. },
  84. };
  85. }
  86. describe("getMyQuota - concurrent limit inheritance", () => {
  87. beforeEach(() => {
  88. vi.clearAllMocks();
  89. getSessionMock.mockResolvedValue(
  90. createSession({ keyLimitConcurrentSessions: 0, userLimitConcurrentSessions: 15 })
  91. );
  92. });
  93. it("Key 并发为 0 时应回退到 User 并发上限", async () => {
  94. const { getMyQuota } = await import("@/actions/my-usage");
  95. const result = await getMyQuota();
  96. expect(result.ok).toBe(true);
  97. if (result.ok) {
  98. expect(result.data.keyLimitConcurrentSessions).toBe(15);
  99. }
  100. });
  101. it("Key 并发为正数时应优先使用 Key 自身上限", async () => {
  102. getSessionMock.mockResolvedValue(
  103. createSession({ keyLimitConcurrentSessions: 5, userLimitConcurrentSessions: 15 })
  104. );
  105. const { getMyQuota } = await import("@/actions/my-usage");
  106. const result = await getMyQuota();
  107. expect(result.ok).toBe(true);
  108. if (result.ok) {
  109. expect(result.data.keyLimitConcurrentSessions).toBe(5);
  110. }
  111. });
  112. it("Key=0 且 User=0 时应返回 0(无限制)", async () => {
  113. getSessionMock.mockResolvedValue(
  114. createSession({ keyLimitConcurrentSessions: 0, userLimitConcurrentSessions: 0 })
  115. );
  116. const { getMyQuota } = await import("@/actions/my-usage");
  117. const result = await getMyQuota();
  118. expect(result.ok).toBe(true);
  119. if (result.ok) {
  120. expect(result.data.keyLimitConcurrentSessions).toBe(0);
  121. }
  122. });
  123. });