Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

125 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import test from "node:test";
3
4import type { TelemetrySpan } from "@g1t/contracts";
5
6import { change, count, duration, errorItem, filterOf, matchesLog, niceTicks, parseView, percent, viewHref } from "./observability.ts";
7import { spanTree } from "./observability-trace.ts";
8
9test("a view reads from the address and writes back to it", () => {
10 const view = parseView(new URLSearchParams("range=1h&env=production&q=timeout"));
11 assert.deepEqual(view, { range: "1h", environment: "production", service: null, deployment: null, q: "timeout" });
12 assert.equal(viewHref("/a/b/observability/logs", view), "/a/b/observability/logs?range=1h&env=production&q=timeout");
13 assert.equal(viewHref("/x", view, { env: null, q: null, range: "24h" }), "/x?range=24h");
14 // The default period is left out of the address.
15 assert.equal(viewHref("/x", parseView(new URLSearchParams("range=nonsense"))), "/x");
16 const filter = filterOf(view, 3_600_000);
17 assert.equal(filter.from_us, 0);
18 assert.equal(filter.to_us, 3_600_000_000);
19 assert.deepEqual(filter.environments, ["production"]);
20});
21
22test("numbers read as people read them", () => {
23 assert.equal(duration(0.42), "420 µs");
24 assert.equal(duration(4.25), "4.3 ms");
25 assert.equal(duration(87), "87 ms");
26 assert.equal(duration(1500), "1.5 s");
27 assert.equal(duration(125_000), "2m 5s");
28 assert.equal(duration(null), "—");
29 assert.equal(duration(0), "0");
30 assert.equal(count(12_400), "12.4K");
31 assert.equal(percent(0.0004), "0.04%");
32 assert.equal(percent(0.25), "25%");
33 assert.deepEqual(change(110, 100, true), { text: "+10%", good: false });
34 assert.deepEqual(change(90, 100, true), { text: "−10%", good: true });
35 assert.equal(change(5, 0, true), null);
36 assert.deepEqual(niceTicks(0.37), [0, 0.1, 0.2, 0.3, 0.4]);
37 assert.deepEqual(niceTicks(870), [0, 250, 500, 750, 1000]);
38});
39
40test("a live line is shown only when the page's search would have found it", () => {
41 const log = {
42 severity: "error" as const,
43 body: "Payment timeout for order 7",
44 environment: "production",
45 service: "api",
46 deployment_id: null,
47 attributes: { "user.id": "42" },
48 };
49 const view = parseView(new URLSearchParams("env=production"));
50 assert.equal(matchesLog(log, view, []), true);
51 assert.equal(matchesLog(log, { ...view, q: "timeout" }, []), true);
52 assert.equal(matchesLog(log, { ...view, q: "refund" }, []), false);
53 assert.equal(matchesLog(log, { ...view, q: "user.id=42" }, []), true);
54 assert.equal(matchesLog(log, { ...view, q: "user.id=7" }, []), false);
55 assert.equal(matchesLog(log, { ...view, q: "level:warn" }, []), false);
56 assert.equal(matchesLog(log, view, ["error", "fatal"]), true);
57 assert.equal(matchesLog(log, { ...view, environment: "preview" }, []), false);
58});
59
60const span = (id: string, parent: string | null, start: number): TelemetrySpan => ({
61 trace_id: "t",
62 span_id: id,
63 parent_span_id: parent,
64 name: id,
65 kind: "internal",
66 start_us: start,
67 duration_us: 1,
68 status: "unset",
69 status_message: null,
70 attributes: {},
71 resource: {},
72 events: [],
73 links: [],
74 service: "s",
75 environment: null,
76 deployment_id: null,
77 commit_sha: null,
78 source: "otlp",
79});
80
81test("spans nest under their parents, and orphans are still shown", () => {
82 const rows = spanTree([
83 span("c", "a", 3),
84 span("a", null, 1),
85 span("b", "a", 2),
86 span("x", "missing", 5),
87 span("loop1", "loop2", 9),
88 span("loop2", "loop1", 9),
89 ]);
90 assert.deepEqual(
91 rows.map((r) => [r.span.span_id, r.depth]),
92 [
93 ["a", 0],
94 ["b", 1],
95 ["c", 1],
96 ["x", 0],
97 ["loop1", 0],
98 ["loop2", 0],
99 ],
100 );
101});
102
103test("an error's work item says what, how often and where to look", () => {
104 const item = errorItem(
105 {
106 name: "TypeError",
107 message: "x is undefined",
108 stack: "TypeError\n at f (a.ts:1)",
109 count: 3,
110 first_us: 0,
111 last_us: 60_000_000,
112 service: "web",
113 environment: "production",
114 first_deployment_id: "dep_1",
115 last_request_id: "r1",
116 last_trace_id: null,
117 },
118 "https://g1t.sh/acme/web/observability",
119 );
120 assert.equal(item.title, "TypeError: x is undefined");
121 assert.match(item.body, /3 times/);
122 assert.match(item.body, /requests\/r1/);
123 assert.match(item.body, /dep_1/);
124 assert.doesNotMatch(item.body, /traces\//);
125});