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 "math" | 9 "math" |
9 "testing" | 10 "testing" |
10 | 11 |
11 dsS "github.com/luci/gae/service/datastore" | 12 dsS "github.com/luci/gae/service/datastore" |
| 13 "github.com/luci/gae/service/datastore/serialize" |
| 14 "github.com/luci/luci-go/common/cmpbin" |
| 15 . "github.com/luci/luci-go/common/testing/assertions" |
12 . "github.com/smartystreets/goconvey/convey" | 16 . "github.com/smartystreets/goconvey/convey" |
13 "golang.org/x/net/context" | 17 "golang.org/x/net/context" |
14 ) | 18 ) |
15 | 19 |
16 const ( | 20 const ( |
17 MaxUint = ^uint(0) | 21 MaxUint = ^uint(0) |
18 MaxInt = int(MaxUint >> 1) | 22 MaxInt = int(MaxUint >> 1) |
19 IntIs32Bits = int64(MaxInt) < math.MaxInt64 | 23 IntIs32Bits = int64(MaxInt) < math.MaxInt64 |
20 ) | 24 ) |
21 | 25 |
22 func TestDatastoreQueries(t *testing.T) { | 26 func TestDatastoreQueries(t *testing.T) { |
23 Convey("Datastore Query suport", t, func() { | 27 Convey("Datastore Query suport", t, func() { |
24 c := Use(context.Background()) | 28 c := Use(context.Background()) |
25 ds := dsS.Get(c) | 29 ds := dsS.Get(c) |
26 So(ds, ShouldNotBeNil) | 30 So(ds, ShouldNotBeNil) |
27 | 31 |
28 Convey("can create good queries", func() { | 32 Convey("can create good queries", func() { |
29 » » » q := ds.NewQuery("Foo").KeysOnly().Limit(10).Offset(39) | 33 » » » q := ds.NewQuery("Foo").Filter("farnsworth >", 20).KeysO
nly().Limit(10).Offset(39) |
30 » » » q = q.Start(queryCursor("kosmik")).End(queryCursor("krab
s")) | 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_AIGHyIkAAUc32-A
GabMAAA==`) |
| 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) |
31 So(q, ShouldNotBeNil) | 55 So(q, ShouldNotBeNil) |
32 So(q.(*queryImpl).err, ShouldBeNil) | 56 So(q.(*queryImpl).err, ShouldBeNil) |
33 » » » done, err := q.(*queryImpl).valid("", false) | 57 » » » rq, err := q.(*queryImpl).reduce("", false) |
34 » » » So(done, ShouldBeFalse) | 58 » » » So(rq, ShouldNotBeNil) |
35 So(err, ShouldBeNil) | 59 So(err, ShouldBeNil) |
36 }) | 60 }) |
37 | 61 |
38 Convey("ensures orders make sense", func() { | 62 Convey("ensures orders make sense", func() { |
39 q := ds.NewQuery("Cool") | 63 q := ds.NewQuery("Cool") |
40 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") | 64 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") |
41 | 65 |
42 Convey("removes dups and equality orders", func() { | 66 Convey("removes dups and equality orders", func() { |
43 q = q.Order("wat") | 67 q = q.Order("wat") |
44 qi := q.(*queryImpl) | 68 qi := q.(*queryImpl) |
45 » » » » done, err := qi.valid("", false) | 69 » » » » So(qi.err, ShouldBeNil) |
46 » » » » So(done, ShouldBeFalse) | 70 » » » » rq, err := qi.reduce("", false) |
47 So(err, ShouldBeNil) | 71 So(err, ShouldBeNil) |
48 » » » » So(qi.order, ShouldResemble, []dsS.IndexColumn{{
Property: "wat"}}) | 72 » » » » So(rq.suffixFormat, ShouldResemble, []dsS.IndexC
olumn{ |
| 73 » » » » » {Property: "wat"}, {Property: "__key__"}
}) |
49 }) | 74 }) |
50 | 75 |
51 Convey("if we equality-filter on __key__, that's just si
lly", func() { | 76 Convey("if we equality-filter on __key__, that's just si
lly", func() { |
52 » » » » q = q.Order("wat") | 77 » » » » q = q.Order("wat").Filter("__key__ =", ds.NewKey
("Foo", "wat", 0, nil)) |
53 » » » » q := q.Filter("__key__ =", ds.NewKey("Foo", "wat
", 0, nil)) | 78 » » » » _, err := q.(*queryImpl).reduce("", false) |
54 » » » » _, err := q.(*queryImpl).valid("", false) | 79 » » » » So(err, ShouldErrLike, "query equality filter on
__key__ is silly") |
55 » » » » So(err.Error(), ShouldContainSubstring, | |
56 » » » » » "query equality filter on __key__ is sil
ly") | |
57 }) | 80 }) |
58 | 81 |
59 }) | 82 }) |
60 | 83 |
61 Convey("inequalities apply immediately", func() { | 84 }) |
62 // NOTE: this is (maybe?) a slight divergence from reali
ty, but it's | 85 } |
63 // helpful to retain sanity. It's possible that in real-
appengine, many | 86 |
64 // inequalities count towards the MaxQueryComponents lim
it (100), where | 87 type queryTest struct { |
65 // in this system we will never have more than 2 (an upp
er and lower | 88 // name is the name of the test case |
66 // bound). | 89 name string |
67 }) | 90 |
68 | 91 // q is the input query |
69 Convey("can create bad queries", func() { | 92 q dsS.Query |
70 q := ds.NewQuery("Foo") | 93 |
71 | 94 // err is the error to expect after prepping the query (error, string or
nil) |
72 Convey("only one inequality", func() { | 95 err interface{} |
73 q = q.Order("bob").Order("wat") | 96 |
74 q = q.Filter("bob >", 10).Filter("wat <", 29) | 97 // equivalentQuery is another query which ShouldResemble q. This is usef
ul to |
75 qi := q.(*queryImpl) | 98 // see the effects of redundancy pruning on e.g. filters. |
76 _, err := qi.valid("", false) | 99 equivalentQuery dsS.Query |
77 So(err.Error(), ShouldContainSubstring, | 100 } |
78 "inequality filters on multiple properti
es") | 101 |
79 }) | 102 type sillyCursor string |
80 | 103 |
81 Convey("bad filter ops", func() { | 104 func (s sillyCursor) String() string { return string(s) } |
82 q := q.Filter("Bob !", "value") | 105 |
83 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid operator \"!\"") | 106 func curs(pairs ...interface{}) queryCursor { |
84 }) | 107 if len(pairs)%2 != 0 { |
85 Convey("bad filter", func() { | 108 panic("curs() takes only even pairs") |
86 q := q.Filter("Bob", "value") | 109 } |
87 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid filter") | 110 pre := &bytes.Buffer{} |
88 }) | 111 cmpbin.WriteUint(pre, uint64(len(pairs)/2)) |
89 Convey("bad order", func() { | 112 post := serialize.Invertible(&bytes.Buffer{}) |
90 q := q.Order("+Bob") | 113 for i := 0; i < len(pairs); i += 2 { |
91 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid order") | 114 k, v := pairs[i].(string), pairs[i+1] |
92 }) | 115 |
93 Convey("empty", func() { | 116 col := dsS.IndexColumn{Property: k} |
94 q := q.Order("") | 117 |
95 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "empty order") | 118 post.SetInvert(false) |
96 }) | 119 if k[0] == '-' { |
97 Convey("OOB limit", func() { | 120 post.SetInvert(false) |
98 // this is supremely stupid. The SDK uses 'int'
which measn we have to | 121 col.Property = k[1:] |
99 // use it too, but then THEY BOUNDS CHECK IT FOR
32 BITS... *sigh* | 122 col.Direction = dsS.DESCENDING |
100 if !IntIs32Bits { | 123 } |
101 q := q.Limit(MaxInt) | 124 serialize.WriteIndexColumn(pre, col) |
102 So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query limit overflow") | 125 serialize.WriteProperty(post, serialize.WithoutContext, prop(v)) |
| 126 } |
| 127 return queryCursor(bjoin(pre.Bytes(), post.Bytes())) |
| 128 } |
| 129 |
| 130 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)", |
| 156 nq().Start(queryCursor("")), |
| 157 "invalid cursor", nil}, |
| 158 |
| 159 {"bad cursors (nil)", |
| 160 nq().Start(queryCursor("")), |
| 161 "invalid cursor", nil}, |
| 162 |
| 163 {"bad cursors (no key)", |
| 164 nq().End(curs("Foo", 100)), |
| 165 "invalid cursor", nil}, |
| 166 |
| 167 // 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. |
| 169 {"bad cursors (doesn't include ineq)", |
| 170 nq().Filter("Bob >", 10).Start( |
| 171 curs("Foo", 100, "__key__", key("something", 1)), |
| 172 ), |
| 173 "start cursor is invalid", nil}, |
| 174 |
| 175 {"bad cursors (doesn't include all orders)", |
| 176 nq().Order("Luci").Order("Charliene").Start( |
| 177 curs("Luci", 100, "__key__", key("something", 1)), |
| 178 ), |
| 179 "start cursor is invalid", nil}, |
| 180 |
| 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", |
| 190 nq().Order("Luci").End(sillyCursor("I am a banana")), |
| 191 "unknown 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 |
| 304 {"overconstrained inequality (>= v <)", |
| 305 nq().Filter("bob >=", 10).Filter("bob <", 10), |
| 306 "done", nil}, |
| 307 |
| 308 {"overconstrained inequality (> v <)", |
| 309 nq().Filter("bob >", 10).Filter("bob <", 10), |
| 310 "done", nil}, |
| 311 |
| 312 {"overconstrained inequality (> v <=)", |
| 313 nq().Filter("bob >", 10).Filter("bob <=", 10), |
| 314 "done", nil}, |
| 315 |
| 316 {"silly inequality (=> v <=)", |
| 317 nq().Filter("bob >=", 10).Filter("bob <=", 10), |
| 318 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 |
| 346 {"cursors get smooshed into the inquality range", |
| 347 (nq().Filter("Foo >", 3).Filter("Foo <", 10). |
| 348 Start(curs("Foo", 2, "__key__", key("Something", 1))). |
| 349 End(curs("Foo", 20, "__key__", key("Something", 20)))), |
| 350 nil, |
| 351 nq().Filter("Foo >", 3).Filter("Foo <", 10)}, |
| 352 |
| 353 {"cursors could cause the whole query to be useless", |
| 354 (nq().Filter("Foo >", 3).Filter("Foo <", 10). |
| 355 Start(curs("Foo", 200, "__key__", key("Something", 1))). |
| 356 End(curs("Foo", 1, "__key__", key("Something", 20)))), |
| 357 errQueryDone, |
| 358 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 } |
| 381 |
| 382 func TestQueries(t *testing.T) { |
| 383 t.Parallel() |
| 384 |
| 385 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) |
103 } | 395 } |
104 }) | 396 }) |
105 » » » Convey("underflow offset", func() { | 397 » » } |
106 » » » » q := q.Offset(-29) | 398 |
107 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "negative query offset") | 399 » » Convey("non-ancestor queries in a transaction", func() { |
108 » » » }) | 400 » » » _, err := nq().(*queryImpl).reduce("ns", true) |
109 » » » Convey("OOB offset", func() { | 401 » » » So(err, ShouldErrLike, "Only ancestor queries") |
110 » » » » if !IntIs32Bits { | |
111 » » » » » q := q.Offset(MaxInt) | |
112 » » » » » So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query offset overflow") | |
113 » » » » } | |
114 » » » }) | |
115 » » » Convey("Bad cursors", func() { | |
116 » » » » q := q.Start(queryCursor("")).End(queryCursor(""
)) | |
117 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid cursor") | |
118 » » » }) | |
119 » » » Convey("Bad ancestors", func() { | |
120 » » » » q := q.Ancestor(ds.NewKey("Goop", "wat", 10, nil
)) | |
121 » » » » So(q, ShouldNotBeNil) | |
122 » » » » _, err := q.(*queryImpl).valid("", false) | |
123 » » » » So(err, ShouldEqual, dsS.ErrInvalidKey) | |
124 » » » }) | |
125 » » » Convey("nil ancestors", func() { | |
126 » » » » _, err := q.Ancestor(nil).(*queryImpl).valid("",
false) | |
127 » » » » So(err.Error(), ShouldContainSubstring, "nil que
ry ancestor") | |
128 » » » }) | |
129 » » » Convey("Bad key filters", func() { | |
130 » » » » q := q.Filter("__key__ >", ds.NewKey("Goop", "wa
t", 10, nil)) | |
131 » » » » _, err := q.(*queryImpl).valid("", false) | |
132 » » » » So(err, ShouldEqual, dsS.ErrInvalidKey) | |
133 » » » }) | |
134 » » » Convey("non-ancestor queries in a transaction", func() { | |
135 » » » » _, err := q.(*queryImpl).valid("", true) | |
136 » » » » So(err.Error(), ShouldContainSubstring, "Only an
cestor queries") | |
137 » » » }) | |
138 » » » Convey("absurd numbers of filters are prohibited", func(
) { | |
139 » » » » q := q.Ancestor(ds.NewKey("thing", "wat", 0, nil
)) | |
140 » » » » for i := 0; i < 100; i++ { | |
141 » » » » » q = q.Filter("something =", i) | |
142 » » » » } | |
143 » » » » _, err := q.(*queryImpl).valid("", false) | |
144 » » » » So(err.Error(), ShouldContainSubstring, "query i
s too large") | |
145 » » » }) | |
146 » » » Convey("filters for __key__ that aren't keys", func() { | |
147 » » » » q := q.Filter("__key__ > ", 10) | |
148 » » » » _, err := q.(*queryImpl).valid("", false) | |
149 » » » » So(err.Error(), ShouldContainSubstring, "is not
a key") | |
150 » » » }) | |
151 » » » Convey("multiple inequalities", func() { | |
152 » » » » q := q.Filter("bob > ", 19).Filter("charlie < ",
20) | |
153 » » » » _, err := q.(*queryImpl).valid("", false) | |
154 » » » » So(err.Error(), ShouldContainSubstring, | |
155 » » » » » "inequality filters on multiple properti
es") | |
156 » » » }) | |
157 » » » Convey("bad sort orders", func() { | |
158 » » » » q := q.Filter("bob > ", 19).Order("-charlie") | |
159 » » » » _, err := q.(*queryImpl).valid("", false) | |
160 » » » » So(err.Error(), ShouldContainSubstring, "first s
ort order") | |
161 » » » }) | |
162 » » » Convey("kindless with non-__key__ filters", func() { | |
163 » » » » q := ds.NewQuery("").Filter("face <", 25.3) | |
164 » » » » _, err := q.(*queryImpl).valid("", false) | |
165 » » » » So(err.Error(), ShouldContainSubstring, | |
166 » » » » » "kindless queries can only filter on __k
ey__") | |
167 » » » }) | |
168 » » » Convey("kindless with non-__key__ orders", func() { | |
169 » » » » q := ds.NewQuery("").Order("face") | |
170 » » » » _, err := q.(*queryImpl).valid("", false) | |
171 » » » » So(err.Error(), ShouldContainSubstring, | |
172 » » » » » "invalid order for kindless query") | |
173 » » » }) | |
174 » » » Convey("kindless with decending-__key__ orders", func()
{ | |
175 » » » » q := ds.NewQuery("").Order("-__key__") | |
176 » » » » _, err := q.(*queryImpl).valid("", false) | |
177 » » » » So(err.Error(), ShouldContainSubstring, | |
178 » » » » » "invalid order for kindless query") | |
179 » » » }) | |
180 }) | 402 }) |
181 | 403 |
| 404 Convey("absurd numbers of filters are prohibited", func() { |
| 405 q := nq().Ancestor(key("thing", "wat")) |
| 406 for i := 0; i < 100; i++ { |
| 407 q = q.Filter("something =", i) |
| 408 } |
| 409 //So(q.(*queryImpl).numComponents(), ShouldEqual, 101) |
| 410 _, err := q.(*queryImpl).reduce("ns", false) |
| 411 So(err, ShouldErrLike, "query is too large") |
| 412 }) |
182 }) | 413 }) |
183 } | 414 } |
OLD | NEW |