fix(diagnostics): honest live console after review
- Live stops polling on a failed read and marks the list as possibly stale (with Retry), instead of re-raising the error dialog every tick. - While live, a refetch reads the newest page only, whatever the cache holds (a late "Load older", a revisited filter set). - Copy is disabled and the list dimmed while a filter change loads. - Dates follow the UI language; the pivot chip uses the shared .chip. - Tests: query-string wire test, Copy, every facet's skip key.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// @vitest-environment jsdom
|
||||
//
|
||||
// The log console's query strings. The console's DOM tests mock this slice, so nothing else in the
|
||||
// suite looks at the URL that goes out — and a multi-level group is the case that breaks quietly:
|
||||
// `levels` must repeat, or FastAPI's list reads only the last one.
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { adminApi } from "./admin";
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset().mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
headers: new Headers({ "content-type": "application/json" }),
|
||||
json: async () => ({}),
|
||||
text: async () => "{}",
|
||||
});
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
});
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
const lastQuery = (): URLSearchParams => {
|
||||
const calls = fetchMock.mock.calls;
|
||||
return new URL(String(calls[calls.length - 1]?.[0]), "http://x").searchParams;
|
||||
};
|
||||
|
||||
describe("adminApi log query strings", () => {
|
||||
it("repeats the list filters and sends every single one it is given", async () => {
|
||||
await adminApi.adminLogs(
|
||||
{
|
||||
levels: ["ERROR", "CRITICAL"],
|
||||
sources: ["api", "worker"],
|
||||
user: "none",
|
||||
event: "sync.rss.failed",
|
||||
request_id: "req-abcdef12",
|
||||
job_id: 42,
|
||||
q: "100% done",
|
||||
since: "2026-09-19T11:00:00.000Z",
|
||||
},
|
||||
"cursor-1",
|
||||
);
|
||||
const p = lastQuery();
|
||||
expect(p.getAll("levels")).toEqual(["ERROR", "CRITICAL"]);
|
||||
expect(p.getAll("sources")).toEqual(["api", "worker"]);
|
||||
expect(Object.fromEntries([...p].filter(([k]) => k !== "levels" && k !== "sources"))).toEqual({
|
||||
user: "none",
|
||||
event: "sync.rss.failed",
|
||||
request_id: "req-abcdef12",
|
||||
job_id: "42",
|
||||
q: "100% done",
|
||||
since: "2026-09-19T11:00:00.000Z",
|
||||
before: "cursor-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("leaves out what is not set, and the facets carry no cursor", async () => {
|
||||
await adminApi.adminLogs({ q: "", user: undefined }, null);
|
||||
expect(String(fetchMock.mock.calls[0]?.[0])).toBe("/api/admin/logs");
|
||||
|
||||
await adminApi.adminLogFacets({ levels: ["WARNING"], since: "2026-09-19T11:00:00.000Z" });
|
||||
expect([...lastQuery().keys()]).toEqual(["levels", "since"]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user