Files
lolia-frp/pkg/metrics/text/text_test.go
T
fatedier 5c0a125c16 deps: bump prometheus/client_golang to v1.25.0 (#5599)
Move client_golang to v1.25.0 with its required module updates
(client_model v0.6.3, common v0.72.0, procfs v0.22.0, xxhash v2.3.0,
protobuf v1.36.12, munnerz/goautoneg). go.mod keeps go 1.26.0 and has
no toolchain line.

Sync the frpc text metrics clone with the new default Go collector:
add go_gc_gogc_percent, go_gc_gomemlimit_bytes and
go_sched_gomaxprocs_threads, update the go_memstats help texts, and
remove go_memstats_lookups_total. frpc still exposes go_* metrics only.
2026-11-04 14:43:00 +08:00

126 lines
4.1 KiB
Go

// Copyright 2026 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package text
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
)
// gather returns what frpc would serve.
func gather(t *testing.T) string {
t.Helper()
reg := NewRegistry()
reg.RegisterRuntime()
var b strings.Builder
require.NoError(t, reg.WritePrometheus(&b))
return b.String()
}
// parseFamilies decodes the text exposition format with the Prometheus parser,
// so anything a scraper would reject fails the test.
func parseFamilies(t *testing.T, body string) map[string]*dto.MetricFamily {
t.Helper()
parser := expfmt.NewTextParser(model.UTF8Validation)
families, err := parser.TextToMetricFamilies(strings.NewReader(body))
require.NoError(t, err, "output must be valid text exposition format")
return families
}
// TestMatchesClientGolang checks that every go_* family this package emits has
// the same name, type and help text as client_golang's default Go collector. The
// official collector is used as the oracle, so a difference shows up here rather
// than in someone's dashboard.
func TestMatchesClientGolang(t *testing.T) {
reg := prometheus.NewRegistry()
reg.MustRegister(collectors.NewGoCollector())
mfs, err := reg.Gather()
require.NoError(t, err)
reference := make(map[string]*dto.MetricFamily, len(mfs))
for _, mf := range mfs {
reference[mf.GetName()] = mf
}
ours := parseFamilies(t, gather(t))
checked := 0
for name, want := range reference {
if !strings.HasPrefix(name, "go_") {
continue
}
got, ok := ours[name]
require.True(t, ok, "%s is missing", name)
require.Equal(t, want.GetType(), got.GetType(), "%s type", name)
require.Equal(t, want.GetHelp(), got.GetHelp(), "%s help", name)
checked++
}
require.Greater(t, checked, 20, "comparison covered too few families")
}
// TestNoBusinessMetrics pins what frpc must not expose: no frp_ business metrics,
// none of the handler instrumentation client_golang adds, and no process_
// families. frpc reports Go runtime metrics only, on every platform.
func TestNoBusinessMetrics(t *testing.T) {
body := gather(t)
require.NotContains(t, body, "frp_")
require.NotContains(t, body, "promhttp_")
require.NotContains(t, body, "process_")
}
func TestHandler(t *testing.T) {
reg := NewRegistry()
reg.RegisterRuntime()
rec := httptest.NewRecorder()
reg.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/metrics", nil))
require.Equal(t, http.StatusOK, rec.Code)
require.Equal(t, ContentType, rec.Header().Get("Content-Type"))
families := parseFamilies(t, rec.Body.String())
require.Contains(t, families, "go_goroutines")
gc, ok := families["go_gc_duration_seconds"]
require.True(t, ok)
require.Equal(t, dto.MetricType_SUMMARY, gc.GetType())
require.Len(t, gc.GetMetric()[0].GetSummary().GetQuantile(), 5)
}
func TestEmptyRegistry(t *testing.T) {
var b strings.Builder
require.NoError(t, NewRegistry().WritePrometheus(&b))
require.Empty(t, b.String())
}
func TestEscaping(t *testing.T) {
var b strings.Builder
require.NoError(t, encode(&b, []family{{
name: "test_labels",
help: "help with \\ and\nnewline",
typ: typeGauge,
samples: []sample{{labels: []label{{name: "v", value: "a\"b\\c\nd"}}, value: 1}},
}}))
require.Equal(t, "# HELP test_labels help with \\\\ and\\nnewline\n"+
"# TYPE test_labels gauge\n"+
"test_labels{v=\"a\\\"b\\\\c\\nd\"} 1\n", b.String())
}