github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/prometheus/response_test.go (about)

     1  // Copyright (c) 2020 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package prometheus
    22  
    23  import (
    24  	"encoding/json"
    25  	"fmt"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  var (
    33  	fullMatch = MatchInformation{FullMatch: true}
    34  	noMatch   = MatchInformation{NoMatch: true}
    35  )
    36  
    37  func TestUnmarshalPrometheusResponse(t *testing.T) {
    38  	tests := []struct {
    39  		name         string
    40  		givenJson    string
    41  		wantResponse Response
    42  	}{
    43  		{
    44  			name: "status: error",
    45  			givenJson: `{
    46  				"status": "error",
    47  				"errorType": "bad_data",
    48  				"error": "invalid parameter"
    49  			}`,
    50  			wantResponse: Response{
    51  				Status: "error",
    52  			},
    53  		},
    54  		{
    55  			name: "resultType: scalar",
    56  			givenJson: `{
    57  				"status": "success",
    58  				"data": {
    59  					"resultType": "scalar",
    60  					"result": [1590605774, "84"]
    61  				}
    62  			}`,
    63  			wantResponse: Response{
    64  				"success",
    65  				data{
    66  					"scalar",
    67  					&ScalarResult{Value{1590605774.0, "84"}},
    68  				},
    69  			},
    70  		},
    71  		{
    72  			name: "resultType: string",
    73  			givenJson: `{
    74  				"status": "success",
    75  				"data": {
    76  					"resultType": "string",
    77  					"result": [1590605775, "FOO"]
    78  				}
    79  			}`,
    80  			wantResponse: Response{
    81  				"success",
    82  				data{
    83  					"string",
    84  					&StringResult{Value{1590605775.0, "FOO"}},
    85  				},
    86  			},
    87  		},
    88  		{
    89  			name: "resultType: vector",
    90  			givenJson: `{
    91  				"status": "success",
    92  				"data": {
    93  					"resultType": "vector",
    94  					"result": [
    95  						{
    96  							"metric": {
    97  								"__name__": "foo",
    98  								"bar": "1"
    99  							},
   100  							"value": [1590605775, "0.5"]
   101  						},
   102  						{
   103  							"metric": {
   104  								"__name__": "foo",
   105  								"bar": "2"
   106  							},
   107  							"value": [1590605776, "2"]
   108  						}
   109  					]
   110  				}
   111  			}`,
   112  			wantResponse: Response{
   113  				"success",
   114  				data{
   115  					"vector",
   116  					&VectorResult{[]vectorItem{
   117  						{
   118  							Metric: Tags{"__name__": "foo", "bar": "1"},
   119  							Value:  Value{1590605775.0, "0.5"},
   120  						},
   121  						{
   122  							Metric: Tags{"__name__": "foo", "bar": "2"},
   123  							Value:  Value{1590605776.0, "2"},
   124  						},
   125  					}},
   126  				},
   127  			},
   128  		},
   129  		{
   130  			name: "resultType: matrix",
   131  			givenJson: `{
   132  				"status": "success",
   133  				"data": {
   134  					"resultType": "matrix",
   135  					"result": [
   136  						{
   137  							"metric": {
   138  								"__name__": "foo",
   139  								"bar": "1"
   140  							},
   141  							"values": [[1590605775, "1"], [1590605785, "11"]]
   142  						},
   143  						{
   144  							"metric": {
   145  								"__name__": "foo",
   146  								"bar": "2"
   147  							},
   148  							"values": [[1590605776, "2"], [1590605786, "22"]]
   149  						}
   150  					]
   151  				}
   152  			}`,
   153  			wantResponse: Response{
   154  				"success",
   155  				data{
   156  					"matrix",
   157  					&MatrixResult{[]matrixRow{
   158  						{
   159  							Metric: Tags{"__name__": "foo", "bar": "1"},
   160  							Values: Values{{1590605775.0, "1"}, {1590605785.0, "11"}},
   161  						},
   162  						{
   163  							Metric: Tags{"__name__": "foo", "bar": "2"},
   164  							Values: Values{{1590605776.0, "2"}, {1590605786.0, "22"}},
   165  						},
   166  					}},
   167  				},
   168  			},
   169  		},
   170  	}
   171  
   172  	for _, tt := range tests {
   173  		t.Run(tt.name, func(t *testing.T) {
   174  			response := &Response{}
   175  			err := json.Unmarshal([]byte(tt.givenJson), response)
   176  			require.NoError(t, err)
   177  			assert.Equal(t, tt.wantResponse, *response)
   178  		})
   179  	}
   180  }
   181  
   182  func TestResponseMatching(t *testing.T) {
   183  	tests := []struct {
   184  		name     string
   185  		response Response
   186  	}{
   187  		{
   188  			name: "error",
   189  			response: Response{
   190  				Status: "error",
   191  			},
   192  		},
   193  
   194  		{
   195  			name: "scalar",
   196  			response: Response{
   197  				"success",
   198  				data{
   199  					"scalar",
   200  					&ScalarResult{Value{1590605774.0, "1"}},
   201  				},
   202  			},
   203  		},
   204  		{
   205  			name: "scalar other timestamp",
   206  			response: Response{
   207  				"success",
   208  				data{
   209  					"scalar",
   210  					&ScalarResult{Value{1590605775.0, "1"}},
   211  				},
   212  			},
   213  		},
   214  		{
   215  			name: "scalar other value",
   216  			response: Response{
   217  				"success",
   218  				data{
   219  					"scalar",
   220  					&ScalarResult{Value{1590605774.0, "2"}},
   221  				},
   222  			},
   223  		},
   224  
   225  		{
   226  			name: "vector",
   227  			response: Response{
   228  				"success",
   229  				data{
   230  					"vector",
   231  					&VectorResult{[]vectorItem{
   232  						{
   233  							Metric: Tags{"__name__": "foo"},
   234  							Value:  Value{1590605775.0, "0.5"},
   235  						},
   236  					}},
   237  				},
   238  			},
   239  		},
   240  		{
   241  			name: "vector more tags",
   242  			response: Response{
   243  				"success",
   244  				data{
   245  					"vector",
   246  					&VectorResult{[]vectorItem{
   247  						{
   248  							Metric: Tags{"__name__": "foo", "bar": "1"},
   249  							Value:  Value{1590605775.0, "0.5"},
   250  						},
   251  					}},
   252  				},
   253  			},
   254  		},
   255  		{
   256  			name: "vector more items",
   257  			response: Response{
   258  				"success",
   259  				data{
   260  					"vector",
   261  					&VectorResult{[]vectorItem{
   262  						{
   263  							Metric: Tags{"__name__": "foo", "bar": "1"},
   264  							Value:  Value{1590605775.0, "0.5"},
   265  						},
   266  						{
   267  							Metric: Tags{"__name__": "foo", "bar": "2"},
   268  							Value:  Value{1590605775.0, "0.5"},
   269  						},
   270  					}},
   271  				},
   272  			},
   273  		},
   274  		{
   275  			name: "vector different tag",
   276  			response: Response{
   277  				"success",
   278  				data{
   279  					"vector",
   280  					&VectorResult{[]vectorItem{
   281  						{
   282  							Metric: Tags{"__name__": "bar"},
   283  							Value:  Value{1590605775.0, "0.5"},
   284  						},
   285  					}},
   286  				},
   287  			},
   288  		},
   289  		{
   290  			name: "vector different value",
   291  			response: Response{
   292  				"success",
   293  				data{
   294  					"vector",
   295  					&VectorResult{[]vectorItem{
   296  						{
   297  							Metric: Tags{"__name__": "foo"},
   298  							Value:  Value{1590605775.0, "1"},
   299  						},
   300  					}},
   301  				},
   302  			},
   303  		},
   304  		{
   305  			name: "vector different timestamp",
   306  			response: Response{
   307  				"success",
   308  				data{
   309  					"vector",
   310  					&VectorResult{[]vectorItem{
   311  						{
   312  							Metric: Tags{"__name__": "foo"},
   313  							Value:  Value{1590605774.0, "0.5"},
   314  						},
   315  					}},
   316  				},
   317  			},
   318  		},
   319  
   320  		{
   321  			name: "matrix",
   322  			response: Response{
   323  				"success",
   324  				data{
   325  					"matrix",
   326  					&MatrixResult{[]matrixRow{
   327  						{
   328  							Metric: Tags{"__name__": "foo"},
   329  							Values: Values{{1590605775.0, "1"}},
   330  						},
   331  					}},
   332  				},
   333  			},
   334  		},
   335  		{
   336  			name: "matrix other tag",
   337  			response: Response{
   338  				"success",
   339  				data{
   340  					"matrix",
   341  					&MatrixResult{[]matrixRow{
   342  						{
   343  							Metric: Tags{"__name__": "bar"},
   344  							Values: Values{{1590605775.0, "1"}},
   345  						},
   346  					}},
   347  				},
   348  			},
   349  		},
   350  		{
   351  			name: "matrix other value",
   352  			response: Response{
   353  				"success",
   354  				data{
   355  					"matrix",
   356  					&MatrixResult{[]matrixRow{
   357  						{
   358  							Metric: Tags{"__name__": "foo"},
   359  							Values: Values{{1590605775.0, "2"}},
   360  						},
   361  					}},
   362  				},
   363  			},
   364  		},
   365  		{
   366  			name: "matrix other timestamp",
   367  			response: Response{
   368  				"success",
   369  				data{
   370  					"matrix",
   371  					&MatrixResult{[]matrixRow{
   372  						{
   373  							Metric: Tags{"__name__": "foo"},
   374  							Values: Values{{1590605776.0, "1"}},
   375  						},
   376  					}},
   377  				},
   378  			},
   379  		},
   380  		{
   381  			name: "matrix more tags",
   382  			response: Response{
   383  				"success",
   384  				data{
   385  					"matrix",
   386  					&MatrixResult{[]matrixRow{
   387  						{
   388  							Metric: Tags{"__name__": "foo", "bar": "1"},
   389  							Values: Values{{1590605775.0, "1"}},
   390  						},
   391  					}},
   392  				},
   393  			},
   394  		},
   395  		{
   396  			name: "matrix more values",
   397  			response: Response{
   398  				"success",
   399  				data{
   400  					"matrix",
   401  					&MatrixResult{[]matrixRow{
   402  						{
   403  							Metric: Tags{"__name__": "foo"},
   404  							Values: Values{{1590605775.0, "1"}, {1590605776.0, "2"}},
   405  						},
   406  					}},
   407  				},
   408  			},
   409  		},
   410  		{
   411  			name: "matrix more rows",
   412  			response: Response{
   413  				"success",
   414  				data{
   415  					"matrix",
   416  					&MatrixResult{[]matrixRow{
   417  						{
   418  							Metric: Tags{"__name__": "foo"},
   419  							Values: Values{{1590605775.0, "1"}},
   420  						},
   421  						{
   422  							Metric: Tags{"__name__": "bar"},
   423  							Values: Values{{1590605775.0, "1"}},
   424  						},
   425  					}},
   426  				},
   427  			},
   428  		},
   429  	}
   430  
   431  	for i, ti := range tests {
   432  		for j, tj := range tests {
   433  			t.Run(fmt.Sprintf("%s vs %s", ti.name, tj.name), func(t *testing.T) {
   434  				matchResult, err := ti.response.Matches(tj.response)
   435  				if i == j { // should match
   436  					require.NoError(t, err)
   437  					assert.Equal(t, fullMatch, matchResult)
   438  				} else { // should not match
   439  					require.Error(t, err)
   440  					assert.Equal(t, noMatch, matchResult)
   441  				}
   442  			})
   443  		}
   444  	}
   445  }
   446  
   447  func TestResponseMatchingOrderInsensitive(t *testing.T) {
   448  	tests := []struct {
   449  		name  string
   450  		left  Response
   451  		right Response
   452  	}{
   453  		{
   454  			name: "vector",
   455  			left: Response{
   456  				"success",
   457  				data{
   458  					"vector",
   459  					&VectorResult{[]vectorItem{
   460  						{
   461  							Metric: Tags{"__name__": "first"},
   462  							Value:  Value{1590605775.0, "1"},
   463  						},
   464  						{
   465  							Metric: Tags{"__name__": "second"},
   466  							Value:  Value{1590605775.0, "2"},
   467  						},
   468  					}},
   469  				},
   470  			},
   471  			right: Response{
   472  				"success",
   473  				data{
   474  					"vector",
   475  					&VectorResult{[]vectorItem{
   476  						{
   477  							Metric: Tags{"__name__": "second"},
   478  							Value:  Value{1590605775.0, "2"},
   479  						},
   480  						{
   481  							Metric: Tags{"__name__": "first"},
   482  							Value:  Value{1590605775.0, "1"},
   483  						},
   484  					}},
   485  				},
   486  			},
   487  		},
   488  		{
   489  			name: "matrix",
   490  			left: Response{
   491  				"success",
   492  				data{
   493  					"matrix",
   494  					&MatrixResult{[]matrixRow{
   495  						{
   496  							Metric: Tags{"__name__": "first"},
   497  							Values: Values{{1590605775.0, "1"}},
   498  						},
   499  						{
   500  							Metric: Tags{"__name__": "second"},
   501  							Values: Values{{1590605775.0, "2"}},
   502  						},
   503  					}},
   504  				},
   505  			},
   506  			right: Response{
   507  				"success",
   508  				data{
   509  					"matrix",
   510  					&MatrixResult{[]matrixRow{
   511  						{
   512  							Metric: Tags{"__name__": "second"},
   513  							Values: Values{{1590605775.0, "2"}},
   514  						},
   515  						{
   516  							Metric: Tags{"__name__": "first"},
   517  							Values: Values{{1590605775.0, "1"}},
   518  						},
   519  					}},
   520  				},
   521  			},
   522  		},
   523  	}
   524  
   525  	for _, tt := range tests {
   526  		t.Run(fmt.Sprintf(tt.name), func(t *testing.T) {
   527  			matchResult, err := tt.left.Matches(tt.right)
   528  			require.NoError(t, err)
   529  			assert.Equal(t, fullMatch, matchResult)
   530  		})
   531  	}
   532  }