import { beforeEach, describe, expect, it, vi } from "vitest"; import { ACTION_ERROR_API_MESSAGES, ACTION_ERROR_MESSAGES, ACTION_ERROR_STATUS, } from "@/lib/action-errors"; import { SCHEDULE_FREQUENCY, type ScheduleFormValues } from "@/types/schedules"; const { scanOnDemandMock, scheduleDailyMock, updateScheduleMock } = vi.hoisted( () => ({ scanOnDemandMock: vi.fn(), scheduleDailyMock: vi.fn(), updateScheduleMock: vi.fn(), }), ); vi.mock("@/actions/scans", () => ({ scanOnDemand: scanOnDemandMock, scheduleDaily: scheduleDailyMock, })); vi.mock("@/actions/schedules", () => ({ updateSchedule: updateScheduleMock, })); import { SAVE_SCHEDULE_STATUS, saveScheduleWithInitialScan, } from "./save-schedule"; const values: ScheduleFormValues = { frequency: SCHEDULE_FREQUENCY.WEEKLY, hour: 9, dayOfWeek: 1, dayOfMonth: 1, intervalHours: 48, launchInitialScan: false, }; describe("saveScheduleWithInitialScan", () => { beforeEach(() => { vi.clearAllMocks(); updateScheduleMock.mockResolvedValue({}); scheduleDailyMock.mockResolvedValue({}); scanOnDemandMock.mockResolvedValue({ data: { id: "scan-1" } }); }); it("saves through the schedules API with the mapped payload", async () => { const result = await saveScheduleWithInitialScan({ providerId: "p1", values, }); expect(result.status).toBe(SAVE_SCHEDULE_STATUS.SAVED); expect(updateScheduleMock).toHaveBeenCalledWith( "p1", expect.objectContaining({ scan_enabled: true, scan_frequency: SCHEDULE_FREQUENCY.WEEKLY, scan_hour: 9, scan_day_of_week: 1, }), ); expect(scheduleDailyMock).not.toHaveBeenCalled(); expect(scanOnDemandMock).not.toHaveBeenCalled(); }); it("uses the legacy daily endpoint when requested", async () => { const result = await saveScheduleWithInitialScan({ providerId: "p1", values, useLegacyDaily: true, }); expect(result.status).toBe(SAVE_SCHEDULE_STATUS.SAVED); expect(scheduleDailyMock).toHaveBeenCalledTimes(1); expect(updateScheduleMock).not.toHaveBeenCalled(); }); it("returns an error status when the schedule save fails", async () => { updateScheduleMock.mockResolvedValue({ error: "boom" }); const result = await saveScheduleWithInitialScan({ providerId: "p1", values, }); expect(result).toEqual({ status: SAVE_SCHEDULE_STATUS.ERROR, message: "boom", }); expect(scanOnDemandMock).not.toHaveBeenCalled(); }); it("maps subscription errors when the schedule save is blocked", async () => { updateScheduleMock.mockResolvedValue({ error: ACTION_ERROR_API_MESSAGES[ACTION_ERROR_STATUS.PAYMENT_REQUIRED], status: ACTION_ERROR_STATUS.PAYMENT_REQUIRED, }); const result = await saveScheduleWithInitialScan({ providerId: "p1", values, }); expect(result).toEqual({ status: SAVE_SCHEDULE_STATUS.ERROR, message: ACTION_ERROR_MESSAGES[ACTION_ERROR_STATUS.PAYMENT_REQUIRED], }); expect(scanOnDemandMock).not.toHaveBeenCalled(); }); it("launches the initial scan when requested", async () => { const result = await saveScheduleWithInitialScan({ providerId: "p1", values: { ...values, launchInitialScan: true }, }); expect(result.status).toBe(SAVE_SCHEDULE_STATUS.SAVED_AND_LAUNCHED); const formData = scanOnDemandMock.mock.calls[0][0] as FormData; expect(formData.get("providerId")).toBe("p1"); }); it("reports partial success when the initial scan fails", async () => { scanOnDemandMock.mockResolvedValue({ error: "limit reached" }); const result = await saveScheduleWithInitialScan({ providerId: "p1", values: { ...values, launchInitialScan: true }, }); expect(result).toEqual({ status: SAVE_SCHEDULE_STATUS.SAVED_SCAN_FAILED, message: "limit reached", }); }); it("maps subscription errors when the initial scan is blocked", async () => { scanOnDemandMock.mockResolvedValue({ error: ACTION_ERROR_API_MESSAGES[ACTION_ERROR_STATUS.PAYMENT_REQUIRED], status: ACTION_ERROR_STATUS.PAYMENT_REQUIRED, }); const result = await saveScheduleWithInitialScan({ providerId: "p1", values: { ...values, launchInitialScan: true }, }); expect(result).toEqual({ status: SAVE_SCHEDULE_STATUS.SAVED_SCAN_FAILED, message: ACTION_ERROR_MESSAGES[ACTION_ERROR_STATUS.PAYMENT_REQUIRED], }); }); });