github.com/go-graphite/carbonapi@v0.17.0/zipper/zipper_test.go (about)

     1  package zipper
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"github.com/ansel1/merry"
     9  
    10  	"github.com/go-graphite/carbonapi/zipper/types"
    11  	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"
    12  )
    13  
    14  type mergeValuesData struct {
    15  	name           string
    16  	m1             protov3.FetchResponse
    17  	m2             protov3.FetchResponse
    18  	expectedResult protov3.FetchResponse
    19  	expectedError  merry.Error
    20  }
    21  
    22  var (
    23  	errMetadataMismatchFmt = "%v mismatch, got %v, expected %v"
    24  	errLengthMismatchFmt   = "length mismatch, got %v, expected %v"
    25  	errContentMismatchFmt  = "content mismatch at pos %v, got %v, expected %v"
    26  )
    27  
    28  func fetchResponseEquals(r1, r2 *protov3.FetchResponse) error {
    29  	if r1.StartTime != r2.StartTime {
    30  		return fmt.Errorf(errMetadataMismatchFmt, "StartTime", r1.StartTime, r2.StartTime)
    31  	}
    32  
    33  	if r1.StopTime != r2.StopTime {
    34  		return fmt.Errorf(errMetadataMismatchFmt, "StopTime", r1.StopTime, r2.StopTime)
    35  	}
    36  
    37  	if r1.XFilesFactor != r2.XFilesFactor {
    38  		return fmt.Errorf(errMetadataMismatchFmt, "XFilesFactor", r1.XFilesFactor, r2.XFilesFactor)
    39  	}
    40  
    41  	if r1.Name != r2.Name {
    42  		return fmt.Errorf(errMetadataMismatchFmt, "Name", r1.Name, r2.Name)
    43  	}
    44  
    45  	if r1.StepTime != r2.StepTime {
    46  		return fmt.Errorf(errMetadataMismatchFmt, "StepTime", r1.StepTime, r2.StepTime)
    47  	}
    48  
    49  	if r1.ConsolidationFunc != r2.ConsolidationFunc {
    50  		return fmt.Errorf(errMetadataMismatchFmt, "ConsolidationFunc", r1.ConsolidationFunc, r2.ConsolidationFunc)
    51  	}
    52  
    53  	if len(r1.Values) != len(r2.Values) {
    54  		return fmt.Errorf(errLengthMismatchFmt, r1.Values, r2.Values)
    55  	}
    56  
    57  	for i := range r1.Values {
    58  		if math.IsNaN(r1.Values[i]) && math.IsNaN(r2.Values[i]) {
    59  			continue
    60  		}
    61  		if r1.Values[i] != r2.Values[i] {
    62  			return fmt.Errorf(errContentMismatchFmt, i, r1.Values[i], r2.Values[i])
    63  		}
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  func TestMergeValues(t *testing.T) {
    70  	tests := []mergeValuesData{
    71  		{
    72  			name: "simple 1",
    73  			// 60 seconds
    74  			m1: protov3.FetchResponse{
    75  				Name:              "foo",
    76  				StartTime:         60,
    77  				StepTime:          60,
    78  				ConsolidationFunc: "average",
    79  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
    80  			},
    81  			// 120 seconds
    82  			m2: protov3.FetchResponse{
    83  				Name:              "foo",
    84  				StartTime:         0,
    85  				StepTime:          120,
    86  				ConsolidationFunc: "average",
    87  				Values:            []float64{1, 3, 5, 7, 9},
    88  			},
    89  
    90  			expectedResult: protov3.FetchResponse{
    91  				Name:              "foo",
    92  				StartTime:         60,
    93  				StepTime:          60,
    94  				ConsolidationFunc: "average",
    95  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
    96  			},
    97  
    98  			expectedError: nil,
    99  		},
   100  		{
   101  			name: "simple 2",
   102  			// 60 seconds
   103  			m1: protov3.FetchResponse{
   104  				Name:              "foo",
   105  				StartTime:         0,
   106  				StepTime:          120,
   107  				ConsolidationFunc: "average",
   108  				Values:            []float64{1, 3, 5, 7, 9},
   109  			},
   110  			// 120 seconds
   111  			m2: protov3.FetchResponse{
   112  				Name:              "foo",
   113  				StartTime:         60,
   114  				StepTime:          60,
   115  				ConsolidationFunc: "average",
   116  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
   117  			},
   118  
   119  			expectedResult: protov3.FetchResponse{
   120  				Name:              "foo",
   121  				StartTime:         60,
   122  				StepTime:          60,
   123  				ConsolidationFunc: "average",
   124  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
   125  			},
   126  
   127  			expectedError: nil,
   128  		},
   129  		{
   130  			name: "fill the gaps simple",
   131  			// 60 seconds
   132  			m1: protov3.FetchResponse{
   133  				Name:              "foo",
   134  				StartTime:         60,
   135  				StepTime:          60,
   136  				ConsolidationFunc: "average",
   137  				Values:            []float64{1, 2, 3, 4, math.NaN(), 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, math.NaN(), math.NaN(), math.NaN(), 20},
   138  			},
   139  			// 120 seconds
   140  			m2: protov3.FetchResponse{
   141  				Name:              "foo",
   142  				StartTime:         60,
   143  				StepTime:          60,
   144  				ConsolidationFunc: "average",
   145  				Values:            []float64{1, 2, math.NaN(), math.NaN(), 5, 6, 7, 8, 9, math.NaN(), 11, 12, math.NaN(), 14, 15, 16, 17, 18, math.NaN(), 20},
   146  			},
   147  
   148  			expectedResult: protov3.FetchResponse{
   149  				Name:              "foo",
   150  				StartTime:         60,
   151  				StepTime:          60,
   152  				ConsolidationFunc: "average",
   153  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, 17, 18, math.NaN(), 20},
   154  			},
   155  
   156  			expectedError: nil,
   157  		},
   158  		{
   159  			name: "fill end of metric",
   160  			// 60 seconds
   161  			m1: protov3.FetchResponse{
   162  				Name:              "foo",
   163  				StartTime:         60,
   164  				StepTime:          60,
   165  				ConsolidationFunc: "average",
   166  				Values:            []float64{1, 2, 3, 4, math.NaN(), 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, math.NaN(), math.NaN(), math.NaN()},
   167  			},
   168  			// 120 seconds
   169  			m2: protov3.FetchResponse{
   170  				Name:              "foo",
   171  				StartTime:         60,
   172  				StepTime:          60,
   173  				ConsolidationFunc: "average",
   174  				Values:            []float64{1, 2, math.NaN(), math.NaN(), 5, 6, 7, 8, 9, math.NaN(), 11, 12, math.NaN(), 14, 15, 16, 17, 18, math.NaN(), 20},
   175  			},
   176  
   177  			expectedResult: protov3.FetchResponse{
   178  				Name:              "foo",
   179  				StartTime:         60,
   180  				StepTime:          60,
   181  				ConsolidationFunc: "average",
   182  				Values:            []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, 17, 18, math.NaN(), 20},
   183  			},
   184  
   185  			expectedError: nil,
   186  		},
   187  	}
   188  
   189  	for _, test := range tests {
   190  		t.Run(test.name, func(t *testing.T) {
   191  			if test.m1.StopTime <= test.m1.StartTime {
   192  				test.m1.StopTime = test.m1.StartTime + int64(len(test.m1.Values))*test.m1.StepTime
   193  			}
   194  			if test.m2.StopTime <= test.m2.StartTime {
   195  				test.m2.StopTime = test.m2.StartTime + int64(len(test.m2.Values))*test.m2.StepTime
   196  			}
   197  			if test.expectedResult.StopTime <= test.expectedResult.StartTime {
   198  				test.expectedResult.StopTime = test.expectedResult.StartTime + int64(len(test.expectedResult.Values))*test.expectedResult.StepTime
   199  			}
   200  			err := types.MergeFetchResponses(&test.m1, &test.m2)
   201  			if !merry.Is(err, test.expectedError) {
   202  				t.Fatalf("unexpected error: '%v'", err)
   203  			}
   204  
   205  			err2 := fetchResponseEquals(&test.m1, &test.expectedResult)
   206  			if err2 != nil {
   207  				t.Fatalf("unexpected difference: '%v'\n    got     : %+v\n    expected: %+v\n", err, test.m1, test.expectedResult)
   208  			}
   209  		})
   210  	}
   211  }