import { describe, it, expect, vi, beforeEach } from "vitest"; import { renderHook, act } from "@testing-library/react"; import * as jailsApi from "../../api/jails"; import { useJailDetail } from "../useJails"; import type { Jail } from "../../types/jail"; // Mock the API module vi.mock("../../api/jails"); const mockJail: Jail = { name: "sshd", running: true, idle: false, backend: "pyinotify", log_paths: ["/var/log/auth.log"], fail_regex: ["^\\[.*\\]\\s.*Failed password"], ignore_regex: [], date_pattern: "%b %d %H:%M:%S", log_encoding: "UTF-8", actions: [], find_time: 600, ban_time: 600, max_retry: 5, status: null, bantime_escalation: null, }; describe("useJailDetail control methods", () => { beforeEach(() => { vi.clearAllMocks(); vi.mocked(jailsApi.fetchJail).mockResolvedValue({ jail: mockJail, ignore_list: [], ignore_self: false, }); }); it("calls start() and refetches jail data", async () => { vi.mocked(jailsApi.startJail).mockResolvedValue({ message: "jail started", jail: "sshd" }); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); expect(result.current.jail?.name).toBe("sshd"); expect(jailsApi.startJail).not.toHaveBeenCalled(); // Call start() await act(async () => { await result.current.start(); }); expect(jailsApi.startJail).toHaveBeenCalledWith("sshd"); expect(jailsApi.fetchJail).toHaveBeenCalledTimes(2); // Initial fetch + refetch after start }); it("calls stop() and refetches jail data", async () => { vi.mocked(jailsApi.stopJail).mockResolvedValue({ message: "jail stopped", jail: "sshd" }); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call stop() await act(async () => { await result.current.stop(); }); expect(jailsApi.stopJail).toHaveBeenCalledWith("sshd"); expect(jailsApi.fetchJail).toHaveBeenCalledTimes(2); // Initial fetch + refetch after stop }); it("calls reload() and refetches jail data", async () => { vi.mocked(jailsApi.reloadJail).mockResolvedValue({ message: "jail reloaded", jail: "sshd" }); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call reload() await act(async () => { await result.current.reload(); }); expect(jailsApi.reloadJail).toHaveBeenCalledWith("sshd"); expect(jailsApi.fetchJail).toHaveBeenCalledTimes(2); // Initial fetch + refetch after reload }); it("calls setIdle() with correct parameter and refetches jail data", async () => { vi.mocked(jailsApi.setJailIdle).mockResolvedValue({ message: "jail idle toggled", jail: "sshd" }); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call setIdle(true) await act(async () => { await result.current.setIdle(true); }); expect(jailsApi.setJailIdle).toHaveBeenCalledWith("sshd", true); expect(jailsApi.fetchJail).toHaveBeenCalledTimes(2); // Reset mock to verify second call vi.mocked(jailsApi.setJailIdle).mockClear(); vi.mocked(jailsApi.fetchJail).mockResolvedValue({ jail: { ...mockJail, idle: true }, ignore_list: [], ignore_self: false, }); // Call setIdle(false) await act(async () => { await result.current.setIdle(false); }); expect(jailsApi.setJailIdle).toHaveBeenCalledWith("sshd", false); }); it("propagates errors from start()", async () => { const error = new Error("Failed to start jail"); vi.mocked(jailsApi.startJail).mockRejectedValue(error); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call start() and expect it to throw await expect( act(async () => { await result.current.start(); }), ).rejects.toThrow("Failed to start jail"); }); it("propagates errors from stop()", async () => { const error = new Error("Failed to stop jail"); vi.mocked(jailsApi.stopJail).mockRejectedValue(error); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call stop() and expect it to throw await expect( act(async () => { await result.current.stop(); }), ).rejects.toThrow("Failed to stop jail"); }); it("propagates errors from reload()", async () => { const error = new Error("Failed to reload jail"); vi.mocked(jailsApi.reloadJail).mockRejectedValue(error); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call reload() and expect it to throw await expect( act(async () => { await result.current.reload(); }), ).rejects.toThrow("Failed to reload jail"); }); it("propagates errors from setIdle()", async () => { const error = new Error("Failed to set idle mode"); vi.mocked(jailsApi.setJailIdle).mockRejectedValue(error); const { result } = renderHook(() => useJailDetail("sshd")); // Wait for initial fetch await act(async () => { await new Promise((r) => setTimeout(r, 0)); }); // Call setIdle() and expect it to throw await expect( act(async () => { await result.current.setIdle(true); }), ).rejects.toThrow("Failed to set idle mode"); }); });