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 "math" | 8 "math" |
9 "testing" | 9 "testing" |
10 | 10 |
11 dsS "github.com/luci/gae/service/datastore" | 11 dsS "github.com/luci/gae/service/datastore" |
| 12 "github.com/luci/gae/service/datastore/serialize" |
| 13 . "github.com/luci/luci-go/common/testing/assertions" |
12 . "github.com/smartystreets/goconvey/convey" | 14 . "github.com/smartystreets/goconvey/convey" |
13 "golang.org/x/net/context" | 15 "golang.org/x/net/context" |
14 ) | 16 ) |
15 | 17 |
16 const ( | 18 const ( |
17 MaxUint = ^uint(0) | 19 MaxUint = ^uint(0) |
18 MaxInt = int(MaxUint >> 1) | 20 MaxInt = int(MaxUint >> 1) |
19 IntIs32Bits = int64(MaxInt) < math.MaxInt64 | 21 IntIs32Bits = int64(MaxInt) < math.MaxInt64 |
20 ) | 22 ) |
21 | 23 |
22 func TestDatastoreQueries(t *testing.T) { | 24 func TestDatastoreQueries(t *testing.T) { |
23 Convey("Datastore Query suport", t, func() { | 25 Convey("Datastore Query suport", t, func() { |
24 c := Use(context.Background()) | 26 c := Use(context.Background()) |
25 ds := dsS.Get(c) | 27 ds := dsS.Get(c) |
26 So(ds, ShouldNotBeNil) | 28 So(ds, ShouldNotBeNil) |
27 | 29 |
28 Convey("can create good queries", func() { | 30 Convey("can create good queries", func() { |
29 » » » q := ds.NewQuery("Foo").KeysOnly().Limit(10).Offset(39) | 31 » » » q := ds.NewQuery("Foo").Filter("farnsworth >", 20).KeysO
nly().Limit(10).Offset(39) |
30 » » » q = q.Start(queryCursor("kosmik")).End(queryCursor("krab
s")) | 32 |
| 33 » » » // normally you can only get cursors from inside of the
memory |
| 34 » » » // implementation, so this construction is just for test
ing. |
| 35 » » » start := queryCursor(bjoin( |
| 36 » » » » mkNum(1), |
| 37 » » » » serialize.ToBytes(dsS.IndexColumn{Property: "far
nsworth"}), |
| 38 » » » » mkNum(200), |
| 39 » » » » serialize.ToBytes(ds.NewKey("Foo", "id", 0, nil)
))) |
| 40 |
| 41 » » » So(start.String(), ShouldEqual, `gIAAZzFdTeeb3d9zOxsAh8g
AAUc32-AGabMAAA==`) |
| 42 |
| 43 » » » end := queryCursor(bjoin( |
| 44 » » » » mkNum(1), |
| 45 » » » » serialize.ToBytes(dsS.IndexColumn{Property: "far
nsworth"}), |
| 46 » » » » mkNum(3000), |
| 47 » » » » serialize.ToBytes(ds.NewKey("Foo", "zeta", 0, ni
l)))) |
| 48 |
| 49 » » » q = q.Start(start).End(end) |
31 So(q, ShouldNotBeNil) | 50 So(q, ShouldNotBeNil) |
32 So(q.(*queryImpl).err, ShouldBeNil) | 51 So(q.(*queryImpl).err, ShouldBeNil) |
33 » » » done, err := q.(*queryImpl).valid("", false) | 52 » » » rq, err := q.(*queryImpl).prep("", false) |
34 » » » So(done, ShouldBeFalse) | 53 » » » So(rq, ShouldNotBeNil) |
35 So(err, ShouldBeNil) | 54 So(err, ShouldBeNil) |
36 }) | 55 }) |
37 | 56 |
38 Convey("ensures orders make sense", func() { | 57 Convey("ensures orders make sense", func() { |
39 q := ds.NewQuery("Cool") | 58 q := ds.NewQuery("Cool") |
40 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") | 59 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") |
41 | 60 |
42 Convey("removes dups and equality orders", func() { | 61 Convey("removes dups and equality orders", func() { |
43 q = q.Order("wat") | 62 q = q.Order("wat") |
44 qi := q.(*queryImpl) | 63 qi := q.(*queryImpl) |
45 » » » » done, err := qi.valid("", false) | 64 » » » » rq, err := qi.prep("", false) |
46 » » » » So(done, ShouldBeFalse) | |
47 So(err, ShouldBeNil) | 65 So(err, ShouldBeNil) |
48 » » » » So(qi.order, ShouldResemble, []dsS.IndexColumn{{
Property: "wat"}}) | 66 » » » » So(rq.suffixFormat, ShouldResemble, []dsS.IndexC
olumn{{Property: "wat"}}) |
49 }) | 67 }) |
50 | 68 |
51 Convey("if we equality-filter on __key__, that's just si
lly", func() { | 69 Convey("if we equality-filter on __key__, that's just si
lly", func() { |
52 » » » » q = q.Order("wat") | 70 » » » » q = q.Order("wat").Filter("__key__ =", ds.NewKey
("Foo", "wat", 0, nil)) |
53 » » » » q := q.Filter("__key__ =", ds.NewKey("Foo", "wat
", 0, nil)) | 71 » » » » _, err := q.(*queryImpl).prep("", false) |
54 » » » » _, err := q.(*queryImpl).valid("", false) | 72 » » » » 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 }) | 73 }) |
58 | 74 |
59 }) | 75 }) |
60 | 76 |
61 » » Convey("inequalities apply immediately", func() { | 77 » }) |
62 » » » // NOTE: this is (maybe?) a slight divergence from reali
ty, but it's | 78 } |
63 » » » // helpful to retain sanity. It's possible that in real-
appengine, many | 79 |
64 » » » // inequalities count towards the MaxQueryComponents lim
it (100), where | 80 type queryTest struct { |
65 » » » // in this system we will never have more than 2 (an upp
er and lower | 81 » // name is the name of the test case |
66 » » » // bound). | 82 » name string |
67 » » }) | 83 |
68 | 84 » // q is the input query |
69 » » Convey("can create bad queries", func() { | 85 » q dsS.Query |
70 » » » q := ds.NewQuery("Foo") | 86 |
71 | 87 » // err is the error to expect after prepping the query (error, string or
nil) |
72 » » » Convey("only one inequality", func() { | 88 » err interface{} |
73 » » » » q = q.Order("bob").Order("wat") | 89 |
74 » » » » q = q.Filter("bob >", 10).Filter("wat <", 29) | 90 » // equivalentQuery is another query which ShouldResemble q. This is usef
ul to |
75 » » » » qi := q.(*queryImpl) | 91 » // see the effects of redundancy pruning on e.g. filters. |
76 » » » » _, err := qi.valid("", false) | 92 » equivalentQuery dsS.Query |
77 » » » » So(err.Error(), ShouldContainSubstring, | 93 } |
78 » » » » » "inequality filters on multiple properti
es") | 94 |
79 » » » }) | 95 var nq dsS.Query = &queryImpl{kind: "Foo", ns: "ns"} |
80 | 96 |
81 » » » Convey("bad filter ops", func() { | 97 type sillyCursor string |
82 » » » » q := q.Filter("Bob !", "value") | 98 |
83 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid operator \"!\"") | 99 func (s sillyCursor) String() string { return string(s) } |
84 » » » }) | 100 |
85 » » » Convey("bad filter", func() { | 101 var queryTests = []queryTest{ |
86 » » » » q := q.Filter("Bob", "value") | 102 » {"only one inequality", |
87 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid filter") | 103 » » nq.Order("bob").Order("wat").Filter("bob >", 10).Filter("wat <",
29), |
88 » » » }) | 104 » » "inequality filters on multiple properties", nil}, |
89 » » » Convey("bad order", func() { | 105 |
90 » » » » q := q.Order("+Bob") | 106 » {"bad filter ops", |
91 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid order") | 107 » » nq.Filter("Bob !", "value"), |
92 » » » }) | 108 » » "invalid operator \"!\"", nil}, |
93 » » » Convey("empty", func() { | 109 |
94 » » » » q := q.Order("") | 110 » {"bad filter", |
95 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "empty order") | 111 » » nq.Filter("Bob", "value"), |
96 » » » }) | 112 » » "invalid filter", nil}, |
97 » » » Convey("OOB limit", func() { | 113 |
98 » » » » // this is supremely stupid. The SDK uses 'int'
which measn we have to | 114 » {"bad order", |
99 » » » » // use it too, but then THEY BOUNDS CHECK IT FOR
32 BITS... *sigh* | 115 » » nq.Order("+Bob"), |
100 » » » » if !IntIs32Bits { | 116 » » "invalid order", nil}, |
101 » » » » » q := q.Limit(MaxInt) | 117 |
102 » » » » » So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query limit overflow") | 118 » {"empty order", |
| 119 » » nq.Order(""), |
| 120 » » "empty order", nil}, |
| 121 |
| 122 » {"underflow offset", |
| 123 » » nq.Offset(-20), |
| 124 » » "negative query offset", nil}, |
| 125 |
| 126 » {"bad cursors (empty)", |
| 127 » » nq.Start(queryCursor("")), |
| 128 » » "invalid cursor", nil}, |
| 129 |
| 130 » {"bad cursors (nil)", |
| 131 » » nq.Start(queryCursor("")), |
| 132 » » "invalid cursor", nil}, |
| 133 |
| 134 » {"bad cursors (no key)", |
| 135 » » nq.End(queryCursor(serialize.ToBytes(dsS.PropertyMap{ |
| 136 » » » "Foo": {prop(100)}, |
| 137 » » }))), |
| 138 » » "cursor is invalid", nil}, |
| 139 |
| 140 » // TODO(riannucci): exclude cursors which are out-of-bounds with inequal
ity? |
| 141 » // I think right now you could have a query for > 10 with a start cursor
of 1. |
| 142 » {"bad cursors (doesn't include ineq)", |
| 143 » » nq.Filter("Bob >", 10).Start(queryCursor(serialize.ToBytes(dsS.P
ropertyMap{ |
| 144 » » » "__key__": {prop(key("something", 1))}, |
| 145 » » » "Foo": {prop(100)}, |
| 146 » » }))), |
| 147 » » "cursor is invalid", nil}, |
| 148 |
| 149 » {"bad cursors (doesn't include all orders)", |
| 150 » » nq.Order("Luci").Order("Charliene").Start(queryCursor(serialize.
ToBytes(dsS.PropertyMap{ |
| 151 » » » "__key__": {prop(key("something", 1))}, |
| 152 » » » "Luci": {prop(100)}, |
| 153 » » }))), |
| 154 » » "cursor is invalid", nil}, |
| 155 |
| 156 » {"bad cursors (#values != 1)", |
| 157 » » nq.Order("Luci").End(queryCursor(serialize.ToBytes(dsS.PropertyM
ap{ |
| 158 » » » "__key__": {prop(key("something", 1))}, |
| 159 » » » "Luci": {prop(100), prop(101)}, |
| 160 » » }))), |
| 161 » » "cursor is invalid", nil}, |
| 162 |
| 163 » {"cursor set multiple times", |
| 164 » » nq.Order("Luci").End(queryCursor(serialize.ToBytes(dsS.PropertyM
ap{ |
| 165 » » » "__key__": {prop(key("something", 1))}, |
| 166 » » » "Luci": {prop(100), prop(101)}, |
| 167 » » }))).End(queryCursor(serialize.ToBytes(dsS.PropertyMap{ |
| 168 » » » "__key__": {prop(key("something", 1))}, |
| 169 » » » "Luci": {prop(100), prop(101)}, |
| 170 » » }))), |
| 171 » » "multiply defined", nil}, |
| 172 |
| 173 » {"cursor bad type", |
| 174 » » nq.Order("Luci").End(sillyCursor("I am a banana")), |
| 175 » » "unknown type", nil}, |
| 176 |
| 177 » {"projecting a keys-only query", |
| 178 » » nq.Project("hello").KeysOnly(), |
| 179 » » "cannot project a keysOnly query", nil}, |
| 180 |
| 181 » {"projecting a keys-only query (reverse)", |
| 182 » » nq.KeysOnly().Project("hello"), |
| 183 » » "cannot project a keysOnly query", nil}, |
| 184 |
| 185 » {"projecting an empty field", |
| 186 » » nq.Project("hello", ""), |
| 187 » » "cannot project on an empty field", nil}, |
| 188 |
| 189 » {"projecting __key__", |
| 190 » » nq.Project("hello", "__key__"), |
| 191 » » "cannot project on __key__", nil}, |
| 192 |
| 193 » {"projecting a duplicate", |
| 194 » » nq.Project("hello", "hello"), |
| 195 » » "cannot project on the same field twice", nil}, |
| 196 |
| 197 » {"projecting a duplicate (style 2)", |
| 198 » » nq.Project("hello").Project("hello"), |
| 199 » » "cannot project on the same field twice", nil}, |
| 200 |
| 201 » {"bad ancestors", |
| 202 » » nq.Ancestor(key("goop", nil)), |
| 203 » » dsS.ErrInvalidKey, nil}, |
| 204 |
| 205 » {"nil ancestors", |
| 206 » » nq.Ancestor(nil), |
| 207 » » "nil query ancestor", nil}, |
| 208 |
| 209 » {"Bad key filters", |
| 210 » » nq.Filter("__key__ >", key("goop", nil)), |
| 211 » » dsS.ErrInvalidKey, nil}, |
| 212 |
| 213 » {"filters for __key__ that aren't keys", |
| 214 » » nq.Filter("__key__ >", 10), |
| 215 » » "is not a key", nil}, |
| 216 |
| 217 » {"multiple inequalities", |
| 218 » » nq.Filter("bob > ", 19).Filter("charlie < ", 20), |
| 219 » » "inequality filters on multiple properties", nil}, |
| 220 |
| 221 » {"inequality must be first sort order", |
| 222 » » nq.Filter("bob > ", 19).Order("-charlie"), |
| 223 » » "first sort order", nil}, |
| 224 |
| 225 » {"inequality must be first sort order (reverse)", |
| 226 » » nq.Order("-charlie").Filter("bob > ", 19), |
| 227 » » "first sort order", nil}, |
| 228 |
| 229 » {"equality filter projected field", |
| 230 » » nq.Project("foo").Filter("foo = ", 10), |
| 231 » » "cannot project", nil}, |
| 232 |
| 233 » {"equality filter projected field (reverse)", |
| 234 » » nq.Filter("foo = ", 10).Project("foo"), |
| 235 » » "cannot project", nil}, |
| 236 |
| 237 » {"kindless with non-__key__ filters", |
| 238 » » (&queryImpl{ns: "ns"}).Filter("face <", 25.3), |
| 239 » » "kindless queries can only filter on __key__", nil}, |
| 240 |
| 241 » {"kindless with non-__key__ orders", |
| 242 » » (&queryImpl{ns: "ns"}).Order("face"), |
| 243 » » "invalid order for kindless query", nil}, |
| 244 |
| 245 » {"kindless with descending-__key__ order", |
| 246 » » (&queryImpl{ns: "ns"}).Order("-__key__"), |
| 247 » » "invalid order for kindless query", nil}, |
| 248 |
| 249 » {"bad namespace", |
| 250 » » (&queryImpl{kind: "something", ns: "sup"}).Order("__key__"), |
| 251 » » "Namespace mismatched", nil}, |
| 252 |
| 253 » {"distinct non-projection", |
| 254 » » nq.Distinct().Filter("marla >", 1), |
| 255 » » "only makes sense on projection queries", nil}, |
| 256 |
| 257 » {"chained errors return the first", |
| 258 » » nq.Ancestor(nil).Filter("hello", "wurld").Order(""), |
| 259 » » "nil query ancestor", nil}, |
| 260 |
| 261 » {"bad ancestor namespace", |
| 262 » » (&queryImpl{ns: "nerd"}).Ancestor(key("something", "correct")), |
| 263 » » "bad namespace", nil}, |
| 264 |
| 265 » {"multiple ancestors", |
| 266 » » nq.Ancestor(key("something", "correct")).Ancestor(key("something
", "else")), |
| 267 » » "more than one ancestor", nil}, |
| 268 |
| 269 » {"filter with illegal type", |
| 270 » » nq.Filter("something =", complex(1, 2)), |
| 271 » » "bad type complex", nil}, |
| 272 |
| 273 » {"sort orders used for equality are ignored", |
| 274 » » nq.Order("a").Order("b").Order("c").Filter("b =", 2), |
| 275 » » nil, |
| 276 » » nq.Order("a").Order("c").Filter("b =", 2)}, |
| 277 |
| 278 » {"sort orders used for equality are ignored (reversed)", |
| 279 » » nq.Filter("b =", 2).Order("a").Order("b").Order("c"), |
| 280 » » nil, |
| 281 » » nq.Order("a").Order("c").Filter("b =", 2)}, |
| 282 |
| 283 » {"duplicate orders are ignored", |
| 284 » » nq.Order("a").Order("a").Order("a"), |
| 285 » » nil, |
| 286 » » nq.Order("a")}, |
| 287 |
| 288 » {"overconstrained inequality (>= v <)", |
| 289 » » nq.Filter("bob >=", 10).Filter("bob <", 10), |
| 290 » » "done", nil}, |
| 291 |
| 292 » {"overconstrained inequality (> v <)", |
| 293 » » nq.Filter("bob >", 10).Filter("bob <", 10), |
| 294 » » "done", nil}, |
| 295 |
| 296 » {"overconstrained inequality (> v <=)", |
| 297 » » nq.Filter("bob >", 10).Filter("bob <=", 10), |
| 298 » » "done", nil}, |
| 299 |
| 300 » {"silly inequality (=> v <=)", |
| 301 » » nq.Filter("bob >=", 10).Filter("bob <=", 10), |
| 302 » » nil, |
| 303 » » nil}, |
| 304 } |
| 305 |
| 306 func init() { |
| 307 » // this is supremely stupid. The SDK uses 'int' which measn we have to |
| 308 » // use it too, but then THEY BOUNDS CHECK IT FOR 32 BITS... *sigh* |
| 309 » if !IntIs32Bits { |
| 310 » » queryTests = append(queryTests, []queryTest{ |
| 311 » » » {"OOB limit (32 bit)", |
| 312 » » » » nq.Limit(MaxInt), |
| 313 » » » » "query limit overflow", nil}, |
| 314 |
| 315 » » » {"OOB offset (32 bit)", |
| 316 » » » » nq.Offset(MaxInt), |
| 317 » » » » "query offset overflow", nil}, |
| 318 » » }...) |
| 319 » } |
| 320 } |
| 321 |
| 322 func TestBadQueries(t *testing.T) { |
| 323 » t.Parallel() |
| 324 |
| 325 » Convey("can create bad queries", t, func() { |
| 326 » » for _, tc := range queryTests { |
| 327 » » » Convey(tc.name, func() { |
| 328 » » » » _, err := tc.q.(*queryImpl).prep("ns", false) |
| 329 » » » » So(err, ShouldErrLike, tc.err) |
| 330 |
| 331 » » » » if tc.equivalentQuery != nil { |
| 332 » » » » » So(tc.q, ShouldResemble, tc.equivalentQu
ery) |
103 } | 333 } |
104 }) | 334 }) |
105 » » » Convey("underflow offset", func() { | 335 » » } |
106 » » » » q := q.Offset(-29) | 336 |
107 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "negative query offset") | 337 » » Convey("non-ancestor queries in a transaction", func() { |
108 » » » }) | 338 » » » _, err := nq.(*queryImpl).prep("ns", true) |
109 » » » Convey("OOB offset", func() { | 339 » » » So(err, ShouldErrLike, "Only ancestor queries") |
110 » » » » if !IntIs32Bits { | 340 » » }) |
111 » » » » » q := q.Offset(MaxInt) | 341 |
112 » » » » » So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query offset overflow") | 342 » » Convey("absurd numbers of filters are prohibited", func() { |
113 » » » » } | 343 » » » q := nq.Ancestor(key("thing", "wat")) |
114 » » » }) | 344 » » » for i := 0; i < 100; i++ { |
115 » » » Convey("Bad cursors", func() { | 345 » » » » q = q.Filter("something =", i) |
116 » » » » q := q.Start(queryCursor("")).End(queryCursor(""
)) | 346 » » » } |
117 » » » » So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid cursor") | 347 » » » //So(q.(*queryImpl).numComponents(), ShouldEqual, 101) |
118 » » » }) | 348 » » » _, err := q.(*queryImpl).prep("ns", false) |
119 » » » Convey("Bad ancestors", func() { | 349 » » » So(err, ShouldErrLike, "query is too large") |
120 » » » » q := q.Ancestor(ds.NewKey("Goop", "wat", 10, nil
)) | 350 » » }) |
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 » » }) | |
181 | |
182 }) | 351 }) |
183 } | 352 } |
| 353 |
| 354 func TestQueryPreparation(t *testing.T) { |
| 355 t.Parallel() |
| 356 |
| 357 Convey("Test Query.prep", t, func() { |
| 358 Convey("ancestor is serialized", func() { |
| 359 rq, err := nq.Ancestor(key("hi", 1)).(*queryImpl).prep("
ns", false) |
| 360 So(err, ShouldBeNil) |
| 361 So(rq.ancestor, ShouldResemble, []byte{0, 1, 0x69, 0x35,
0x40, 1, 0x80, 0x80, 0}) |
| 362 }) |
| 363 |
| 364 Convey("inequality property adjusts start/end", func() { |
| 365 |
| 366 }) |
| 367 }) |
| 368 } |
OLD | NEW |