Path: blob/main/pkg/util/testappender/testappender_test.go
4096 views
package testappender_test12import (3"fmt"4"testing"56"github.com/grafana/agent/pkg/util/testappender"7"github.com/prometheus/prometheus/model/exemplar"8"github.com/prometheus/prometheus/model/labels"9"github.com/prometheus/prometheus/model/metadata"10"github.com/prometheus/prometheus/model/textparse"11"github.com/stretchr/testify/require"12)1314func Example() {15var app testappender.Appender16app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)17app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{18Type: textparse.MetricTypeGauge,19})2021expect := `22# TYPE example_metric gauge23example_metric{foo="bar"} 1234 6024`2526_ = app.Commit()27families, _ := app.MetricFamilies()2829err := testappender.Compare(families, expect)30if err != nil {31fmt.Println("Metrics do not match:", err)32} else {33fmt.Println("Metrics match!")34}35// Output: Metrics match!36}3738// TestAppender_NoOp asserts that not doing anything results in no data.39func TestAppender_NoOp(t *testing.T) {40var app testappender.Appender41requireAppenderData(t, &app, "", false)42}4344func TestAppender_Metadata(t *testing.T) {45t.Run("No metadata", func(t *testing.T) {46var app testappender.Appender47app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)4849expect := `50# TYPE example_metric untyped51example_metric{foo="bar"} 1234 6052`53requireAppenderData(t, &app, expect, false)54})5556t.Run("Has metadata", func(t *testing.T) {57var app testappender.Appender58app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)59app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{60Type: textparse.MetricTypeGauge,61Help: "example metric",62})6364expect := `65# HELP example_metric example metric66# TYPE example_metric gauge67example_metric{foo="bar"} 1234 6068`69requireAppenderData(t, &app, expect, false)70})7172t.Run("No help field", func(t *testing.T) {73var app testappender.Appender74app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)75app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{76Type: textparse.MetricTypeGauge,77})7879expect := `80# TYPE example_metric gauge81example_metric{foo="bar"} 1234 6082`83requireAppenderData(t, &app, expect, false)84})85}8687func TestAppender_Types(t *testing.T) {88t.Run("Untyped", func(t *testing.T) {89var app testappender.Appender90app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)91app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{92Type: textparse.MetricTypeUnknown,93})9495expect := `96# TYPE example_metric untyped97example_metric{foo="bar"} 1234 6098`99requireAppenderData(t, &app, expect, false)100})101102t.Run("Counter", func(t *testing.T) {103var app testappender.Appender104app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)105app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{106Type: textparse.MetricTypeCounter,107})108109expect := `110# TYPE example_metric counter111example_metric{foo="bar"} 1234 60112`113requireAppenderData(t, &app, expect, false)114})115116t.Run("Gauge", func(t *testing.T) {117var app testappender.Appender118app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar"), 60, 1234)119app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{120Type: textparse.MetricTypeGauge,121})122123expect := `124# TYPE example_metric gauge125example_metric{foo="bar"} 1234 60126`127requireAppenderData(t, &app, expect, false)128})129130t.Run("Summary", func(t *testing.T) {131var app testappender.Appender132133// Summaries have quantiles from 0 to 1, counts, and sums. Append the134// metadata first and then append all the various samples.135app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{136Type: textparse.MetricTypeSummary,137})138139app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar", "quantile", "0"), 10, 10)140app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar", "quantile", "0.25"), 10, 10)141app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar", "quantile", "0.50"), 10, 10)142app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar", "quantile", "0.75"), 10, 10)143app.Append(0, labels.FromStrings("__name__", "example_metric", "foo", "bar", "quantile", "1"), 10, 10)144app.Append(0, labels.FromStrings("__name__", "example_metric_count", "foo", "bar"), 10, 5)145app.Append(0, labels.FromStrings("__name__", "example_metric_sum", "foo", "bar"), 10, 50)146147expect := `148# TYPE example_metric summary149example_metric{foo="bar",quantile="0"} 10 10150example_metric{foo="bar",quantile="0.25"} 10 10151example_metric{foo="bar",quantile="0.5"} 10 10152example_metric{foo="bar",quantile="0.75"} 10 10153example_metric{foo="bar",quantile="1"} 10 10154example_metric_sum{foo="bar"} 50 10155example_metric_count{foo="bar"} 5 10156`157requireAppenderData(t, &app, expect, false)158})159160t.Run("Histogram", func(t *testing.T) {161var app testappender.Appender162163// Histograms have buckets, counts, and sums. Append the metadata first and164// then append all the various samples.165app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{166Type: textparse.MetricTypeHistogram,167})168169app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "0.5"), 10, 10)170app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "0.75"), 10, 20)171app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "1"), 10, 30)172app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "+Inf"), 10, 40)173app.Append(0, labels.FromStrings("__name__", "example_metric_count", "foo", "bar"), 10, 4)174app.Append(0, labels.FromStrings("__name__", "example_metric_sum", "foo", "bar"), 10, 100)175176expect := `177# TYPE example_metric histogram178example_metric_bucket{foo="bar",le="0.5"} 10 10179example_metric_bucket{foo="bar",le="0.75"} 20 10180example_metric_bucket{foo="bar",le="1"} 30 10181example_metric_bucket{foo="bar",le="+Inf"} 40 10182example_metric_sum{foo="bar"} 100 10183example_metric_count{foo="bar"} 4 10184`185requireAppenderData(t, &app, expect, false)186})187}188189func TestAppender_Exemplars(t *testing.T) {190// These tests are the only tests where we explicitly test the OpenMetrics191// exposition format since OpenMetrics is the only text exposition format192// that supports exemplars.193194t.Run("Counter", func(t *testing.T) {195var app testappender.Appender196app.Append(0, labels.FromStrings("__name__", "example_metric_total", "foo", "bar"), 60, 1234)197app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric_total"), metadata.Metadata{198Type: textparse.MetricTypeCounter,199})200app.AppendExemplar(0, labels.FromStrings("__name__", "example_metric_total", "foo", "bar"), exemplar.Exemplar{201Labels: labels.FromStrings("hello", "world"),202Value: 1337,203Ts: 30,204HasTs: true,205})206207expect := `208# TYPE example_metric counter209example_metric_total{foo="bar"} 1234.0 0.06 # {hello="world"} 1337.0 0.03210`211requireAppenderData(t, &app, expect, true)212})213214t.Run("Histogram", func(t *testing.T) {215var app testappender.Appender216217// Histograms have buckets, counts, and sums. Append the metadata first and218// then append all the various samples.219app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric"), metadata.Metadata{220Type: textparse.MetricTypeHistogram,221})222223app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "0.5"), 10, 10)224app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "0.75"), 10, 20)225app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "1"), 10, 30)226app.Append(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "+Inf"), 10, 40)227app.Append(0, labels.FromStrings("__name__", "example_metric_count", "foo", "bar"), 10, 4)228app.Append(0, labels.FromStrings("__name__", "example_metric_sum", "foo", "bar"), 10, 100)229230// Exemplars must be attached to a specific bucket.231app.AppendExemplar(0, labels.FromStrings("__name__", "example_metric_bucket", "foo", "bar", "le", "0.75"), exemplar.Exemplar{232Labels: labels.FromStrings("hello", "world"),233Value: 1337,234Ts: 30,235HasTs: true,236})237238expect := `239# TYPE example_metric histogram240example_metric_bucket{foo="bar",le="0.5"} 10 0.01241example_metric_bucket{foo="bar",le="0.75"} 20 0.01 # {hello="world"} 1337.0 0.03242example_metric_bucket{foo="bar",le="1.0"} 30 0.01243example_metric_bucket{foo="bar",le="+Inf"} 40 0.01244example_metric_sum{foo="bar"} 100.0 0.01245example_metric_count{foo="bar"} 4 0.01246`247requireAppenderData(t, &app, expect, true)248})249}250251// TestAppender_MultipleMetrics tests that multiple metrics, where some have252// metadata and others don't, works as expected.253func TestAppender_MultipleMetrics(t *testing.T) {254var app testappender.Appender255256app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric_a"), metadata.Metadata{257Type: textparse.MetricTypeCounter,258})259app.UpdateMetadata(0, labels.FromStrings("__name__", "example_metric_b"), metadata.Metadata{260Type: textparse.MetricTypeGauge,261})262263app.Append(0, labels.FromStrings("__name__", "example_metric_a", "foo", "bar"), 10, 10)264app.Append(0, labels.FromStrings("__name__", "example_metric_a", "foo", "rab"), 10, 20)265app.Append(0, labels.FromStrings("__name__", "example_metric_b", "fizz", "buzz"), 10, 30)266app.Append(0, labels.FromStrings("__name__", "example_metric_b", "fizz", "zzub"), 10, 40)267app.Append(0, labels.FromStrings("__name__", "example_metric_c", "hello", "world"), 10, 50)268app.Append(0, labels.FromStrings("__name__", "example_metric_c", "hello", "dlrow"), 10, 60)269270expect := `271# TYPE example_metric_a counter272example_metric_a{foo="bar"} 10 10273example_metric_a{foo="rab"} 20 10274# TYPE example_metric_b gauge275example_metric_b{fizz="buzz"} 30 10276example_metric_b{fizz="zzub"} 40 10277# TYPE example_metric_c untyped278example_metric_c{hello="dlrow"} 60 10279example_metric_c{hello="world"} 50 10280`281requireAppenderData(t, &app, expect, false)282}283284// requireAppenderData commits the appender and asserts that its resulting data285// matches the Prometheus Exposition Format string specified by expect.286func requireAppenderData(t *testing.T, app *testappender.Appender, expect string, openMetrics bool) {287t.Helper()288289require.NoError(t, app.Commit(), "commit should not have failed")290291families, err := app.MetricFamilies()292require.NoError(t, err, "failed to get metric families")293294var c testappender.Comparer295c.OpenMetrics = openMetrics296require.NoError(t, c.Compare(families, expect))297}298299300