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 }