// Copyright 2017 The Prometheus 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 remote import ( "fmt" "testing" "github.com/stretchr/testify/require" "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/prompb" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/testutil" ) func TestValidateLabelsAndMetricName(t *testing.T) { tests := []struct { input labels.Labels expectedErr string shouldPass bool description string }{ { input: labels.FromStrings( "__name__", "name", "labelName", "labelValue", ), expectedErr: "", shouldPass: true, description: "regular labels", }, { input: labels.FromStrings( "__name__", "name", "_labelName", "labelValue", ), expectedErr: "", shouldPass: true, description: "label name with _", }, { input: labels.FromStrings( "__name__", "name", "@labelName", "labelValue", ), expectedErr: "invalid label name: @labelName", shouldPass: false, description: "label name with @", }, { input: labels.FromStrings( "__name__", "name", "123labelName", "labelValue", ), expectedErr: "invalid label name: 123labelName", shouldPass: false, description: "label name starts with numbers", }, { input: labels.FromStrings( "__name__", "name", "", "labelValue", ), expectedErr: "invalid label name: ", shouldPass: false, description: "label name is empty string", }, { input: labels.FromStrings( "__name__", "name", "labelName", string([]byte{0xff}), ), expectedErr: "invalid label value: " + string([]byte{0xff}), shouldPass: false, description: "label value is an invalid UTF-8 value", }, { input: labels.FromStrings( "__name__", "@invalid_name", ), expectedErr: "invalid metric name: @invalid_name", shouldPass: false, description: "metric name starts with @", }, { input: labels.FromStrings( "__name__", "name1", "__name__", "name2", ), expectedErr: "duplicate label with name: __name__", shouldPass: false, description: "duplicate label names", }, { input: labels.FromStrings( "label1", "name", "label2", "name", ), expectedErr: "", shouldPass: true, description: "duplicate label values", }, { input: labels.FromStrings( "", "name", "label2", "name", ), expectedErr: "invalid label name: ", shouldPass: false, description: "don't report as duplicate label name", }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { err := validateLabelsAndMetricName(test.input) if err == nil { if !test.shouldPass { t.Fatalf("Test should fail, but passed instead.") } } else { if test.shouldPass { t.Fatalf("Test should pass, got unexpected error: %v", err) } else if err.Error() != test.expectedErr { t.Fatalf("Test should fail with: %s got unexpected error instead: %v", test.expectedErr, err) } } }) } } func TestConcreteSeriesSet(t *testing.T) { series1 := &concreteSeries{ labels: labels.FromStrings("foo", "bar"), samples: []prompb.Sample{{Value: 1, Timestamp: 2}}, } series2 := &concreteSeries{ labels: labels.FromStrings("foo", "baz"), samples: []prompb.Sample{{Value: 3, Timestamp: 4}}, } c := &concreteSeriesSet{ series: []storage.Series{series1, series2}, } if !c.Next() { t.Fatalf("Expected Next() to be true.") } if c.At() != series1 { t.Fatalf("Unexpected series returned.") } if !c.Next() { t.Fatalf("Expected Next() to be true.") } if c.At() != series2 { t.Fatalf("Unexpected series returned.") } if c.Next() { t.Fatalf("Expected Next() to be false.") } } func TestConcreteSeriesClonesLabels(t *testing.T) { lbls := labels.Labels{ labels.Label{Name: "a", Value: "b"}, labels.Label{Name: "c", Value: "d"}, } cs := concreteSeries{ labels: labels.New(lbls...), } gotLabels := cs.Labels() require.Equal(t, lbls, gotLabels) gotLabels[0].Value = "foo" gotLabels[1].Value = "bar" gotLabels = cs.Labels() require.Equal(t, lbls, gotLabels) } func TestFromQueryResultWithDuplicates(t *testing.T) { ts1 := prompb.TimeSeries{ Labels: []prompb.Label{ prompb.Label{Name: "foo", Value: "bar"}, prompb.Label{Name: "foo", Value: "def"}, }, Samples: []prompb.Sample{ prompb.Sample{Value: 0.0, Timestamp: 0}, }, } res := prompb.QueryResult{ Timeseries: []*prompb.TimeSeries{ &ts1, }, } series := FromQueryResult(&res) errSeries, isErrSeriesSet := series.(errSeriesSet) testutil.Assert(t, isErrSeriesSet, "Expected resulting series to be an errSeriesSet") errMessage := errSeries.Err().Error() testutil.Assert(t, errMessage == "duplicate label with name: foo", fmt.Sprintf("Expected error to be from duplicate label, but got: %s", errMessage)) }