Menu
popagent
publicLatest change 0ea30ecfc0b99682425d7170ee77bf5bcf8ace06 - Add range, filter, and paging controls to the Observability page by AkurAI Build
import { expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import type { ObservabilityLog, ObservabilityTraceSpan, ObservabilityTraceSummary } from "../api-types";
import {
filterLogs,
filterTraces,
formatDuration,
groupTraces,
observabilityRangeQuery,
ObservabilitySummary,
relativeTime,
spanTimeline,
} from "./ObservabilityPage";
test("renders current process memory in MiB", () => {
const html = renderToStaticMarkup(<ObservabilitySummary overview={{
from: "2026-08-14T00:00:00.000Z",
to: "2026-08-15T00:00:00.000Z",
runs: 12,
errors: 1,
errorRate: 1 / 12,
p95LatencyMs: 420,
inputTokens: 1_200,
outputTokens: 340,
processRssBytes: 73_400_320,
processHeapUsedBytes: 31_457_280,
}} />);
expect(html).toContain("Process RSS");
expect(html).toContain("70.0 MiB");
expect(html).toContain("Heap used");
expect(html).toContain("30.0 MiB");
});
test("builds a bounded query window for each selectable range", () => {
const now = new Date("2026-08-16T12:00:00.000Z");
expect(observabilityRangeQuery("1h", now)).toBe("from=2026-08-16T11:00:00.000Z&to=2026-08-16T12:00:00.000Z");
expect(observabilityRangeQuery("30d", now)).toBe("from=2026-07-17T12:00:00.000Z&to=2026-08-16T12:00:00.000Z");
});
test("formats durations and relative times for operators", () => {
expect(formatDuration(null)).toBe("running");
expect(formatDuration(412)).toBe("412 ms");
expect(formatDuration(12_600)).toBe("12.6 s");
expect(formatDuration(205_020)).toBe("3m 25s");
const now = new Date("2026-08-16T12:00:00.000Z");
expect(relativeTime("2026-08-16T11:59:30.000Z", now)).toBe("30s ago");
expect(relativeTime("2026-08-16T09:00:00.000Z", now)).toBe("3h ago");
});
const trace = (overrides: Partial<ObservabilityTraceSummary>): ObservabilityTraceSummary => ({
traceId: "a".repeat(32),
name: "agent run",
entityName: "Orchistrator",
status: "success",
startedAt: "2026-08-16T11:00:00.000Z",
endedAt: "2026-08-16T11:00:10.000Z",
durationMs: 10_000,
tags: [],
metadata: {},
...overrides,
});
test("collapses repeated root spans into one row per trace", () => {
const groups = groupTraces([
trace({}),
trace({ startedAt: "2026-08-16T10:59:00.000Z", durationMs: 61_000, status: "error" }),
trace({ traceId: "b".repeat(32), entityName: "Implementer" }),
]);
expect(groups).toHaveLength(2);
expect(groups[0]).toMatchObject({ status: "error", rootCount: 2, durationMs: 61_000, startedAt: "2026-08-16T10:59:00.000Z" });
expect(filterTraces(groups, "error", "")).toHaveLength(1);
expect(filterTraces(groups, "all", "implementer")).toHaveLength(1);
expect(filterTraces(groups, "all", "nothing")).toHaveLength(0);
});
test("filters logs by level and message text", () => {
const logs: ObservabilityLog[] = [
{ id: "1", timestamp: "2026-08-16T11:00:00.000Z", level: "warn", message: "task retry", traceId: null, spanId: null, entityName: null, data: {} },
{ id: "2", timestamp: "2026-08-16T11:01:00.000Z", level: "error", message: "connection timeout", traceId: "c".repeat(32), spanId: null, entityName: null, data: {} },
];
expect(filterLogs(logs, [], "")).toHaveLength(2);
expect(filterLogs(logs, ["error"], "")).toHaveLength(1);
expect(filterLogs(logs, [], "timeout")[0]?.id).toBe("2");
});
test("lays spans out as a waterfall relative to the trace window", () => {
const span = (overrides: Partial<ObservabilityTraceSpan>): ObservabilityTraceSpan => ({
...trace({}),
spanId: "s1",
parentSpanId: null,
spanType: "agent_run",
input: null,
output: null,
error: null,
...overrides,
});
const bars = spanTimeline([
span({ spanId: "root", startedAt: "2026-08-16T11:00:00.000Z", endedAt: "2026-08-16T11:00:10.000Z" }),
span({ spanId: "child", parentSpanId: "root", startedAt: "2026-08-16T11:00:05.000Z", endedAt: "2026-08-16T11:00:10.000Z" }),
]);
expect(bars.map((bar) => bar.span.spanId)).toEqual(["root", "child"]);
expect(bars[0]).toMatchObject({ depth: 0, offset: 0, width: 100 });
expect(bars[1]).toMatchObject({ depth: 1, offset: 50, width: 50 });
});