import { describe, it, expect, vi, beforeEach } from "vitest"; import { renderHook, act } from "@testing-library/react"; import * as configApi from "../../api/config"; import { useActionConfig } from "../useActionConfig"; vi.mock("../../api/config"); describe("useActionConfig", () => { beforeEach(() => { vi.clearAllMocks(); vi.mocked(configApi.fetchAction).mockResolvedValue({ name: "iptables", filename: "iptables.conf", source_file: "/etc/fail2ban/action.d/iptables.conf", active: false, used_by_jails: [], before: null, after: null, actionstart: "", actionstop: "", actioncheck: "", actionban: "", actionunban: "", actionflush: "", definition_vars: {}, init_vars: {}, has_local_override: false, }); vi.mocked(configApi.updateAction).mockResolvedValue(undefined); }); it("calls fetchAction exactly once for stable name and rerenders", async () => { const { rerender } = renderHook( ({ name }) => useActionConfig(name), { initialProps: { name: "iptables" } }, ); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); expect(configApi.fetchAction).toHaveBeenCalledTimes(1); // Rerender with the same action name; fetch should not be called again. rerender({ name: "iptables" }); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); expect(configApi.fetchAction).toHaveBeenCalledTimes(1); }); it("calls fetchAction again when name changes", async () => { const { rerender } = renderHook( ({ name }) => useActionConfig(name), { initialProps: { name: "iptables" } }, ); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); expect(configApi.fetchAction).toHaveBeenCalledTimes(1); rerender({ name: "ssh" }); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); expect(configApi.fetchAction).toHaveBeenCalledTimes(2); }); });