diff --git a/model/fingerprinting_test.go b/model/fingerprinting_test.go new file mode 100644 index 000000000..b12375100 --- /dev/null +++ b/model/fingerprinting_test.go @@ -0,0 +1,214 @@ +// Copyright 2019 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 model + +import ( + "sort" + "testing" +) + +func TestFingerprintFromString(t *testing.T) { + fs := "4294967295" + + f, err := FingerprintFromString(fs) + + if err != nil { + t.Errorf("unexpected error while getting Fingerprint from string: %s", err.Error()) + } + + expected := Fingerprint(285960729237) + + if expected != f { + t.Errorf("expected to get %d, but got %d instead", f, expected) + } + + f, err = ParseFingerprint(fs) + + if err != nil { + t.Errorf("unexpected error while getting Fingerprint from string: %s", err.Error()) + } + + if expected != f { + t.Errorf("expected to get %d, but got %d instead", f, expected) + } +} + +func TestFingerprintsSort(t *testing.T) { + fingerPrints := Fingerprints{ + 14695981039346656037, + 285960729237, + 0, + 4294967295, + 285960729237, + 18446744073709551615, + } + + sort.Sort(fingerPrints) + + expected := Fingerprints{ + 0, + 4294967295, + 285960729237, + 285960729237, + 14695981039346656037, + 18446744073709551615, + } + + for i, f := range fingerPrints { + if f != expected[i] { + t.Errorf("expected Fingerprint %d, but got %d for index %d", expected[i], f, i) + } + } +} + +func TestFingerprintSet(t *testing.T) { + // Testing with two sets of unequal length. + f := FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + 285960729237: struct{}{}, + 18446744073709551615: struct{}{}, + } + + f2 := FingerprintSet{ + 285960729237: struct{}{}, + } + + if f.Equal(f2) { + t.Errorf("expected two FingerPrintSets of unequal length to be unequal") + } + + // Testing with two unequal sets of equal length. + f = FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + } + + f2 = FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 285960729237: struct{}{}, + } + + if f.Equal(f2) { + t.Errorf("expected two FingerPrintSets of unequal content to be unequal") + } + + // Testing with equal sets of equal length. + f = FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + } + + f2 = FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + } + + if !f.Equal(f2) { + t.Errorf("expected two FingerPrintSets of equal content to be equal") + } +} + +func TestFingerprintIntersection(t *testing.T) { + scenarios := []struct { + name string + input1 FingerprintSet + input2 FingerprintSet + expected FingerprintSet + }{ + { + name: "two empty sets", + input1: FingerprintSet{}, + input2: FingerprintSet{}, + expected: FingerprintSet{}, + }, + { + name: "one empty set", + input1: FingerprintSet{ + 0: struct{}{}, + }, + input2: FingerprintSet{}, + expected: FingerprintSet{}, + }, + { + name: "two non-empty unequal sets", + input1: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + }, + + input2: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + }, + expected: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + }, + }, + { + name: "two non-empty equal sets", + input1: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 285960729237: struct{}{}, + }, + + input2: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 4294967295: struct{}{}, + }, + expected: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + }, + }, + { + name: "two non-empty equal sets of unequal length", + input1: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + 285960729237: struct{}{}, + }, + + input2: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + }, + expected: FingerprintSet{ + 14695981039346656037: struct{}{}, + 0: struct{}{}, + }, + }, + } + + for _, scenario := range scenarios { + s1 := scenario.input1 + s2 := scenario.input2 + actual := s1.Intersection(s2) + + if !actual.Equal(scenario.expected) { + t.Errorf("expected %v to be equal to %v", actual, scenario.expected) + } + } +} diff --git a/model/labels_test.go b/model/labels_test.go index e8df28ffa..2ee5b31a3 100644 --- a/model/labels_test.go +++ b/model/labels_test.go @@ -138,3 +138,63 @@ func TestLabelNameIsValid(t *testing.T) { } } } + +func TestSortLabelPairs(t *testing.T) { + labelPairs := LabelPairs{ + { + Name: "FooName", + Value: "FooValue", + }, + { + Name: "FooName", + Value: "BarValue", + }, + { + Name: "BarName", + Value: "FooValue", + }, + { + Name: "BazName", + Value: "BazValue", + }, + { + Name: "BarName", + Value: "FooValue", + }, + { + Name: "BazName", + Value: "FazValue", + }, + } + + sort.Sort(labelPairs) + + expectedLabelPairs := LabelPairs{ + { + Name: "BarName", + Value: "FooValue", + }, + { + Name: "BarName", + Value: "FooValue", + }, + { + Name: "BazName", + Value: "BazValue", + }, + { + Name: "BazName", + Value: "FazValue", + }, + { + Name: "FooName", + Value: "BarValue", + }, + } + + for i, expected := range expectedLabelPairs { + if expected.Name != labelPairs[i].Name || expected.Value != labelPairs[i].Value { + t.Errorf("%d expected %s, got %s", i, expected, labelPairs[i]) + } + } +} diff --git a/model/labelset_test.go b/model/labelset_test.go new file mode 100644 index 000000000..dfdfc5949 --- /dev/null +++ b/model/labelset_test.go @@ -0,0 +1,120 @@ +// Copyright 2019 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 model + +import ( + "encoding/json" + "testing" +) + +func TestUnmarshalJSONLabelSet(t *testing.T) { + type testConfig struct { + LabelSet LabelSet `yaml:"labelSet,omitempty"` + } + + // valid LabelSet JSON + labelSetJSON := `{ + "labelSet": { + "monitor": "codelab", + "foo": "bar" + } +}` + var c testConfig + err := json.Unmarshal([]byte(labelSetJSON), &c) + + if err != nil { + t.Errorf("unexpected error while marshalling JSON : %s", err.Error()) + } + + labelSetString := c.LabelSet.String() + + expected := `{foo="bar", monitor="codelab"}` + + if expected != labelSetString { + t.Errorf("expected %s but got %s", expected, labelSetString) + } + + // invalid LabelSet JSON + invalidlabelSetJSON := `{ + "labelSet": { + "1nvalid_23name": "codelab", + "foo": "bar" + } +}` + + err = json.Unmarshal([]byte(invalidlabelSetJSON), &c) + expectedErr := `"1nvalid_23name" is not a valid label name` + if err == nil || err.Error() != expectedErr { + t.Errorf("expected an error with message '%s' to be thrown", expectedErr) + } +} + +func TestLabelSetClone(t *testing.T) { + labelSet := LabelSet{ + "monitor": "codelab", + "foo": "bar", + "bar": "baz", + } + + cloneSet := labelSet.Clone() + + if len(labelSet) != len(cloneSet) { + t.Errorf("expected the length of the cloned Label set to be %d, but got %d", + len(labelSet), len(cloneSet)) + } + + for ln, lv := range labelSet { + expected := cloneSet[ln] + if expected != lv { + t.Errorf("expected to get LabelValue %s, but got %s for LabelName %s", expected, lv, ln) + } + } +} + +func TestLabelSetMerge(t *testing.T) { + labelSet := LabelSet{ + "monitor": "codelab", + "foo": "bar", + "bar": "baz", + } + + labelSet2 := LabelSet{ + "monitor": "codelab", + "dolor": "mi", + "lorem": "ipsum", + } + + expectedSet := LabelSet{ + "monitor": "codelab", + "foo": "bar", + "bar": "baz", + "dolor": "mi", + "lorem": "ipsum", + } + + mergedSet := labelSet.Merge(labelSet2) + + if len(mergedSet) != len(expectedSet) { + t.Errorf("expected the length of the cloned Label set to be %d, but got %d", + len(expectedSet), len(mergedSet)) + } + + for ln, lv := range mergedSet { + expected := expectedSet[ln] + if expected != lv { + t.Errorf("expected to get LabelValue %s, but got %s for LabelName %s", expected, lv, ln) + } + } + +} diff --git a/model/metric_test.go b/model/metric_test.go index 8745bf27d..db447f6f2 100644 --- a/model/metric_test.go +++ b/model/metric_test.go @@ -134,3 +134,72 @@ func TestMetricNameIsValid(t *testing.T) { } } } + +func TestMetricClone(t *testing.T) { + m := Metric{ + "first_name": "electro", + "occupation": "robot", + "manufacturer": "westinghouse", + } + + m2 := m.Clone() + + if len(m) != len(m2) { + t.Errorf("expected the length of the cloned metric to be equal to the input metric") + } + + for ln, lv := range m2 { + expected := m[ln] + if expected != lv { + t.Errorf("expected label value %s but got %s for label name %s", expected, lv, ln) + } + } +} + +func TestMetricToString(t *testing.T) { + scenarios := []struct { + name string + input Metric + expected string + }{ + { + name: "valid metric without __name__ label", + input: Metric{ + "first_name": "electro", + "occupation": "robot", + "manufacturer": "westinghouse", + }, + expected: `{first_name="electro", manufacturer="westinghouse", occupation="robot"}`, + }, + { + name: "valid metric with __name__ label", + input: Metric{ + "__name__": "electro", + "occupation": "robot", + "manufacturer": "westinghouse", + }, + expected: `electro{manufacturer="westinghouse", occupation="robot"}`, + }, + { + name: "empty metric with __name__ label", + input: Metric{ + "__name__": "fooname", + }, + expected: "fooname", + }, + { + name: "empty metric", + input: Metric{}, + expected: "{}", + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + actual := scenario.input.String() + if actual != scenario.expected { + t.Errorf("expected string output %s but got %s", actual, scenario.expected) + } + }) + } +} diff --git a/model/silence_test.go b/model/silence_test.go index 8eaaf0744..f9aae4fc3 100644 --- a/model/silence_test.go +++ b/model/silence_test.go @@ -206,6 +206,16 @@ func TestSilenceValidate(t *testing.T) { }, err: "creator information missing", }, + { + sil: &Silence{ + Matchers: []*Matcher{}, + StartsAt: ts, + EndsAt: ts, + CreatedAt: ts, + Comment: "comment", + }, + err: "at least one matcher required", + }, } for i, c := range cases {