| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /**
- * user-all-limit-window tests
- *
- * Verify getUserAllLimitUsage correctly uses getTimeRangeForPeriodWithMode for daily window,
- * respecting user.dailyResetMode configuration.
- *
- * Rolling mode: past 24 hours window
- * Fixed mode: since reset time window
- */
- import { beforeEach, describe, expect, it, vi } from "vitest";
- // Mock functions
- const getSessionMock = vi.fn();
- const findUserByIdMock = vi.fn();
- const getTimeRangeForPeriodMock = vi.fn();
- const getTimeRangeForPeriodWithModeMock = vi.fn();
- const sumUserCostInTimeRangeMock = vi.fn();
- const sumUserTotalCostMock = vi.fn();
- // Mock modules
- vi.mock("@/lib/auth", () => ({
- getSession: () => getSessionMock(),
- }));
- vi.mock("@/repository/user", () => ({
- findUserById: (...args: unknown[]) => findUserByIdMock(...args),
- }));
- vi.mock("@/lib/rate-limit/time-utils", () => ({
- getTimeRangeForPeriod: (...args: unknown[]) => getTimeRangeForPeriodMock(...args),
- getTimeRangeForPeriodWithMode: (...args: unknown[]) => getTimeRangeForPeriodWithModeMock(...args),
- }));
- vi.mock("@/repository/statistics", () => ({
- sumUserCostInTimeRange: (...args: unknown[]) => sumUserCostInTimeRangeMock(...args),
- sumUserTotalCost: (...args: unknown[]) => sumUserTotalCostMock(...args),
- }));
- vi.mock("@/lib/logger", () => ({
- logger: {
- trace: vi.fn(),
- debug: vi.fn(),
- info: vi.fn(),
- warn: vi.fn(),
- error: vi.fn(),
- },
- }));
- vi.mock("next-intl/server", () => ({
- getTranslations: vi.fn(() => async (key: string) => key),
- getLocale: vi.fn(() => "en"),
- }));
- vi.mock("next/cache", () => ({
- revalidatePath: vi.fn(),
- }));
- describe("getUserAllLimitUsage - daily window mode handling", () => {
- const now = new Date("2024-06-15T12:00:00.000Z");
- const past24h = new Date("2024-06-14T12:00:00.000Z");
- const fixedReset = new Date("2024-06-15T00:00:00.000Z");
- beforeEach(() => {
- vi.clearAllMocks();
- vi.useFakeTimers();
- vi.setSystemTime(now);
- // Default: admin session
- getSessionMock.mockResolvedValue({
- user: { id: 1, role: "admin" },
- key: { id: 1 },
- });
- // Default time range mocks
- getTimeRangeForPeriodMock.mockImplementation(async (period: string) => {
- switch (period) {
- case "5h":
- return { startTime: new Date(now.getTime() - 5 * 60 * 60 * 1000), endTime: now };
- case "weekly":
- return { startTime: new Date("2024-06-10T00:00:00.000Z"), endTime: now };
- case "monthly":
- return { startTime: new Date("2024-06-01T00:00:00.000Z"), endTime: now };
- default:
- return { startTime: fixedReset, endTime: now };
- }
- });
- getTimeRangeForPeriodWithModeMock.mockImplementation(
- async (period: string, _resetTime: string, mode: string) => {
- if (period === "daily" && mode === "rolling") {
- return { startTime: past24h, endTime: now };
- }
- // fixed mode
- return { startTime: fixedReset, endTime: now };
- }
- );
- // Default cost mocks
- sumUserCostInTimeRangeMock.mockResolvedValue(1.0);
- sumUserTotalCostMock.mockResolvedValue(10.0);
- });
- afterEach(() => {
- vi.useRealTimers();
- });
- it("should use rolling mode (past 24h) when user.dailyResetMode is rolling", async () => {
- // User with rolling mode
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "rolling",
- dailyResetTime: "00:00",
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- const result = await getUserAllLimitUsage(1);
- expect(result.ok).toBe(true);
- // Verify getTimeRangeForPeriodWithMode was called with rolling mode
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "00:00", "rolling");
- });
- it("should use fixed mode when user.dailyResetMode is fixed", async () => {
- // User with fixed mode
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "fixed",
- dailyResetTime: "18:00",
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- const result = await getUserAllLimitUsage(1);
- expect(result.ok).toBe(true);
- // Verify getTimeRangeForPeriodWithMode was called with fixed mode and custom reset time
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "18:00", "fixed");
- });
- it("should default to fixed mode when dailyResetMode is not set", async () => {
- // User without explicit dailyResetMode (defaults to fixed)
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: undefined, // or null
- dailyResetTime: "00:00",
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- const result = await getUserAllLimitUsage(1);
- expect(result.ok).toBe(true);
- // Should default to fixed mode
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "00:00", "fixed");
- });
- it("should pass correct dailyResetTime from user config", async () => {
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "fixed",
- dailyResetTime: "09:30", // Custom reset time
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- await getUserAllLimitUsage(1);
- // Verify custom reset time is passed
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "09:30", "fixed");
- });
- it("should default to 00:00 when dailyResetTime is not set", async () => {
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "fixed",
- dailyResetTime: undefined, // or null
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- await getUserAllLimitUsage(1);
- // Should default to "00:00"
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "00:00", "fixed");
- });
- it("should NOT use getTimeRangeForPeriod for daily (consistency with getUserLimitUsage)", async () => {
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "rolling",
- dailyResetTime: "00:00",
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- await getUserAllLimitUsage(1);
- // getTimeRangeForPeriod should only be called for 5h, weekly, monthly - NOT daily
- const dailyCalls = getTimeRangeForPeriodMock.mock.calls.filter((call) => call[0] === "daily");
- expect(dailyCalls).toHaveLength(0);
- });
- it("should still use getTimeRangeForPeriod for non-daily periods", async () => {
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "fixed",
- dailyResetTime: "00:00",
- dailyQuota: 10,
- limit5hUsd: 5,
- limitWeeklyUsd: 50,
- limitMonthlyUsd: 200,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- await getUserAllLimitUsage(1);
- // Verify other periods still use getTimeRangeForPeriod
- expect(getTimeRangeForPeriodMock).toHaveBeenCalledWith("5h");
- expect(getTimeRangeForPeriodMock).toHaveBeenCalledWith("weekly");
- expect(getTimeRangeForPeriodMock).toHaveBeenCalledWith("monthly");
- });
- });
- describe("getUserAllLimitUsage - consistency with key-quota.ts", () => {
- const now = new Date("2024-06-15T12:00:00.000Z");
- beforeEach(() => {
- vi.clearAllMocks();
- vi.useFakeTimers();
- vi.setSystemTime(now);
- getSessionMock.mockResolvedValue({
- user: { id: 1, role: "admin" },
- key: { id: 1 },
- });
- getTimeRangeForPeriodMock.mockImplementation(async (period: string) => {
- switch (period) {
- case "5h":
- return { startTime: new Date(now.getTime() - 5 * 60 * 60 * 1000), endTime: now };
- case "weekly":
- return { startTime: new Date("2024-06-10T00:00:00.000Z"), endTime: now };
- case "monthly":
- return { startTime: new Date("2024-06-01T00:00:00.000Z"), endTime: now };
- default:
- return { startTime: new Date("2024-06-15T00:00:00.000Z"), endTime: now };
- }
- });
- getTimeRangeForPeriodWithModeMock.mockResolvedValue({
- startTime: new Date("2024-06-15T00:00:00.000Z"),
- endTime: now,
- });
- sumUserCostInTimeRangeMock.mockResolvedValue(1.0);
- sumUserTotalCostMock.mockResolvedValue(10.0);
- });
- afterEach(() => {
- vi.useRealTimers();
- });
- it("should match key-quota.ts pattern: getTimeRangeForPeriodWithMode for daily", async () => {
- // key-quota.ts line 91-95:
- // const keyDailyTimeRange = await getTimeRangeForPeriodWithMode(
- // "daily",
- // keyRow.dailyResetTime ?? "00:00",
- // (keyRow.dailyResetMode as DailyResetMode | undefined) ?? "fixed"
- // );
- findUserByIdMock.mockResolvedValue({
- id: 1,
- name: "Test User",
- dailyResetMode: "rolling",
- dailyResetTime: "12:00",
- dailyQuota: 10,
- limit5hUsd: null,
- limitWeeklyUsd: null,
- limitMonthlyUsd: null,
- limitTotalUsd: null,
- });
- const { getUserAllLimitUsage } = await import("@/actions/users");
- await getUserAllLimitUsage(1);
- // Should use getTimeRangeForPeriodWithMode matching key-quota.ts pattern
- expect(getTimeRangeForPeriodWithModeMock).toHaveBeenCalledWith("daily", "12:00", "rolling");
- });
- });
|