diff --git a/plugins/inputs/vault/vault_test.go b/plugins/inputs/vault/vault_test.go index b0d051c79..fde45c790 100644 --- a/plugins/inputs/vault/vault_test.go +++ b/plugins/inputs/vault/vault_test.go @@ -241,5 +241,5 @@ func TestIntegration(t *testing.T) { require.NoError(t, plugin.Gather(&acc)) actual := acc.GetTelegrafMetrics() - testutil.RequireMetricsStructureEqual(t, expected, actual, options...) + testutil.RequireMetricsStructureSubset(t, expected, actual, options...) } diff --git a/testutil/metric.go b/testutil/metric.go index cb59eaa9e..123f8f84a 100644 --- a/testutil/metric.go +++ b/testutil/metric.go @@ -244,6 +244,49 @@ func RequireMetricsEqual(t testing.TB, expected, actual []telegraf.Metric, opts } } +// RequireMetricsSubset halts the test with an error if the expected array +// of metrics is not a subset of the actual metrics. +func RequireMetricsSubset(t testing.TB, expected, actual []telegraf.Metric, opts ...cmp.Option) { + if x, ok := t.(helper); ok { + x.Helper() + } + + lhs := make([]*metricDiff, 0, len(expected)) + for _, m := range expected { + lhs = append(lhs, newMetricDiff(m)) + } + rhs := make([]*metricDiff, 0, len(actual)) + for _, m := range actual { + rhs = append(rhs, newMetricDiff(m)) + } + + // Sort the metrics + sort.SliceStable(lhs, func(i, j int) bool { + return lessFunc(lhs[i], lhs[j]) + }) + sort.SliceStable(rhs, func(i, j int) bool { + return lessFunc(rhs[i], rhs[j]) + }) + + // Filter the right-hand-side (aka actual) by being contained in the + // left-hand-side (aka expected). + rhsFiltered := make([]*metricDiff, 0, len(rhs)) + for _, r := range rhs { + // Find the next element in the sorted list that might match + for _, l := range lhs { + if cmp.Equal(l, r, opts...) { + rhsFiltered = append(rhsFiltered, r) + break + } + } + } + + opts = append(opts, cmpopts.EquateNaNs()) + if diff := cmp.Diff(lhs, rhsFiltered, opts...); diff != "" { + t.Fatalf("[]telegraf.Metric\n--- expected\n+++ actual\n%s", diff) + } +} + // RequireMetricsStructureEqual halts the test with an error if the array of // metrics is structural different. Structure means that the metric differs // in either name, tag key/values, time (if not ignored) or fields. For fields @@ -268,6 +311,51 @@ func RequireMetricsStructureEqual(t testing.TB, expected, actual []telegraf.Metr } } +// RequireMetricsStructureSubset halts the test with an error if the expected +// array of metrics is not a subset of the actual metrics. The equality here +// is only based on the structure (i.e. key name and value types) and NOT on +// the actual value. +func RequireMetricsStructureSubset(t testing.TB, expected, actual []telegraf.Metric, opts ...cmp.Option) { + if x, ok := t.(helper); ok { + x.Helper() + } + + lhs := make([]*metricDiff, 0, len(expected)) + for _, m := range expected { + lhs = append(lhs, newMetricStructureDiff(m)) + } + rhs := make([]*metricDiff, 0, len(actual)) + for _, m := range actual { + rhs = append(rhs, newMetricStructureDiff(m)) + } + + // Sort the metrics + sort.SliceStable(lhs, func(i, j int) bool { + return lessFunc(lhs[i], lhs[j]) + }) + sort.SliceStable(rhs, func(i, j int) bool { + return lessFunc(rhs[i], rhs[j]) + }) + + // Filter the right-hand-side (aka actual) by being contained in the + // left-hand-side (aka expected). + rhsFiltered := make([]*metricDiff, 0, len(rhs)) + for _, r := range rhs { + // Find the next element in the sorted list that might match + for _, l := range lhs { + if cmp.Equal(l, r, opts...) { + rhsFiltered = append(rhsFiltered, r) + break + } + } + } + + opts = append(opts, cmpopts.EquateNaNs()) + if diff := cmp.Diff(lhs, rhsFiltered, opts...); diff != "" { + t.Fatalf("[]telegraf.Metric\n--- expected\n+++ actual\n%s", diff) + } +} + // MustMetric creates a new metric. func MustMetric( name string, diff --git a/testutil/metric_test.go b/testutil/metric_test.go index e84fc569e..c0cb45801 100644 --- a/testutil/metric_test.go +++ b/testutil/metric_test.go @@ -104,3 +104,156 @@ func TestRequireMetricsEqual(t *testing.T) { }) } } + +func TestRequireMetricsSubset(t *testing.T) { + tests := []struct { + name string + got []telegraf.Metric + want []telegraf.Metric + opts []cmp.Option + }{ + { + name: "subset of metrics", + got: []telegraf.Metric{ + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(3.14)}, + time.Unix(0, 0), + ), + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(42)}, + time.Unix(0, 0), + ), + MustMetric( + "superfluous", + map[string]string{}, + map[string]interface{}{"value": true}, + time.Unix(0, 0), + ), + }, + want: []telegraf.Metric{ + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(42)}, + time.Unix(0, 0), + ), + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(3.14)}, + time.Unix(0, 0), + ), + }, + opts: []cmp.Option{SortMetrics()}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + RequireMetricsSubset(t, tt.want, tt.got, tt.opts...) + }) + } +} + +func TestRequireMetricsStructureEqual(t *testing.T) { + tests := []struct { + name string + got []telegraf.Metric + want []telegraf.Metric + opts []cmp.Option + }{ + { + name: "compare structure", + got: []telegraf.Metric{ + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(3.14)}, + time.Unix(0, 0), + ), + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(42)}, + time.Unix(0, 0), + ), + }, + want: []telegraf.Metric{ + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(0)}, + time.Unix(0, 0), + ), + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(0)}, + time.Unix(0, 0), + ), + }, + opts: []cmp.Option{SortMetrics()}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + RequireMetricsStructureEqual(t, tt.want, tt.got, tt.opts...) + }) + } +} + +func TestRequireMetricsStructureSubset(t *testing.T) { + tests := []struct { + name string + got []telegraf.Metric + want []telegraf.Metric + opts []cmp.Option + }{ + { + name: "subset of metric structure", + got: []telegraf.Metric{ + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(3.14)}, + time.Unix(0, 0), + ), + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(42)}, + time.Unix(0, 0), + ), + MustMetric( + "superfluous", + map[string]string{}, + map[string]interface{}{"value": true}, + time.Unix(0, 0), + ), + }, + want: []telegraf.Metric{ + MustMetric( + "net", + map[string]string{}, + map[string]interface{}{"value": int64(0)}, + time.Unix(0, 0), + ), + MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{"value": float64(0)}, + time.Unix(0, 0), + ), + }, + opts: []cmp.Option{SortMetrics()}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + RequireMetricsStructureSubset(t, tt.want, tt.got, tt.opts...) + }) + } +}