Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for pkg/util/test.AssertGatherAndCompare #9543

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading