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

     1  // Copyright (c) 2019 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 native
    22  
    23  import (
    24  	"encoding/json"
    25  	"fmt"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"net/url"
    29  	"strconv"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/m3db/m3/src/query/block"
    34  	"github.com/m3db/m3/src/query/models"
    35  	"github.com/m3db/m3/src/query/test"
    36  	"github.com/m3db/m3/src/x/headers"
    37  	xjson "github.com/m3db/m3/src/x/json"
    38  	xtest "github.com/m3db/m3/src/x/test"
    39  
    40  	"github.com/stretchr/testify/assert"
    41  	"github.com/stretchr/testify/require"
    42  )
    43  
    44  type vectorResult struct {
    45  	Data struct {
    46  		Result []struct {
    47  			Metric map[string]string  `json:"metric"`
    48  			Value  vectorResultValues `json:"value"`
    49  		} `json:"result"`
    50  	} `json:"data"`
    51  }
    52  
    53  type vectorResultValues []interface{}
    54  
    55  func (v vectorResultValues) parse() (time.Time, int, error) {
    56  	if len(v) != 2 {
    57  		return time.Time{}, 0,
    58  			fmt.Errorf("expected length 2: actual=%d", len(v))
    59  	}
    60  
    61  	t, ok := v[0].(float64)
    62  	if !ok {
    63  		return time.Time{}, 0,
    64  			fmt.Errorf("could not unmarshal time: %v", v[0])
    65  	}
    66  
    67  	str, ok := v[1].(string)
    68  	if !ok {
    69  		return time.Time{}, 0,
    70  			fmt.Errorf("could not unmarshal value: %v", v[1])
    71  	}
    72  
    73  	n, err := strconv.Atoi(str)
    74  	if err != nil {
    75  		return time.Time{}, 0,
    76  			fmt.Errorf("could not convert value to number: err=%v", err)
    77  	}
    78  
    79  	return time.Unix(int64(t), 0), n, nil
    80  }
    81  
    82  func TestPromReadInstantHandler(t *testing.T) {
    83  	testPromReadInstantHandler(t, block.NewResultMetadata(), "", "")
    84  	testPromReadInstantHandler(t, buildWarningMeta("foo", "bar"), "foo_bar", "foo_bar")
    85  	testPromReadInstantHandler(t, block.ResultMetadata{Exhaustive: false},
    86  		headers.LimitHeaderSeriesLimitApplied,
    87  		"m3db exceeded query limit: results not exhaustive")
    88  }
    89  
    90  func testPromReadInstantHandler(
    91  	t *testing.T,
    92  	resultMeta block.ResultMetadata,
    93  	ex string,
    94  	jsonWarning string,
    95  ) {
    96  	values, bounds := test.GenerateValuesAndBounds(nil, nil)
    97  
    98  	setup := newTestSetup(t, nil)
    99  	promReadInstant := setup.Handlers.instantRead
   100  
   101  	seriesMeta := test.NewSeriesMeta("dummy", len(values))
   102  	meta := block.Metadata{
   103  		Bounds:         bounds,
   104  		Tags:           models.NewTags(0, models.NewTagOptions()),
   105  		ResultMetadata: resultMeta,
   106  	}
   107  
   108  	b := test.NewBlockFromValuesWithMetaAndSeriesMeta(meta, seriesMeta, values)
   109  	test.NewBlockFromValues(bounds, values)
   110  	setup.Storage.SetFetchBlocksResult(block.Result{Blocks: []block.Block{b}}, nil)
   111  
   112  	req := httptest.NewRequest(PromReadInstantHTTPMethods[0], PromReadInstantURL, nil)
   113  
   114  	params := url.Values{}
   115  	params.Set(QueryParam, "dummy0{}")
   116  
   117  	req.URL.RawQuery = params.Encode()
   118  
   119  	recorder := httptest.NewRecorder()
   120  	promReadInstant.ServeHTTP(recorder, req)
   121  
   122  	require.Equal(t, http.StatusOK, recorder.Result().StatusCode)
   123  
   124  	header := recorder.Header().Get(headers.LimitHeader)
   125  	assert.Equal(t, ex, header)
   126  
   127  	var result vectorResult
   128  	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &result))
   129  	require.Equal(t, 2, len(result.Data.Result))
   130  
   131  	at0, value0, err := result.Data.Result[0].Value.parse()
   132  	require.NoError(t, err)
   133  	at1, value1, err := result.Data.Result[1].Value.parse()
   134  	require.NoError(t, err)
   135  
   136  	expectedResp := xjson.Map{
   137  		"status": "success",
   138  		"data": xjson.Map{
   139  			"resultType": "vector",
   140  			"result": xjson.Array{
   141  				xjson.Map{
   142  					"metric": xjson.Map{
   143  						"__name__": "dummy0",
   144  						"dummy0":   "dummy0",
   145  					},
   146  					"value": xjson.Array{
   147  						at0.Unix(),
   148  						strconv.Itoa(value0),
   149  					},
   150  				},
   151  				xjson.Map{
   152  					"metric": xjson.Map{
   153  						"__name__": "dummy1",
   154  						"dummy1":   "dummy1",
   155  					},
   156  					"value": xjson.Array{
   157  						at1.Unix(),
   158  						strconv.Itoa(value1),
   159  					},
   160  				},
   161  			},
   162  		},
   163  	}
   164  
   165  	if len(jsonWarning) != 0 {
   166  		expectedResp["warnings"] = xjson.Array{jsonWarning}
   167  	}
   168  
   169  	expected := xtest.MustPrettyJSONMap(t, expectedResp)
   170  	actual := xtest.MustPrettyJSONString(t, recorder.Body.String())
   171  	assert.Equal(t, expected, actual, xtest.Diff(expected, actual))
   172  }
   173  
   174  func TestPromReadInstantHandlerStorageError(t *testing.T) {
   175  	setup := newTestSetup(t, nil)
   176  	promReadInstant := setup.Handlers.instantRead
   177  
   178  	storageErr := fmt.Errorf("storage err")
   179  	setup.Storage.SetFetchBlocksResult(block.Result{}, storageErr)
   180  
   181  	req := httptest.NewRequest(PromReadInstantHTTPMethods[0], PromReadInstantURL, nil)
   182  
   183  	params := url.Values{}
   184  	params.Set(QueryParam, "dummy0{}")
   185  
   186  	req.URL.RawQuery = params.Encode()
   187  
   188  	recorder := httptest.NewRecorder()
   189  	promReadInstant.ServeHTTP(recorder, req)
   190  
   191  	require.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
   192  
   193  	var errResp struct {
   194  		Error string `json:"error"`
   195  	}
   196  	resp := recorder.Body.Bytes()
   197  	require.NoError(t, json.Unmarshal(resp, &errResp))
   198  	require.Equal(t, storageErr.Error(), errResp.Error)
   199  }