prometheus/prompb/rwcommon/codec_test.go

299 lines
9.0 KiB
Go

// Copyright 2024 Prometheus Team
// 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 rwcommon
import (
"testing"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/metadata"
"github.com/prometheus/prometheus/prompb"
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
)
func TestToLabels(t *testing.T) {
expected := labels.FromStrings("__name__", "metric1", "foo", "bar")
t.Run("v1", func(t *testing.T) {
ts := prompb.TimeSeries{Labels: []prompb.Label{{Name: "__name__", Value: "metric1"}, {Name: "foo", Value: "bar"}}}
b := labels.NewScratchBuilder(2)
require.Equal(t, expected, ts.ToLabels(&b, nil))
require.Equal(t, ts.Labels, prompb.FromLabels(expected, nil))
require.Equal(t, ts.Labels, prompb.FromLabels(expected, ts.Labels))
})
t.Run("v2", func(t *testing.T) {
v2Symbols := []string{"", "__name__", "metric1", "foo", "bar"}
ts := writev2.TimeSeries{LabelsRefs: []uint32{1, 2, 3, 4}}
b := labels.NewScratchBuilder(2)
require.Equal(t, expected, ts.ToLabels(&b, v2Symbols))
// No need for FromLabels in our prod code as we use symbol table to do so.
})
}
func TestFromMetadataType(t *testing.T) {
for _, tc := range []struct {
desc string
input model.MetricType
expectedV1 prompb.MetricMetadata_MetricType
expectedV2 writev2.Metadata_MetricType
}{
{
desc: "with a single-word metric",
input: model.MetricTypeCounter,
expectedV1: prompb.MetricMetadata_COUNTER,
expectedV2: writev2.Metadata_METRIC_TYPE_COUNTER,
},
{
desc: "with a two-word metric",
input: model.MetricTypeStateset,
expectedV1: prompb.MetricMetadata_STATESET,
expectedV2: writev2.Metadata_METRIC_TYPE_STATESET,
},
{
desc: "with an unknown metric",
input: "not-known",
expectedV1: prompb.MetricMetadata_UNKNOWN,
expectedV2: writev2.Metadata_METRIC_TYPE_UNSPECIFIED,
},
} {
t.Run(tc.desc, func(t *testing.T) {
t.Run("v1", func(t *testing.T) {
require.Equal(t, tc.expectedV1, prompb.FromMetadataType(tc.input))
})
t.Run("v2", func(t *testing.T) {
require.Equal(t, tc.expectedV2, writev2.FromMetadataType(tc.input))
})
})
}
}
func TestToMetadata(t *testing.T) {
sym := writev2.NewSymbolTable()
for _, tc := range []struct {
input writev2.Metadata
expected metadata.Metadata
}{
{
input: writev2.Metadata{},
expected: metadata.Metadata{
Type: model.MetricTypeUnknown,
},
},
{
input: writev2.Metadata{
Type: 12414, // Unknown.
},
expected: metadata.Metadata{
Type: model.MetricTypeUnknown,
},
},
{
input: writev2.Metadata{
Type: writev2.Metadata_METRIC_TYPE_COUNTER,
HelpRef: sym.Symbolize("help1"),
UnitRef: sym.Symbolize("unit1"),
},
expected: metadata.Metadata{
Type: model.MetricTypeCounter,
Help: "help1",
Unit: "unit1",
},
},
{
input: writev2.Metadata{
Type: writev2.Metadata_METRIC_TYPE_STATESET,
HelpRef: sym.Symbolize("help2"),
},
expected: metadata.Metadata{
Type: model.MetricTypeStateset,
Help: "help2",
},
},
} {
t.Run("", func(t *testing.T) {
ts := writev2.TimeSeries{Metadata: tc.input}
require.Equal(t, tc.expected, ts.ToMetadata(sym.Symbols()))
})
}
}
func TestToHistogram_Empty(t *testing.T) {
t.Run("v1", func(t *testing.T) {
require.NotNilf(t, prompb.Histogram{}.ToIntHistogram(), "")
require.NotNilf(t, prompb.Histogram{}.ToFloatHistogram(), "")
})
t.Run("v2", func(t *testing.T) {
require.NotNilf(t, writev2.Histogram{}.ToIntHistogram(), "")
require.NotNilf(t, writev2.Histogram{}.ToFloatHistogram(), "")
})
}
// NOTE(bwplotka): This is technically not a valid histogram, but it represents
// important cases to test when copying or converting to/from int/float histograms.
func testIntHistogram() histogram.Histogram {
return histogram.Histogram{
CounterResetHint: histogram.GaugeType,
Schema: 1,
Count: 19,
Sum: 2.7,
ZeroThreshold: 1e-128,
PositiveSpans: []histogram.Span{
{Offset: 0, Length: 4},
{Offset: 0, Length: 0},
{Offset: 0, Length: 3},
},
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 0},
NegativeSpans: []histogram.Span{
{Offset: 0, Length: 5},
{Offset: 1, Length: 0},
{Offset: 0, Length: 1},
},
NegativeBuckets: []int64{1, 2, -2, 1, -1, 0},
CustomValues: []float64{21421, 523},
}
}
// NOTE(bwplotka): This is technically not a valid histogram, but it represents
// important cases to test when copying or converting to/from int/float histograms.
func testFloatHistogram() histogram.FloatHistogram {
return histogram.FloatHistogram{
CounterResetHint: histogram.GaugeType,
Schema: 1,
Count: 19,
Sum: 2.7,
ZeroThreshold: 1e-128,
PositiveSpans: []histogram.Span{
{Offset: 0, Length: 4},
{Offset: 0, Length: 0},
{Offset: 0, Length: 3},
},
PositiveBuckets: []float64{1, 3, 1, 2, 1, 1, 1},
NegativeSpans: []histogram.Span{
{Offset: 0, Length: 5},
{Offset: 1, Length: 0},
{Offset: 0, Length: 1},
},
NegativeBuckets: []float64{1, 3, 1, 2, 1, 1},
CustomValues: []float64{21421, 523},
}
}
func TestFromIntToFloatOrIntHistogram(t *testing.T) {
t.Run("v1", func(t *testing.T) {
// v1 does not support nhcb.
testIntHistWithoutNHCB := testIntHistogram()
testIntHistWithoutNHCB.CustomValues = nil
testFloatHistWithoutNHCB := testFloatHistogram()
testFloatHistWithoutNHCB.CustomValues = nil
h := prompb.FromIntHistogram(123, &testIntHistWithoutNHCB)
require.False(t, h.IsFloatHistogram())
require.Equal(t, int64(123), h.Timestamp)
require.Equal(t, testIntHistWithoutNHCB, *h.ToIntHistogram())
require.Equal(t, testFloatHistWithoutNHCB, *h.ToFloatHistogram())
})
t.Run("v2", func(t *testing.T) {
testIntHist := testIntHistogram()
testFloatHist := testFloatHistogram()
h := writev2.FromIntHistogram(123, &testIntHist)
require.False(t, h.IsFloatHistogram())
require.Equal(t, int64(123), h.Timestamp)
require.Equal(t, testIntHist, *h.ToIntHistogram())
require.Equal(t, testFloatHist, *h.ToFloatHistogram())
})
}
func TestFromFloatToFloatHistogram(t *testing.T) {
t.Run("v1", func(t *testing.T) {
// v1 does not support nhcb.
testFloatHistWithoutNHCB := testFloatHistogram()
testFloatHistWithoutNHCB.CustomValues = nil
h := prompb.FromFloatHistogram(123, &testFloatHistWithoutNHCB)
require.True(t, h.IsFloatHistogram())
require.Equal(t, int64(123), h.Timestamp)
require.Nil(t, h.ToIntHistogram())
require.Equal(t, testFloatHistWithoutNHCB, *h.ToFloatHistogram())
})
t.Run("v2", func(t *testing.T) {
testFloatHist := testFloatHistogram()
h := writev2.FromFloatHistogram(123, &testFloatHist)
require.True(t, h.IsFloatHistogram())
require.Equal(t, int64(123), h.Timestamp)
require.Nil(t, h.ToIntHistogram())
require.Equal(t, testFloatHist, *h.ToFloatHistogram())
})
}
func TestFromIntOrFloatHistogram_ResetHint(t *testing.T) {
for _, tc := range []struct {
input histogram.CounterResetHint
expectedV1 prompb.Histogram_ResetHint
expectedV2 writev2.Histogram_ResetHint
}{
{
input: histogram.UnknownCounterReset,
expectedV1: prompb.Histogram_UNKNOWN,
expectedV2: writev2.Histogram_RESET_HINT_UNSPECIFIED,
},
{
input: histogram.CounterReset,
expectedV1: prompb.Histogram_YES,
expectedV2: writev2.Histogram_RESET_HINT_YES,
},
{
input: histogram.NotCounterReset,
expectedV1: prompb.Histogram_NO,
expectedV2: writev2.Histogram_RESET_HINT_NO,
},
{
input: histogram.GaugeType,
expectedV1: prompb.Histogram_GAUGE,
expectedV2: writev2.Histogram_RESET_HINT_GAUGE,
},
} {
t.Run("", func(t *testing.T) {
t.Run("v1", func(t *testing.T) {
h := testIntHistogram()
h.CounterResetHint = tc.input
got := prompb.FromIntHistogram(1337, &h)
require.Equal(t, tc.expectedV1, got.GetResetHint())
fh := testFloatHistogram()
fh.CounterResetHint = tc.input
got2 := prompb.FromFloatHistogram(1337, &fh)
require.Equal(t, tc.expectedV1, got2.GetResetHint())
})
t.Run("v2", func(t *testing.T) {
h := testIntHistogram()
h.CounterResetHint = tc.input
got := writev2.FromIntHistogram(1337, &h)
require.Equal(t, tc.expectedV2, got.GetResetHint())
fh := testFloatHistogram()
fh.CounterResetHint = tc.input
got2 := writev2.FromFloatHistogram(1337, &fh)
require.Equal(t, tc.expectedV2, got2.GetResetHint())
})
})
}
}