| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package memory | 5 package memory |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "math" | |
| 10 "testing" | 9 "testing" |
| 11 | 10 |
| 12 » dsS "github.com/luci/gae/service/datastore" | 11 » dstore "github.com/luci/gae/service/datastore" |
| 13 "github.com/luci/gae/service/datastore/serialize" | 12 "github.com/luci/gae/service/datastore/serialize" |
| 14 "github.com/luci/luci-go/common/cmpbin" | 13 "github.com/luci/luci-go/common/cmpbin" |
| 14 "github.com/luci/luci-go/common/stringset" |
| 15 . "github.com/luci/luci-go/common/testing/assertions" | 15 . "github.com/luci/luci-go/common/testing/assertions" |
| 16 . "github.com/smartystreets/goconvey/convey" | 16 . "github.com/smartystreets/goconvey/convey" |
| 17 "golang.org/x/net/context" | |
| 18 ) | 17 ) |
| 19 | 18 |
| 20 const ( | |
| 21 MaxUint = ^uint(0) | |
| 22 MaxInt = int(MaxUint >> 1) | |
| 23 IntIs32Bits = int64(MaxInt) < math.MaxInt64 | |
| 24 ) | |
| 25 | |
| 26 func TestDatastoreQueries(t *testing.T) { | |
| 27 Convey("Datastore Query suport", t, func() { | |
| 28 c := Use(context.Background()) | |
| 29 ds := dsS.Get(c) | |
| 30 So(ds, ShouldNotBeNil) | |
| 31 | |
| 32 Convey("can create good queries", func() { | |
| 33 q := ds.NewQuery("Foo").Filter("farnsworth >", 20).KeysO
nly().Limit(10).Offset(39) | |
| 34 | |
| 35 // normally you can only get cursors from inside of the
memory | |
| 36 // implementation, so this construction is just for test
ing. | |
| 37 start := queryCursor(bjoin( | |
| 38 mkNum(2), | |
| 39 serialize.ToBytes(dsS.IndexColumn{Property: "far
nsworth"}), | |
| 40 serialize.ToBytes(dsS.IndexColumn{Property: "__k
ey__"}), | |
| 41 serialize.ToBytes(prop(200)), | |
| 42 serialize.ToBytes(prop(ds.NewKey("Foo", "id", 0,
nil))))) | |
| 43 | |
| 44 So(start.String(), ShouldEqual, | |
| 45 `gYAAZzFdTeeb3d9zOxsAAF-v221Xy32_AIGHyIgAAUc32-A
FabMAAA==`) | |
| 46 | |
| 47 end := queryCursor(bjoin( | |
| 48 mkNum(2), | |
| 49 serialize.ToBytes(dsS.IndexColumn{Property: "far
nsworth"}), | |
| 50 serialize.ToBytes(dsS.IndexColumn{Property: "__k
ey__"}), | |
| 51 serialize.ToBytes(prop(3000)), | |
| 52 serialize.ToBytes(prop(ds.NewKey("Foo", "zeta",
0, nil))))) | |
| 53 | |
| 54 q = q.Start(start).End(end) | |
| 55 So(q, ShouldNotBeNil) | |
| 56 So(q.(*queryImpl).err, ShouldBeNil) | |
| 57 rq, err := q.(*queryImpl).reduce("", false) | |
| 58 So(rq, ShouldNotBeNil) | |
| 59 So(err, ShouldBeNil) | |
| 60 }) | |
| 61 | |
| 62 Convey("ensures orders make sense", func() { | |
| 63 q := ds.NewQuery("Cool") | |
| 64 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") | |
| 65 | |
| 66 Convey("removes dups and equality orders", func() { | |
| 67 q = q.Order("wat") | |
| 68 qi := q.(*queryImpl) | |
| 69 So(qi.err, ShouldBeNil) | |
| 70 rq, err := qi.reduce("", false) | |
| 71 So(err, ShouldBeNil) | |
| 72 So(rq.suffixFormat, ShouldResemble, []dsS.IndexC
olumn{ | |
| 73 {Property: "wat"}, {Property: "__key__"}
}) | |
| 74 }) | |
| 75 | |
| 76 Convey("if we equality-filter on __key__, that's just si
lly", func() { | |
| 77 q = q.Order("wat").Filter("__key__ =", ds.NewKey
("Foo", "wat", 0, nil)) | |
| 78 _, err := q.(*queryImpl).reduce("", false) | |
| 79 So(err, ShouldErrLike, "query equality filter on
__key__ is silly") | |
| 80 }) | |
| 81 | |
| 82 }) | |
| 83 | |
| 84 }) | |
| 85 } | |
| 86 | |
| 87 type queryTest struct { | |
| 88 // name is the name of the test case | |
| 89 name string | |
| 90 | |
| 91 // q is the input query | |
| 92 q dsS.Query | |
| 93 | |
| 94 // err is the error to expect after prepping the query (error, string or
nil) | |
| 95 err interface{} | |
| 96 | |
| 97 // equivalentQuery is another query which ShouldResemble q. This is usef
ul to | |
| 98 // see the effects of redundancy pruning on e.g. filters. | |
| 99 equivalentQuery dsS.Query | |
| 100 } | |
| 101 | |
| 102 type sillyCursor string | 19 type sillyCursor string |
| 103 | 20 |
| 104 func (s sillyCursor) String() string { return string(s) } | 21 func (s sillyCursor) String() string { return string(s) } |
| 105 | 22 |
| 106 func curs(pairs ...interface{}) queryCursor { | 23 func curs(pairs ...interface{}) queryCursor { |
| 107 if len(pairs)%2 != 0 { | 24 if len(pairs)%2 != 0 { |
| 108 panic("curs() takes only even pairs") | 25 panic("curs() takes only even pairs") |
| 109 } | 26 } |
| 110 pre := &bytes.Buffer{} | 27 pre := &bytes.Buffer{} |
| 111 cmpbin.WriteUint(pre, uint64(len(pairs)/2)) | 28 cmpbin.WriteUint(pre, uint64(len(pairs)/2)) |
| 112 post := serialize.Invertible(&bytes.Buffer{}) | 29 post := serialize.Invertible(&bytes.Buffer{}) |
| 113 for i := 0; i < len(pairs); i += 2 { | 30 for i := 0; i < len(pairs); i += 2 { |
| 114 k, v := pairs[i].(string), pairs[i+1] | 31 k, v := pairs[i].(string), pairs[i+1] |
| 115 | 32 |
| 116 » » col := dsS.IndexColumn{Property: k} | 33 » » col := dstore.IndexColumn{Property: k} |
| 117 | 34 |
| 118 post.SetInvert(false) | 35 post.SetInvert(false) |
| 119 if k[0] == '-' { | 36 if k[0] == '-' { |
| 120 post.SetInvert(false) | 37 post.SetInvert(false) |
| 121 col.Property = k[1:] | 38 col.Property = k[1:] |
| 122 » » » col.Direction = dsS.DESCENDING | 39 » » » col.Direction = dstore.DESCENDING |
| 123 } | 40 } |
| 124 serialize.WriteIndexColumn(pre, col) | 41 serialize.WriteIndexColumn(pre, col) |
| 125 serialize.WriteProperty(post, serialize.WithoutContext, prop(v)) | 42 serialize.WriteProperty(post, serialize.WithoutContext, prop(v)) |
| 126 } | 43 } |
| 127 return queryCursor(bjoin(pre.Bytes(), post.Bytes())) | 44 return queryCursor(bjoin(pre.Bytes(), post.Bytes())) |
| 128 } | 45 } |
| 129 | 46 |
| 47 type queryTest struct { |
| 48 // name is the name of the test case |
| 49 name string |
| 50 |
| 51 // q is the input query |
| 52 q *dstore.Query |
| 53 |
| 54 // err is the error to expect after prepping the query (error, string or
nil) |
| 55 err interface{} |
| 56 |
| 57 // equivalentQuery is another query which ShouldResemble q. This is usef
ul to |
| 58 // see the effects of redundancy pruning on e.g. filters. |
| 59 equivalentQuery *reducedQuery |
| 60 } |
| 61 |
| 130 var queryTests = []queryTest{ | 62 var queryTests = []queryTest{ |
| 131 {"only one inequality", | |
| 132 nq().Order("bob").Order("wat").Filter("bob >", 10).Filter("wat <
", 29), | |
| 133 "inequality filters on multiple properties", nil}, | |
| 134 | |
| 135 {"bad filter ops", | |
| 136 nq().Filter("Bob !", "value"), | |
| 137 "invalid operator \"!\"", nil}, | |
| 138 | |
| 139 {"bad filter", | |
| 140 nq().Filter("Bob", "value"), | |
| 141 "invalid filter", nil}, | |
| 142 | |
| 143 {"bad order", | |
| 144 nq().Order("+Bob"), | |
| 145 "invalid order", nil}, | |
| 146 | |
| 147 {"empty order", | |
| 148 nq().Order(""), | |
| 149 "empty order", nil}, | |
| 150 | |
| 151 {"underflow offset", | |
| 152 nq().Offset(-20), | |
| 153 "negative query offset", nil}, | |
| 154 | |
| 155 {"bad cursors (empty)", | 63 {"bad cursors (empty)", |
| 156 nq().Start(queryCursor("")), | 64 nq().Start(queryCursor("")), |
| 157 "invalid cursor", nil}, | 65 "invalid cursor", nil}, |
| 158 | 66 |
| 159 {"bad cursors (nil)", | 67 {"bad cursors (nil)", |
| 160 nq().Start(queryCursor("")), | 68 nq().Start(queryCursor("")), |
| 161 "invalid cursor", nil}, | 69 "invalid cursor", nil}, |
| 162 | 70 |
| 163 {"bad cursors (no key)", | 71 {"bad cursors (no key)", |
| 164 nq().End(curs("Foo", 100)), | 72 nq().End(curs("Foo", 100)), |
| 165 "invalid cursor", nil}, | 73 "invalid cursor", nil}, |
| 166 | 74 |
| 167 // TODO(riannucci): exclude cursors which are out-of-bounds with inequal
ity? | 75 // TODO(riannucci): exclude cursors which are out-of-bounds with inequal
ity? |
| 168 // I think right now you could have a query for > 10 with a start cursor
of 1. | 76 // I think right now you could have a query for > 10 with a start cursor
of 1. |
| 169 {"bad cursors (doesn't include ineq)", | 77 {"bad cursors (doesn't include ineq)", |
| 170 » » nq().Filter("Bob >", 10).Start( | 78 » » nq().Gt("Bob", 10).Start( |
| 171 curs("Foo", 100, "__key__", key("something", 1)), | 79 curs("Foo", 100, "__key__", key("something", 1)), |
| 172 ), | 80 ), |
| 173 "start cursor is invalid", nil}, | 81 "start cursor is invalid", nil}, |
| 174 | 82 |
| 175 {"bad cursors (doesn't include all orders)", | 83 {"bad cursors (doesn't include all orders)", |
| 176 nq().Order("Luci").Order("Charliene").Start( | 84 nq().Order("Luci").Order("Charliene").Start( |
| 177 curs("Luci", 100, "__key__", key("something", 1)), | 85 curs("Luci", 100, "__key__", key("something", 1)), |
| 178 ), | 86 ), |
| 179 "start cursor is invalid", nil}, | 87 "start cursor is invalid", nil}, |
| 180 | 88 |
| 181 {"cursor set multiple times", | |
| 182 nq().Order("Luci").End( | |
| 183 curs("Luci", 100, "__key__", key("something", 1)), | |
| 184 ).End( | |
| 185 curs("Luci", 100, "__key__", key("something", 1)), | |
| 186 ), | |
| 187 "multiply defined", nil}, | |
| 188 | |
| 189 {"cursor bad type", | 89 {"cursor bad type", |
| 190 nq().Order("Luci").End(sillyCursor("I am a banana")), | 90 nq().Order("Luci").End(sillyCursor("I am a banana")), |
| 191 » » "unknown type", nil}, | 91 » » "bad cursor type", nil}, |
| 192 | |
| 193 » {"projecting a keys-only query", | |
| 194 » » nq().Project("hello").KeysOnly(), | |
| 195 » » "cannot project a keysOnly query", nil}, | |
| 196 | |
| 197 » {"projecting a keys-only query (reverse)", | |
| 198 » » nq().KeysOnly().Project("hello"), | |
| 199 » » "cannot project a keysOnly query", nil}, | |
| 200 | |
| 201 » {"projecting an empty field", | |
| 202 » » nq().Project("hello", ""), | |
| 203 » » "cannot project on an empty field", nil}, | |
| 204 | |
| 205 » {"projecting __key__", | |
| 206 » » nq().Project("hello", "__key__"), | |
| 207 » » "cannot project on __key__", nil}, | |
| 208 | |
| 209 » {"projecting a duplicate", | |
| 210 » » nq().Project("hello", "hello"), | |
| 211 » » "cannot project on the same field twice", nil}, | |
| 212 | |
| 213 » {"projecting a duplicate (style 2)", | |
| 214 » » nq().Project("hello").Project("hello"), | |
| 215 » » "cannot project on the same field twice", nil}, | |
| 216 | |
| 217 » {"bad ancestors", | |
| 218 » » nq().Ancestor(key("goop", nil)), | |
| 219 » » dsS.ErrInvalidKey, nil}, | |
| 220 | |
| 221 » {"nil ancestors", | |
| 222 » » nq().Ancestor(nil), | |
| 223 » » "nil query ancestor", nil}, | |
| 224 | |
| 225 » {"Bad key filters", | |
| 226 » » nq().Filter("__key__ >", key("goop", nil)), | |
| 227 » » dsS.ErrInvalidKey, nil}, | |
| 228 | |
| 229 » {"filters for __key__ that aren't keys", | |
| 230 » » nq().Filter("__key__ >", 10), | |
| 231 » » "is not a key", nil}, | |
| 232 | |
| 233 » {"multiple inequalities", | |
| 234 » » nq().Filter("bob > ", 19).Filter("charlie < ", 20), | |
| 235 » » "inequality filters on multiple properties", nil}, | |
| 236 | |
| 237 » {"inequality must be first sort order", | |
| 238 » » nq().Filter("bob > ", 19).Order("-charlie"), | |
| 239 » » "first sort order", nil}, | |
| 240 | |
| 241 » {"inequality must be first sort order (reverse)", | |
| 242 » » nq().Order("-charlie").Filter("bob > ", 19), | |
| 243 » » "first sort order", nil}, | |
| 244 | |
| 245 » {"equality filter projected field", | |
| 246 » » nq().Project("foo").Filter("foo = ", 10), | |
| 247 » » "cannot project", nil}, | |
| 248 | |
| 249 » {"equality filter projected field (reverse)", | |
| 250 » » nq().Filter("foo = ", 10).Project("foo"), | |
| 251 » » "cannot project", nil}, | |
| 252 | |
| 253 » {"kindless with non-__key__ filters", | |
| 254 » » nq("").Filter("face <", 25.3), | |
| 255 » » "kindless queries can only filter on __key__", nil}, | |
| 256 | |
| 257 » {"kindless with non-__key__ orders", | |
| 258 » » nq("").Order("face"), | |
| 259 » » "invalid order for kindless query", nil}, | |
| 260 | |
| 261 » {"kindless with descending-__key__ order", | |
| 262 » » nq("").Order("-__key__"), | |
| 263 » » "invalid order for kindless query", nil}, | |
| 264 | |
| 265 » {"bad namespace", | |
| 266 » » nq("something", "sup").Order("__key__"), | |
| 267 » » "Namespace mismatched", nil}, | |
| 268 | |
| 269 » {"distinct non-projection", | |
| 270 » » nq().Distinct().Filter("marla >", 1), | |
| 271 » » "only makes sense on projection queries", nil}, | |
| 272 | |
| 273 » {"chained errors return the first", | |
| 274 » » nq().Ancestor(nil).Filter("hello", "wurld").Order(""), | |
| 275 » » "nil query ancestor", nil}, | |
| 276 | |
| 277 » {"bad ancestor namespace", | |
| 278 » » nq("", "nerd").Ancestor(key("something", "correct")), | |
| 279 » » "bad namespace", nil}, | |
| 280 | |
| 281 » {"multiple ancestors", | |
| 282 » » nq().Ancestor(key("something", "correct")).Ancestor(key("somethi
ng", "else")), | |
| 283 » » "more than one ancestor", nil}, | |
| 284 | |
| 285 » {"filter with illegal type", | |
| 286 » » nq().Filter("something =", complex(1, 2)), | |
| 287 » » "bad type complex", nil}, | |
| 288 | |
| 289 » {"sort orders used for equality are ignored", | |
| 290 » » nq().Order("a").Order("b").Order("c").Filter("b =", 2), | |
| 291 » » nil, | |
| 292 » » nq().Order("a").Order("c").Filter("b =", 2)}, | |
| 293 | |
| 294 » {"sort orders used for equality are ignored (reversed)", | |
| 295 » » nq().Filter("b =", 2).Order("a").Order("b").Order("c"), | |
| 296 » » nil, | |
| 297 » » nq().Order("a").Order("c").Filter("b =", 2)}, | |
| 298 | |
| 299 » {"duplicate orders are ignored", | |
| 300 » » nq().Order("a").Order("a").Order("a"), | |
| 301 » » nil, | |
| 302 » » nq().Order("a")}, | |
| 303 | 92 |
| 304 {"overconstrained inequality (>= v <)", | 93 {"overconstrained inequality (>= v <)", |
| 305 » » nq().Filter("bob >=", 10).Filter("bob <", 10), | 94 » » nq().Gte("bob", 10).Lt("bob", 10), |
| 306 "done", nil}, | 95 "done", nil}, |
| 307 | 96 |
| 308 {"overconstrained inequality (> v <)", | 97 {"overconstrained inequality (> v <)", |
| 309 » » nq().Filter("bob >", 10).Filter("bob <", 10), | 98 » » nq().Gt("bob", 10).Lt("bob", 10), |
| 310 "done", nil}, | 99 "done", nil}, |
| 311 | 100 |
| 312 {"overconstrained inequality (> v <=)", | 101 {"overconstrained inequality (> v <=)", |
| 313 » » nq().Filter("bob >", 10).Filter("bob <=", 10), | 102 » » nq().Gt("bob", 10).Lte("bob", 10), |
| 314 "done", nil}, | 103 "done", nil}, |
| 315 | 104 |
| 316 {"silly inequality (=> v <=)", | 105 {"silly inequality (=> v <=)", |
| 317 » » nq().Filter("bob >=", 10).Filter("bob <=", 10), | 106 » » nq().Gte("bob", 10).Lte("bob", 10), |
| 318 » » nil, | 107 » » nil, nil}, |
| 319 » » nil}, | |
| 320 | |
| 321 » {"Filtering on a reserved property is forbidden", | |
| 322 » » nq().Filter("__special__ >=", 10), | |
| 323 » » "filter on reserved property", | |
| 324 » » nil}, | |
| 325 | |
| 326 » {"oob key filters with ancestor (highside)", | |
| 327 » » nq().Ancestor(key("Hello", 10)).Filter("__key__ <", key("Hello",
9)), | |
| 328 » » "__key__ inequality", | |
| 329 » » nil}, | |
| 330 | |
| 331 » {"oob key filters with ancestor (lowside)", | |
| 332 » » nq().Ancestor(key("Hello", 10)).Filter("__key__ >", key("Hello",
11)), | |
| 333 » » "__key__ inequality", | |
| 334 » » nil}, | |
| 335 | |
| 336 » {"in-bound key filters with ancestor OK", | |
| 337 » » nq().Ancestor(key("Hello", 10)).Filter("__key__ <", key("Somethi
ng", "hi", key("Hello", 10))), | |
| 338 » » nil, | |
| 339 » » nil}, | |
| 340 | |
| 341 » {"projection elements get filled in", | |
| 342 » » nq().Project("Foo", "Bar").Order("-Bar"), | |
| 343 » » nil, | |
| 344 » » nq().Project("Foo", "Bar").Order("-Bar").Order("Foo")}, | |
| 345 | 108 |
| 346 {"cursors get smooshed into the inquality range", | 109 {"cursors get smooshed into the inquality range", |
| 347 » » (nq().Filter("Foo >", 3).Filter("Foo <", 10). | 110 » » (nq().Gt("Foo", 3).Lt("Foo", 10). |
| 348 Start(curs("Foo", 2, "__key__", key("Something", 1))). | 111 Start(curs("Foo", 2, "__key__", key("Something", 1))). |
| 349 End(curs("Foo", 20, "__key__", key("Something", 20)))), | 112 End(curs("Foo", 20, "__key__", key("Something", 20)))), |
| 350 nil, | 113 nil, |
| 351 » » nq().Filter("Foo >", 3).Filter("Foo <", 10)}, | 114 » » &reducedQuery{ |
| 115 » » » "ns", "Foo", map[string]stringset.Set{}, []dstore.IndexC
olumn{ |
| 116 » » » » {Property: "Foo"}, |
| 117 » » » » {Property: "__key__"}, |
| 118 » » » }, |
| 119 » » » increment(serialize.ToBytes(dstore.MkProperty(3))), |
| 120 » » » serialize.ToBytes(dstore.MkProperty(10)), |
| 121 » » » 2, |
| 122 » » }}, |
| 352 | 123 |
| 353 {"cursors could cause the whole query to be useless", | 124 {"cursors could cause the whole query to be useless", |
| 354 » » (nq().Filter("Foo >", 3).Filter("Foo <", 10). | 125 » » (nq().Gt("Foo", 3).Lt("Foo", 10). |
| 355 Start(curs("Foo", 200, "__key__", key("Something", 1))). | 126 Start(curs("Foo", 200, "__key__", key("Something", 1))). |
| 356 End(curs("Foo", 1, "__key__", key("Something", 20)))), | 127 End(curs("Foo", 1, "__key__", key("Something", 20)))), |
| 357 errQueryDone, | 128 errQueryDone, |
| 358 nil}, | 129 nil}, |
| 359 | |
| 360 {"query without anything is fine", | |
| 361 nq(), | |
| 362 nil, | |
| 363 nil}, | |
| 364 } | |
| 365 | |
| 366 func init() { | |
| 367 // this is supremely stupid. The SDK uses 'int' which measn we have to | |
| 368 // use it too, but then THEY BOUNDS CHECK IT FOR 32 BITS... *sigh* | |
| 369 if !IntIs32Bits { | |
| 370 queryTests = append(queryTests, []queryTest{ | |
| 371 {"OOB limit (32 bit)", | |
| 372 nq().Limit(MaxInt), | |
| 373 "query limit overflow", nil}, | |
| 374 | |
| 375 {"OOB offset (32 bit)", | |
| 376 nq().Offset(MaxInt), | |
| 377 "query offset overflow", nil}, | |
| 378 }...) | |
| 379 } | |
| 380 } | 130 } |
| 381 | 131 |
| 382 func TestQueries(t *testing.T) { | 132 func TestQueries(t *testing.T) { |
| 383 t.Parallel() | 133 t.Parallel() |
| 384 | 134 |
| 385 Convey("queries have tons of condition checking", t, func() { | 135 Convey("queries have tons of condition checking", t, func() { |
| 386 for _, tc := range queryTests { | |
| 387 Convey(tc.name, func() { | |
| 388 rq, err := tc.q.(*queryImpl).reduce("ns", false) | |
| 389 So(err, ShouldErrLike, tc.err) | |
| 390 | |
| 391 if tc.equivalentQuery != nil { | |
| 392 rq2, err := tc.equivalentQuery.(*queryIm
pl).reduce("ns", false) | |
| 393 So(err, ShouldBeNil) | |
| 394 So(rq, ShouldResemble, rq2) | |
| 395 } | |
| 396 }) | |
| 397 } | |
| 398 | |
| 399 Convey("non-ancestor queries in a transaction", func() { | 136 Convey("non-ancestor queries in a transaction", func() { |
| 400 » » » _, err := nq().(*queryImpl).reduce("ns", true) | 137 » » » fq, err := nq().Finalize() |
| 401 » » » So(err, ShouldErrLike, "Only ancestor queries") | 138 » » » So(err, ShouldErrLike, nil) |
| 139 » » » _, err = reduce(fq, "ns", true) |
| 140 » » » So(err, ShouldErrLike, "must include an Ancestor") |
| 402 }) | 141 }) |
| 403 | 142 |
| 404 Convey("absurd numbers of filters are prohibited", func() { | 143 Convey("absurd numbers of filters are prohibited", func() { |
| 405 q := nq().Ancestor(key("thing", "wat")) | 144 q := nq().Ancestor(key("thing", "wat")) |
| 406 for i := 0; i < 100; i++ { | 145 for i := 0; i < 100; i++ { |
| 407 » » » » q = q.Filter("something =", i) | 146 » » » » q = q.Eq("something", i) |
| 408 } | 147 } |
| 409 » » » //So(q.(*queryImpl).numComponents(), ShouldEqual, 101) | 148 » » » fq, err := q.Finalize() |
| 410 » » » _, err := q.(*queryImpl).reduce("ns", false) | 149 » » » So(err, ShouldErrLike, nil) |
| 150 » » » _, err = reduce(fq, "ns", false) |
| 411 So(err, ShouldErrLike, "query is too large") | 151 So(err, ShouldErrLike, "query is too large") |
| 412 }) | 152 }) |
| 153 |
| 154 Convey("bulk check", func() { |
| 155 for _, tc := range queryTests { |
| 156 Convey(tc.name, func() { |
| 157 rq := (*reducedQuery)(nil) |
| 158 fq, err := tc.q.Finalize() |
| 159 if err == nil { |
| 160 err = fq.Valid("s~aid", "ns") |
| 161 if err == nil { |
| 162 rq, err = reduce(fq, "ns
", false) |
| 163 } |
| 164 } |
| 165 So(err, ShouldErrLike, tc.err) |
| 166 |
| 167 if tc.equivalentQuery != nil { |
| 168 So(rq, ShouldResemble, tc.equiva
lentQuery) |
| 169 } |
| 170 }) |
| 171 } |
| 172 }) |
| 413 }) | 173 }) |
| 414 } | 174 } |
| OLD | NEW |