github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/api/query_handler_test.go (about) 1 // Copyright (c) 2017-2018 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package api 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "net/http/httptest" 23 24 "github.com/uber/aresdb/memstore" 25 memMocks "github.com/uber/aresdb/memstore/mocks" 26 metaCom "github.com/uber/aresdb/metastore/common" 27 28 "encoding/json" 29 "github.com/gorilla/mux" 30 "github.com/onsi/ginkgo" 31 . "github.com/onsi/gomega" 32 "github.com/pkg/errors" 33 "github.com/uber/aresdb/common" 34 "github.com/uber/aresdb/query" 35 ) 36 37 var _ = ginkgo.Describe("QueryHandler", func() { 38 var testServer *httptest.Server 39 var testSchema = memstore.NewTableSchema(&metaCom.Table{ 40 Name: "trips", 41 IsFactTable: false, 42 Columns: []metaCom.Column{ 43 { 44 Name: "request_at", 45 Type: "Uint32", 46 }, 47 { 48 Name: "fare_total", 49 Type: "Flaot", 50 }, 51 { 52 Name: "city_id", 53 Type: "Uint16", 54 }, 55 { 56 Name: "status", 57 Type: "SmallEnum", 58 }, 59 }, 60 Config: metaCom.TableConfig{ 61 BatchSize: 10, 62 }, 63 }) 64 65 var memStore *memMocks.MemStore 66 ginkgo.BeforeEach(func() { 67 memStore = CreateMemStore(testSchema, 0, nil, CreateMockDiskStore()) 68 queryHandler := NewQueryHandler(memStore, common.QueryConfig{ 69 DeviceMemoryUtilization: 1.0, 70 }) 71 testRouter := mux.NewRouter() 72 testRouter.HandleFunc("/aql", queryHandler.HandleAQL).Methods(http.MethodGet, http.MethodPost) 73 testServer = httptest.NewUnstartedServer(WithPanicHandling(testRouter)) 74 testServer.Start() 75 }) 76 77 ginkgo.AfterEach(func() { 78 testServer.Close() 79 }) 80 81 ginkgo.It("HandleAQL should succeed on valid requests", func() { 82 hostPort := testServer.Listener.Addr().String() 83 // Invalid timeBucketizer days. 84 query := ` 85 { 86 "queries": [ 87 { 88 "measures": [ 89 { 90 "sqlExpression": "count(*)" 91 } 92 ], 93 "rowFilters": [ 94 "trips.status!='ACTIVE'" 95 ], 96 "table": "trips", 97 "timeFilter": { 98 "column": "trips.request_at", 99 "from": "-6d" 100 }, 101 "dimensions": [ 102 { 103 "sqlExpression": "trips.request_at", 104 "timeBucketizer": "day", 105 "timeUnit": "second" 106 } 107 ] 108 } 109 ] 110 } 111 ` 112 resp, err := http.Post(fmt.Sprintf("http://%s/aql", hostPort), "application/json", bytes.NewBuffer([]byte(query))) 113 Ω(err).Should(BeNil()) 114 bs, err := ioutil.ReadAll(resp.Body) 115 Ω(err).Should(BeNil()) 116 Ω(string(bs)).Should(MatchJSON(`{ 117 "results": [ 118 {} 119 ] 120 }`)) 121 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 122 }) 123 124 ginkgo.It("HandleAQL should fail on request that cannot be unmarshaled", func() { 125 hostPort := testServer.Listener.Addr().String() 126 resp, err := http.Post(fmt.Sprintf("http://%s/aql", hostPort), "application/json", bytes.NewBuffer([]byte{})) 127 Ω(err).Should(BeNil()) 128 bs, err := ioutil.ReadAll(resp.Body) 129 Ω(err).Should(BeNil()) 130 Ω(string(bs)).Should(MatchJSON(` 131 {"message":"Bad request: failed to unmarshal request body","cause":{"Offset":0}} 132 `)) 133 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 134 }) 135 136 ginkgo.It("HandleAQL should succeed on empty query requests", func() { 137 hostPort := testServer.Listener.Addr().String() 138 query := ` 139 { 140 "queries": [ 141 ] 142 } 143 ` 144 resp, err := http.Post(fmt.Sprintf("http://%s/aql", hostPort), "application/json", bytes.NewBuffer([]byte(query))) 145 Ω(err).Should(BeNil()) 146 bs, err := ioutil.ReadAll(resp.Body) 147 Ω(err).Should(BeNil()) 148 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 149 Ω(string(bs)).Should(MatchJSON(` 150 { 151 "results": [] 152 } 153 `)) 154 }) 155 156 ginkgo.It("HandleAQL should fail requests without queries", func() { 157 hostPort := testServer.Listener.Addr().String() 158 query := ` 159 {"q":{}} 160 ` 161 resp, err := http.Post(fmt.Sprintf("http://%s/aql", hostPort), "application/json", bytes.NewBuffer([]byte(query))) 162 Ω(err).Should(BeNil()) 163 bs, err := ioutil.ReadAll(resp.Body) 164 Ω(err).Should(BeNil()) 165 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 166 Ω(string(bs)).Should(ContainSubstring("Bad request: missing/invalid parameter")) 167 }) 168 169 ginkgo.It("HandleAQL should work for queries overwritten by parameter q", func() { 170 hostPort := testServer.Listener.Addr().String() 171 query := "{}" 172 resp, err := http.Post(fmt.Sprintf("http://%s/aql?q={\"queries\":[]}", hostPort), "application/json", bytes.NewBuffer([]byte(query))) 173 Ω(err).Should(BeNil()) 174 _, err = ioutil.ReadAll(resp.Body) 175 Ω(err).Should(BeNil()) 176 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 177 }) 178 179 ginkgo.It("ReportError should work", func() { 180 rw := NewHLLQueryResponseWriter() 181 Ω(rw.GetStatusCode()).Should(Equal(http.StatusOK)) 182 rw.ReportError(0, "test", errors.New("test err"), http.StatusBadRequest) 183 Ω(rw.GetStatusCode()).Should(Equal(http.StatusBadRequest)) 184 185 hllRW := rw.(*HLLQueryResponseWriter) 186 Ω(hllRW.response.GetBytes()).Should(Equal([]byte{2, 1, 237, 172, 0, 0, 0, 0, 8, 0, 0, 0, 187 1, 0, 0, 0, 116, 101, 115, 116, 32, 101, 114, 114, 0, 0, 0, 0, 0, 0, 0, 0})) 188 }) 189 190 ginkgo.It("ReportQueryContext should work", func() { 191 rw := NewHLLQueryResponseWriter() 192 Ω(func() { rw.ReportQueryContext(nil) }).ShouldNot(Panic()) 193 }) 194 195 ginkgo.It("ReportResult should work", func() { 196 rw := NewHLLQueryResponseWriter() 197 rw.ReportResult(0, &query.AQLQueryContext{HLLQueryResult: []byte{0, 0, 0, 0, 0, 0, 0, 0}}) 198 199 hllRW := rw.(*HLLQueryResponseWriter) 200 Ω(hllRW.response.GetBytes()).Should(Equal([]byte{2, 1, 237, 172, 0, 0, 0, 0, 8, 0, 0, 201 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})) 202 }) 203 204 ginkgo.It("Report hll result in JsonResponseWriter should work", func() { 205 q := &query.AQLQuery{ 206 Table: "trips", 207 } 208 209 oopk := query.OOPKContext{ 210 // C.AGGR_HLL 211 AggregateType: 10, 212 } 213 214 data, err := ioutil.ReadFile("../testing/data/query/hll") 215 Ω(err).Should(BeNil()) 216 rw := NewJSONQueryResponseWriter(2) 217 rw.ReportResult(0, &query.AQLQueryContext{ 218 Query: q, 219 OOPK: oopk, 220 HLLQueryResult: []byte{0, 0, 0, 0, 0, 0, 0, 0}, 221 }) 222 rw.ReportResult(1, &query.AQLQueryContext{ 223 Query: q, 224 OOPK: oopk, 225 HLLQueryResult: data, 226 }) 227 228 Ω(rw.(*JSONQueryResponseWriter).response.Results).Should(HaveLen(2)) 229 resultJson, err := json.Marshal(rw.(*JSONQueryResponseWriter).response.Results) 230 Ω(resultJson).Should(MatchJSON(` 231 [ 232 null, 233 { 234 "1": { 235 "c": { 236 "2": 2 237 } 238 }, 239 "4294967295": { 240 "d": { 241 "514": 4 242 } 243 }, 244 "NULL": { 245 "NULL": { 246 "NULL": 3 247 } 248 } 249 } 250 ] 251 `)) 252 Ω(rw.(*JSONQueryResponseWriter).response.Errors).Should(HaveLen(2)) 253 Ω(rw.(*JSONQueryResponseWriter).response.Errors[0]).ShouldNot(BeNil()) 254 Ω(rw.(*JSONQueryResponseWriter).response.Errors[1]).Should(BeNil()) 255 }) 256 257 ginkgo.It("Verbose should work", func() { 258 hostPort := testServer.Listener.Addr().String() 259 query := ` 260 { 261 "queries": [ 262 { 263 "measures": [ 264 { 265 "sqlExpression": "count(*)" 266 } 267 ], 268 "rowFilters": [ 269 "trips.status!='ACTIVE'" 270 ], 271 "table": "trips", 272 "timeFilter": { 273 "column": "trips.request_at", 274 "from": "-6d" 275 }, 276 "dimensions": [ 277 { 278 "sqlExpression": "trips.request_at", 279 "timeBucketizer": "day", 280 "timeUnit": "second" 281 } 282 ] 283 } 284 ] 285 } 286 ` 287 resp, err := http.Post(fmt.Sprintf("http://%s/aql?verbose=1", hostPort), "application/json", bytes.NewBuffer([]byte(query))) 288 Ω(err).Should(BeNil()) 289 bs, err := ioutil.ReadAll(resp.Body) 290 Ω(err).Should(BeNil()) 291 Ω(string(bs)).Should(ContainSubstring("FLOOR")) 292 Ω(string(bs)).Should(ContainSubstring("Unsigned")) 293 Ω(string(bs)).Should(ContainSubstring("allBatches")) 294 }) 295 })