Skip to content

Commit

Permalink
Add tests for pkg/util/test.AssertGatherAndCompare
Browse files Browse the repository at this point in the history
Signed-off-by: Arve Knudsen <[email protected]>
  • Loading branch information
aknuds1 committed Oct 7, 2024
1 parent cd1294b commit 1747a64
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 5 deletions.
37 changes: 32 additions & 5 deletions pkg/util/test/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
)

type ExpectedMetricsContext struct {
Expand Down Expand Up @@ -67,7 +67,20 @@ func (m *ExpectedMetrics) GetNames() []string {
return m.Names
}

// AssertGatherAndCompare asserts that metrics in expectedText are found among g's metrics.
// If, however, any metrics are provided, the following rules apply:
// * Provided metrics that also exist in expectedText are required to be found among g's metrics.
// * Provided metrics that don't exist in expectedText are required to be absent from g's metrics.
func AssertGatherAndCompare(t *testing.T, g prometheus.Gatherer, expectedText string, metrics ...string) {
t.Helper()
assert.NoError(t, gatherAndCompare(g, expectedText, metrics...))
}

func gatherAndCompare(g prometheus.Gatherer, expectedText string, metrics ...string) error {
if len(metrics) == 0 {
return testutil.GatherAndCompare(g, strings.NewReader(expectedText))
}

sc := bufio.NewScanner(strings.NewReader(expectedText))
absent := make([]string, len(metrics))
copy(absent, metrics)
Expand All @@ -85,17 +98,31 @@ func AssertGatherAndCompare(t *testing.T, g prometheus.Gatherer, expectedText st
}
}
}
require.Equal(t, len(metrics), len(required)+len(absent)) // Sanity check.
// Sanity check.
if len(required)+len(absent) != len(metrics) {
panic(fmt.Errorf("length of required+absent doesn't match up with metrics"))
}

if len(required) > 0 {
require.NoError(t, testutil.GatherAndCompare(g, strings.NewReader(expectedText), required...), "should be present: metrics=%s", strings.Join(required, ", "))
if err := testutil.GatherAndCompare(g, strings.NewReader(expectedText), required...); err != nil {
return err
}
}

notAbsent := []string{}
for _, metric := range absent {
count, err := testutil.GatherAndCount(g, metric)
require.NoError(t, err)
if err != nil {
return fmt.Errorf("GatherAndCount(g, %s): %w", metric, err)
}

if count > 0 {
notAbsent = append(notAbsent, metric)
}
}
require.Empty(t, notAbsent, "should be absent: metrics=%s", strings.Join(notAbsent, ", "))
if len(notAbsent) > 0 {
return fmt.Errorf("should be absent: metrics=%s", strings.Join(notAbsent, ", "))
}

return nil
}
126 changes: 126 additions & 0 deletions pkg/util/test/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: AGPL-3.0-only

package test

import (
"testing"

dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)

func TestAssertGatherAndCompare(t *testing.T) {
g := fakeGatherer{
metrics: []*dto.MetricFamily{
{
Name: proto.String("cortex_distributor_deduped_samples_total"),
Help: proto.String("The total number of deduplicated samples."),
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
{
Name: proto.String("environment"),
Value: proto.String("test"),
},
},
TimestampMs: proto.Int64(1000),
Counter: &dto.Counter{
Value: proto.Float64(1),
},
},
},
},
},
}

t.Run("don't specify any metrics", func(t *testing.T) {
// When not specifying any metrics, an error should be returned due to missing metric
// cortex_distributor_latest_seen_sample_timestamp_seconds.
err := gatherAndCompare(g, `
# HELP cortex_distributor_deduped_samples_total The total number of deduplicated samples.
# TYPE cortex_distributor_deduped_samples_total counter
cortex_distributor_deduped_samples_total{environment="test"} 1 1000
# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`)
require.EqualError(t, err, ` # HELP cortex_distributor_deduped_samples_total The total number of deduplicated samples.
# TYPE cortex_distributor_deduped_samples_total counter
cortex_distributor_deduped_samples_total{environment="test"} 1 1000
+# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
+# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
+cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`)
})

t.Run("specify required metric", func(t *testing.T) {
// When specifying that cortex_distributor_deduped_samples_total as the one required metric,
// cortex_distributor_latest_seen_sample_timestamp_seconds should be ignored even if it's missing.
AssertGatherAndCompare(t, g, `
# HELP cortex_distributor_deduped_samples_total The total number of deduplicated samples.
# TYPE cortex_distributor_deduped_samples_total counter
cortex_distributor_deduped_samples_total{environment="test"} 1 1000
# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`, "cortex_distributor_deduped_samples_total")
})

t.Run("specify required metric which isn't there", func(t *testing.T) {
// When specifying that cortex_distributor_deduped_samples_total as the one required metric,
// cortex_distributor_latest_seen_sample_timestamp_seconds should be ignored even if it's missing.
err := gatherAndCompare(g, `
# HELP cortex_distributor_deduped_samples_total The total number of deduplicated samples.
# TYPE cortex_distributor_deduped_samples_total counter
cortex_distributor_deduped_samples_total{environment="test"} 1 1000
# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`, "cortex_distributor_latest_seen_sample_timestamp_seconds")
require.EqualError(t, err, "expected metric name(s) not found: [cortex_distributor_latest_seen_sample_timestamp_seconds]")
})

t.Run("specify required metric and absent metric", func(t *testing.T) {
// Verify that cortex_distributor_deduped_samples_total is found among metrics returned by g,
// and that conversely, cortex_distributor_non_ha_samples_received_total is not found among
// metrics returned by g.
// cortex_distributor_latest_seen_sample_timestamp_seconds is ignored, since it's not among
// the specified metrics.
AssertGatherAndCompare(t, g, `
# HELP cortex_distributor_deduped_samples_total The total number of deduplicated samples.
# TYPE cortex_distributor_deduped_samples_total counter
cortex_distributor_deduped_samples_total{environment="test"} 1 1000
# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`, "cortex_distributor_deduped_samples_total", "cortex_distributor_non_ha_samples_received_total")
})

t.Run("specify absent metric which is actually there", func(t *testing.T) {
// Verify that cortex_distributor_deduped_samples_total is found among metrics returned by g,
// and that conversely, cortex_distributor_non_ha_samples_received_total is not found among
// metrics returned by g.
// cortex_distributor_latest_seen_sample_timestamp_seconds is ignored, since it's not among
// the specified metrics.
err := gatherAndCompare(g, `
# HELP cortex_distributor_latest_seen_sample_timestamp_seconds Unix timestamp of latest received sample per user.
# TYPE cortex_distributor_latest_seen_sample_timestamp_seconds gauge
cortex_distributor_latest_seen_sample_timestamp_seconds{user="userA"} 1111
`, "cortex_distributor_deduped_samples_total", "cortex_distributor_non_ha_samples_received_total")
require.EqualError(t, err, "should be absent: metrics=cortex_distributor_deduped_samples_total")
})
}

type fakeGatherer struct {
metrics []*dto.MetricFamily
err error
}

func (g fakeGatherer) Gather() ([]*dto.MetricFamily, error) {
return g.metrics, g.err
}

0 comments on commit 1747a64

Please sign in to comment.