diff --git a/src/stores/security.test.ts b/src/stores/security.test.ts index 502f9a6..66b82e6 100644 --- a/src/stores/security.test.ts +++ b/src/stores/security.test.ts @@ -7,6 +7,7 @@ vi.mock("@/services/api", () => ({ getStats: vi.fn(), getEvents: vi.fn(), getBlockedIPs: vi.fn(), + getEventsByIP: vi.fn(), getProtectedRoutes: vi.fn(), blockIP: vi.fn(), unblockIP: vi.fn(), @@ -141,6 +142,44 @@ describe("Security Store", () => { }); }); + describe("fetchEventsByIP", () => { + it("returns the events recorded for an IP", async () => { + const { securityApi } = await import("@/services/api"); + const mockEvents = [ + { + id: 1, + event_type: "not_found", + severity: "low", + source_ip: "1.2.3.4", + request_path: "/missing.php", + status_code: 404, + user_agent: "curl/8.4.0", + message: "Not found", + created_at: "2024-01-01", + }, + ]; + vi.mocked(securityApi.getEventsByIP).mockResolvedValue({ + data: { events: mockEvents, ip: "1.2.3.4" }, + } as any); + + const store = useSecurityStore(); + const events = await store.fetchEventsByIP("1.2.3.4"); + + expect(securityApi.getEventsByIP).toHaveBeenCalledWith("1.2.3.4"); + expect(events).toEqual(mockEvents); + }); + + it("returns an empty array when the response has no events", async () => { + const { securityApi } = await import("@/services/api"); + vi.mocked(securityApi.getEventsByIP).mockResolvedValue({ + data: { events: null, ip: "1.2.3.4" }, + } as any); + + const store = useSecurityStore(); + expect(await store.fetchEventsByIP("1.2.3.4")).toEqual([]); + }); + }); + describe("fetchProtectedRoutes - null safety", () => { it("handles null protected_routes response gracefully", async () => { const { securityApi } = await import("@/services/api"); diff --git a/src/stores/security.ts b/src/stores/security.ts index a05e9be..451ffb3 100644 --- a/src/stores/security.ts +++ b/src/stores/security.ts @@ -83,6 +83,11 @@ export const useSecurityStore = defineStore("security", () => { } } + async function fetchEventsByIP(ip: string): Promise { + const response = await securityApi.getEventsByIP(ip); + return response.data.events || []; + } + async function fetchWhitelist() { loading.value = true; error.value = null; @@ -232,6 +237,7 @@ export const useSecurityStore = defineStore("security", () => { fetchStats, fetchEvents, fetchBlockedIPs, + fetchEventsByIP, blockIP, unblockIP, fetchWhitelist, diff --git a/src/views/SecurityView.vue b/src/views/SecurityView.vue index 8a8d7d4..53b590e 100644 --- a/src/views/SecurityView.vue +++ b/src/views/SecurityView.vue @@ -308,6 +308,13 @@ {{ event.status_code || "-" }} + +
+ + +
@@ -655,6 +668,51 @@ + + + + +