github.com/MetalBlockchain/metalgo@v1.11.9/api/metrics/label_gatherer_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package metrics
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/prometheus/client_golang/prometheus"
    10  	"github.com/stretchr/testify/require"
    11  	"google.golang.org/protobuf/proto"
    12  
    13  	dto "github.com/prometheus/client_model/go"
    14  )
    15  
    16  func TestLabelGatherer_Gather(t *testing.T) {
    17  	const (
    18  		labelName         = "smith"
    19  		labelValueA       = "rick"
    20  		labelValueB       = "morty"
    21  		customLabelName   = "tag"
    22  		customLabelValueA = "a"
    23  		customLabelValueB = "b"
    24  	)
    25  	tests := []struct {
    26  		name            string
    27  		labelName       string
    28  		expectedMetrics []*dto.Metric
    29  		expectErr       bool
    30  	}{
    31  		{
    32  			name:      "no overlap",
    33  			labelName: customLabelName,
    34  			expectedMetrics: []*dto.Metric{
    35  				{
    36  					Label: []*dto.LabelPair{
    37  						{
    38  							Name:  proto.String(labelName),
    39  							Value: proto.String(labelValueB),
    40  						},
    41  						{
    42  							Name:  proto.String(customLabelName),
    43  							Value: proto.String(customLabelValueB),
    44  						},
    45  					},
    46  					Counter: &dto.Counter{
    47  						Value: proto.Float64(1),
    48  					},
    49  				},
    50  				{
    51  					Label: []*dto.LabelPair{
    52  						{
    53  							Name:  proto.String(labelName),
    54  							Value: proto.String(labelValueA),
    55  						},
    56  						{
    57  							Name:  proto.String(customLabelName),
    58  							Value: proto.String(customLabelValueA),
    59  						},
    60  					},
    61  					Counter: &dto.Counter{
    62  						Value: proto.Float64(0),
    63  					},
    64  				},
    65  			},
    66  			expectErr: false,
    67  		},
    68  		{
    69  			name:      "has overlap",
    70  			labelName: labelName,
    71  			expectedMetrics: []*dto.Metric{
    72  				{
    73  					Label: []*dto.LabelPair{
    74  						{
    75  							Name:  proto.String(labelName),
    76  							Value: proto.String(labelValueB),
    77  						},
    78  						{
    79  							Name:  proto.String(customLabelName),
    80  							Value: proto.String(customLabelValueB),
    81  						},
    82  					},
    83  					Counter: &dto.Counter{
    84  						Value: proto.Float64(1),
    85  					},
    86  				},
    87  			},
    88  			expectErr: true,
    89  		},
    90  	}
    91  	for _, test := range tests {
    92  		t.Run(test.name, func(t *testing.T) {
    93  			require := require.New(t)
    94  
    95  			gatherer := NewLabelGatherer(labelName)
    96  			require.NotNil(gatherer)
    97  
    98  			registerA := prometheus.NewRegistry()
    99  			require.NoError(gatherer.Register(labelValueA, registerA))
   100  			{
   101  				counterA := prometheus.NewCounterVec(
   102  					counterOpts,
   103  					[]string{test.labelName},
   104  				)
   105  				counterA.With(prometheus.Labels{test.labelName: customLabelValueA})
   106  				require.NoError(registerA.Register(counterA))
   107  			}
   108  
   109  			registerB := prometheus.NewRegistry()
   110  			require.NoError(gatherer.Register(labelValueB, registerB))
   111  			{
   112  				counterB := prometheus.NewCounterVec(
   113  					counterOpts,
   114  					[]string{customLabelName},
   115  				)
   116  				counterB.With(prometheus.Labels{customLabelName: customLabelValueB}).Inc()
   117  				require.NoError(registerB.Register(counterB))
   118  			}
   119  
   120  			metrics, err := gatherer.Gather()
   121  			if test.expectErr {
   122  				require.Error(err) //nolint:forbidigo // the error is not exported
   123  			} else {
   124  				require.NoError(err)
   125  			}
   126  			require.Equal(
   127  				[]*dto.MetricFamily{
   128  					{
   129  						Name:   proto.String(counterOpts.Name),
   130  						Help:   proto.String(counterOpts.Help),
   131  						Type:   dto.MetricType_COUNTER.Enum(),
   132  						Metric: test.expectedMetrics,
   133  					},
   134  				},
   135  				metrics,
   136  			)
   137  		})
   138  	}
   139  }
   140  
   141  func TestLabelGatherer_Register(t *testing.T) {
   142  	firstLabeledGatherer := &labeledGatherer{
   143  		labelValue: "first",
   144  		gatherer:   &testGatherer{},
   145  	}
   146  	firstLabelGatherer := func() *labelGatherer {
   147  		return &labelGatherer{
   148  			multiGatherer: multiGatherer{
   149  				names: []string{firstLabeledGatherer.labelValue},
   150  				gatherers: prometheus.Gatherers{
   151  					firstLabeledGatherer,
   152  				},
   153  			},
   154  		}
   155  	}
   156  	secondLabeledGatherer := &labeledGatherer{
   157  		labelValue: "second",
   158  		gatherer: &testGatherer{
   159  			mfs: []*dto.MetricFamily{{}},
   160  		},
   161  	}
   162  	secondLabelGatherer := &labelGatherer{
   163  		multiGatherer: multiGatherer{
   164  			names: []string{
   165  				firstLabeledGatherer.labelValue,
   166  				secondLabeledGatherer.labelValue,
   167  			},
   168  			gatherers: prometheus.Gatherers{
   169  				firstLabeledGatherer,
   170  				secondLabeledGatherer,
   171  			},
   172  		},
   173  	}
   174  
   175  	tests := []struct {
   176  		name                  string
   177  		labelGatherer         *labelGatherer
   178  		labelValue            string
   179  		gatherer              prometheus.Gatherer
   180  		expectedErr           error
   181  		expectedLabelGatherer *labelGatherer
   182  	}{
   183  		{
   184  			name:                  "first registration",
   185  			labelGatherer:         &labelGatherer{},
   186  			labelValue:            "first",
   187  			gatherer:              firstLabeledGatherer.gatherer,
   188  			expectedErr:           nil,
   189  			expectedLabelGatherer: firstLabelGatherer(),
   190  		},
   191  		{
   192  			name:                  "second registration",
   193  			labelGatherer:         firstLabelGatherer(),
   194  			labelValue:            "second",
   195  			gatherer:              secondLabeledGatherer.gatherer,
   196  			expectedErr:           nil,
   197  			expectedLabelGatherer: secondLabelGatherer,
   198  		},
   199  		{
   200  			name:                  "conflicts with previous registration",
   201  			labelGatherer:         firstLabelGatherer(),
   202  			labelValue:            "first",
   203  			gatherer:              secondLabeledGatherer.gatherer,
   204  			expectedErr:           errDuplicateGatherer,
   205  			expectedLabelGatherer: firstLabelGatherer(),
   206  		},
   207  	}
   208  	for _, test := range tests {
   209  		t.Run(test.name, func(t *testing.T) {
   210  			require := require.New(t)
   211  
   212  			err := test.labelGatherer.Register(test.labelValue, test.gatherer)
   213  			require.ErrorIs(err, test.expectedErr)
   214  			require.Equal(test.expectedLabelGatherer, test.labelGatherer)
   215  		})
   216  	}
   217  }