diff options
| author | Stefan Majewsky <majewsky@gmx.net> | 2026-08-17 22:56:47 +0200 |
|---|---|---|
| committer | Stefan Majewsky <majewsky@gmx.net> | 2026-08-17 22:56:47 +0200 |
| commit | 22e8f3548a72c78deccedc5a65c1cba029f52e6d (patch) | |
| tree | 9b4ad006f91ea2c6c3c97d00f665f79e544c79c5 /testing/microprom | |
| parent | a48f97c25d421c0da21467f8e0e736c19c58c6e1 (diff) | |
| download | go-gg-22e8f3548a72c78deccedc5a65c1cba029f52e6d.tar.gz | |
add testing/microprom, initial parity with promhttp
Diffstat (limited to 'testing/microprom')
| -rw-r--r-- | testing/microprom/handler_test.go | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/testing/microprom/handler_test.go b/testing/microprom/handler_test.go new file mode 100644 index 0000000..6aa60f0 --- /dev/null +++ b/testing/microprom/handler_test.go @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net> +// SPDX-License-Identifier: Apache-2.0 + +package microprom_test + +import ( + "context" + "fmt" + "io" + "maps" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.xyrillian.de/gg/assert" + "go.xyrillian.de/gg/microprom" +) + +func TestHandlerFunctionallyIdenticalToPromhttp(t *testing.T) { + // build a microprom.Handler rendering two metric families + h1 := microprom.Handler{ + Families: map[microprom.MetricFamilyName]microprom.MetricFamilyInfo{ + "events": { + Type: microprom.MetricTypeCounter, + Help: "Counts events that happened.", + }, + "memory_usage_bytes": { + Type: microprom.MetricTypeGauge, + Help: "How much memory is currently used.", + }, + }, + SortOutput: true, + Collect: func(ctx context.Context, ms *microprom.MetricSet) error { + labelNames := microprom.NewLabelNames("shard", "type") + for idx := range 5 { + labels := ms.FormatLabels(labelNames, fmt.Sprintf("node%d", idx), "update") + ms.Add("events", labels, float64(10*idx)) + } + ms.Add("memory_usage_bytes", "", 42<<20) + return nil + }, + } + + // build a promhttp.Handler rendering the same metric families + eventsCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "events_total", + Help: "Counts events that happened.", + }, []string{"type", "shard"}) + for idx := range 5 { + eventsCounter.With(prometheus.Labels{ + "type": "update", + "shard": fmt.Sprintf("node%d", idx), + }).Add(float64(10 * idx)) + } + memoryUsageBytesGauge := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "memory_usage_bytes", + Help: "How much memory is currently used.", + }) + memoryUsageBytesGauge.Set(42 << 20) + r := prometheus.NewRegistry() + r.MustRegister(eventsCounter) + r.MustRegister(memoryUsageBytesGauge) + h2 := promhttp.HandlerFor(r, promhttp.HandlerOpts{EnableOpenMetrics: true}) + + // test identical behavior for Prometheus Text Format + body1, headers1 := getMetrics(t, h1, nil) + body2, headers2 := getMetrics(t, h2, nil) + assert.Equal(t, strings.Split(body1, "\n"), strings.Split(body2, "\n")) + assert.Equal(t, headers1, headers2) + + // test identical behavior for OpenMetrics 1.0 text format + body1, headers1 = getMetrics(t, h1, http.Header{"Accept": {"application/openmetrics-text; version=1.0.0"}}) + body2, headers2 = getMetrics(t, h2, http.Header{"Accept": {"application/openmetrics-text; version=1.0.0"}}) + assert.Equal(t, strings.Split(body1, "\n"), strings.Split(body2, "\n")) + assert.Equal(t, headers1, headers2) + + // test invalid Accept header + body1, headers1 = getMetrics(t, h1, http.Header{"Accept": {"image/*"}}) + body2, headers2 = getMetrics(t, h2, http.Header{"Accept": {"image/*"}}) + assert.Equal(t, strings.Split(body1, "\n"), strings.Split(body2, "\n")) + assert.Equal(t, headers1, headers2) +} + +func getMetrics(t *testing.T, h http.Handler, requestHeaders http.Header) (responseBody string, responseHeaders http.Header) { + t.Helper() + r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", nil) + maps.Copy(r.Header, requestHeaders) + + w := httptest.NewRecorder() + h.ServeHTTP(w, r) + resp := w.Result() + + buf, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err.Error()) + } + return string(buf), resp.Header +} |
