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.

171 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import test from "node:test";
3
4import { OTLP_DOOR, readLogs, readMetrics, readTraces } from "./normalize.ts";
5import { projectOfToken } from "./ingest.ts";
6
7const resource = (attrs: Record<string, string>) => ({ attributes: Object.entries(attrs).map(([key, v]) => ({ key, value: { stringValue: v } })) });
8
9test("a server span is a request, filed under its service and environment", () => {
10 const batch = readTraces(
11 {
12 resourceSpans: [
13 {
14 resource: resource({ "service.name": "billing", "deployment.environment.name": "production", "vcs.ref.head.revision": "abc123" }),
15 scopeSpans: [
16 {
17 spans: [
18 {
19 traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
20 spanId: "00f067aa0ba902b7",
21 name: "POST",
22 kind: 2,
23 startTimeUnixNano: "1760000000000000000",
24 endTimeUnixNano: "1760000000120000000",
25 attributes: [
26 { key: "http.request.method", value: { stringValue: "POST" } },
27 { key: "url.path", value: { stringValue: "/invoices/12345" } },
28 { key: "http.response.status_code", value: { intValue: "502" } },
29 ],
30 events: [
31 {
32 name: "exception",
33 timeUnixNano: "1760000000100000000",
34 attributes: [{ key: "exception.type", value: { stringValue: "UpstreamError" } }],
35 },
36 ],
37 status: { code: 2, message: "upstream refused" },
38 },
39 // Ids of zeros are no ids: the span is dropped.
40 { traceId: "00000000000000000000000000000000", spanId: "0000000000000000", startTimeUnixNano: "1" },
41 ],
42 },
43 ],
44 },
45 ],
46 },
47 OTLP_DOOR,
48 100,
49 );
50 assert.equal(batch.spans.length, 1);
51 const span = batch.spans[0];
52 assert.equal(span.service, "billing");
53 assert.equal(span.environment, "production");
54 assert.equal(span.commit_sha, "abc123");
55 assert.equal(span.start_us, 1_760_000_000_000_000);
56 assert.equal(span.duration_us, 120_000);
57 assert.equal(span.status, "error");
58 assert.equal(batch.requests.length, 1);
59 assert.equal(batch.requests[0].route, "/invoices/:id");
60 assert.equal(batch.requests[0].status, 502);
61 assert.equal(batch.requests[0].outcome, "error");
62 assert.equal(batch.errors[0].name, "UpstreamError");
63});
64
65test("an app on g1t.page has its requests counted from its tail events, not its SDK's spans", () => {
66 const batch = readTraces(
67 {
68 resourceSpans: [
69 {
70 resource: resource({ "service.name": "web", "g1t.deployment.id": "dep_1" }),
71 scopeSpans: [
72 {
73 spans: [
74 {
75 traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
76 spanId: "00f067aa0ba902b7",
77 kind: "SPAN_KIND_SERVER" as never,
78 startTimeUnixNano: "1760000000000000000",
79 },
80 ],
81 },
82 ],
83 },
84 ],
85 },
86 OTLP_DOOR,
87 0,
88 );
89 assert.equal(batch.spans[0].kind, "server");
90 assert.equal(batch.spans[0].deployment_id, "dep_1");
91 assert.equal(batch.requests.length, 0);
92});
93
94test("log records keep their severity, body and trace", () => {
95 const batch = readLogs(
96 {
97 resourceLogs: [
98 {
99 resource: resource({ "service.name": "worker" }),
100 scopeLogs: [
101 {
102 logRecords: [
103 { timeUnixNano: "1760000000000000000", severityText: "warning", body: { stringValue: "slow" } },
104 {
105 observedTimeUnixNano: "1760000000000000000",
106 severityNumber: 17,
107 body: { kvlistValue: { values: [{ key: "a", value: { intValue: "1" } }] } },
108 attributes: [{ key: "exception.type", value: { stringValue: "Boom" } }],
109 },
110 ],
111 },
112 ],
113 },
114 ],
115 },
116 OTLP_DOOR,
117 0,
118 );
119 assert.equal(batch.logs[0].severity, "warn");
120 assert.equal(batch.logs[0].body, "slow");
121 assert.equal(batch.logs[1].severity, "error");
122 assert.equal(batch.logs[1].body, '{"a":1}');
123 assert.equal(batch.errors.length, 1);
124});
125
126test("metrics of each kind become points", () => {
127 const batch = readMetrics(
128 {
129 resourceMetrics: [
130 {
131 resource: resource({ "service.name": "api" }),
132 scopeMetrics: [
133 {
134 metrics: [
135 { name: "queue.depth", gauge: { dataPoints: [{ timeUnixNano: "1760000000000000000", asInt: "7" }] } },
136 { name: "jobs", sum: { aggregationTemporality: 2, isMonotonic: true, dataPoints: [{ timeUnixNano: "1760000000000000000", asDouble: 3 }] } },
137 {
138 name: "latency",
139 unit: "ms",
140 histogram: { dataPoints: [{ timeUnixNano: "1760000000000000000", count: "4", sum: 40, explicitBounds: [10], bucketCounts: ["2", "2"] }] },
141 },
142 ],
143 },
144 ],
145 },
146 ],
147 },
148 OTLP_DOOR,
149 0,
150 );
151 assert.deepEqual(
152 batch.metrics.map((m) => [m.name, m.kind, m.value]),
153 [
154 ["queue.depth", "gauge", 7],
155 ["jobs", "sum", 3],
156 ["latency", "histogram", 10],
157 ],
158 );
159 assert.equal(batch.metrics[1].monotonic, true);
160 assert.deepEqual(batch.metrics[2].counts, [2, 2]);
161});
162
163test("a token names its project", () => {
164 assert.equal(projectOfToken("g1to_prj_01jb2k7x9hfq0b3zj0f5s2m8ra_abcdef0123"), "prj_01jb2k7x9hfq0b3zj0f5s2m8ra");
165 // Only a project id as g1t makes them: anything else never reaches a store.
166 assert.equal(projectOfToken("g1to_prj_01J9ZK3_abcdef0123"), null);
167 assert.equal(projectOfToken("g1to_anything-at-all_abcdef0123"), null);
168 assert.equal(projectOfToken("g1to_prj_01jb2k7x9hfq0b3zj0f5s2m8rax_abcdef0123"), null);
169 assert.equal(projectOfToken("g1t_prj_x_y"), null);
170 assert.equal(projectOfToken("g1to_nounderscore"), null);
171});