Files
2026-06-23 09:52:20 +02:00

156 lines
4.4 KiB
TypeScript

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],
});
});
});