From f5950f9a8dc48bbaedcb24a60385af3e0f7af44e Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Sat, 3 Jan 2026 18:31:01 -0600 Subject: [PATCH] test(DebugLogsPage): add unit tests for fetching, searching, and paginating debug logs --- tests/frontend/DebugLogsPage.test.js | 98 ++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/frontend/DebugLogsPage.test.js diff --git a/tests/frontend/DebugLogsPage.test.js b/tests/frontend/DebugLogsPage.test.js new file mode 100644 index 0000000..034b46f --- /dev/null +++ b/tests/frontend/DebugLogsPage.test.js @@ -0,0 +1,98 @@ +import { mount } from "@vue/test-utils"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import DebugLogsPage from "@/components/debug/DebugLogsPage.vue"; + +// Mock axios +window.axios = { + get: vi.fn(), +}; + +describe("DebugLogsPage.vue", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("fetches and displays logs", async () => { + const mockLogs = [ + { timestamp: Date.now() / 1000, level: "INFO", module: "test", message: "Hello", is_anomaly: 0 }, + { timestamp: (Date.now() - 1000) / 1000, level: "ERROR", module: "test", message: "Boom", is_anomaly: 1, anomaly_type: "repeat" }, + ]; + + window.axios.get.mockResolvedValue({ + data: { + logs: mockLogs, + total: 2, + limit: 100, + offset: 0, + }, + }); + + const wrapper = mount(DebugLogsPage, { + global: { + stubs: ["MaterialDesignIcon"], + }, + }); + + // Wait for axios + await new Promise((resolve) => setTimeout(resolve, 10)); + await wrapper.vm.$nextTick(); + + const logRows = wrapper.findAll(".border-b"); + expect(logRows.length).toBe(2); + expect(wrapper.text()).toContain("Hello"); + expect(wrapper.text()).toContain("Boom"); + expect(wrapper.text().toLowerCase()).toContain("repeat"); // Anomaly badge + }); + + it("handles search input", async () => { + const wrapper = mount(DebugLogsPage, { + global: { + stubs: ["MaterialDesignIcon"], + }, + }); + + const searchInput = wrapper.find("input[placeholder='Search logs...']"); + await searchInput.setValue("error"); + + // Wait for debounce (500ms) + await new Promise((resolve) => setTimeout(resolve, 600)); + + expect(window.axios.get).toHaveBeenCalledWith( + expect.stringContaining("/api/v1/debug/logs"), + expect.objectContaining({ + params: expect.objectContaining({ search: "error" }), + }) + ); + }); + + it("handles pagination", async () => { + window.axios.get.mockResolvedValue({ + data: { + logs: [], + total: 250, + limit: 100, + offset: 0, + }, + }); + + const wrapper = mount(DebugLogsPage, { + global: { + stubs: ["MaterialDesignIcon"], + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 10)); + + // Click next + const nextButton = wrapper.findAll("button").find(b => b.text().includes("Next")); + await nextButton.trigger("click"); + + expect(window.axios.get).toHaveBeenCalledWith( + expect.stringContaining("/api/v1/debug/logs"), + expect.objectContaining({ + params: expect.objectContaining({ offset: 100 }), + }) + ); + }); +}); +