OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package datastore |
| 6 |
| 7 import ( |
| 8 "math" |
| 9 "testing" |
| 10 |
| 11 . "github.com/luci/luci-go/common/testing/assertions" |
| 12 . "github.com/smartystreets/goconvey/convey" |
| 13 ) |
| 14 |
| 15 const ( |
| 16 MaxUint = ^uint(0) |
| 17 MaxInt = int(MaxUint >> 1) |
| 18 IntIs32Bits = int64(MaxInt) < math.MaxInt64 |
| 19 ) |
| 20 |
| 21 func TestDatastoreQueries(t *testing.T) { |
| 22 Convey("Datastore Query suport", t, func() { |
| 23 Convey("can create good queries", func() { |
| 24 q := NewQuery("Foo").Gt("farnsworth", 20).KeysOnly(true)
.Limit(10).Offset(39) |
| 25 |
| 26 start := fakeCursor("hi") |
| 27 |
| 28 end := fakeCursor("end") |
| 29 |
| 30 q = q.Start(start).End(end) |
| 31 So(q, ShouldNotBeNil) |
| 32 fq, err := q.Finalize() |
| 33 So(fq, ShouldNotBeNil) |
| 34 So(err, ShouldBeNil) |
| 35 }) |
| 36 |
| 37 Convey("ensures orders make sense", func() { |
| 38 q := NewQuery("Cool") |
| 39 q = q.Eq("cat", 19).Eq("bob", 10).Order("bob", "bob") |
| 40 |
| 41 Convey("removes dups and equality orders", func() { |
| 42 q = q.Order("wat") |
| 43 fq, err := q.Finalize() |
| 44 So(err, ShouldBeNil) |
| 45 So(fq.Orders(), ShouldResemble, []IndexColumn{ |
| 46 {Property: "wat"}, {Property: "__key__"}
}) |
| 47 }) |
| 48 }) |
| 49 |
| 50 }) |
| 51 } |
| 52 |
| 53 type queryTest struct { |
| 54 // name is the name of the test case |
| 55 name string |
| 56 |
| 57 // q is the input query |
| 58 q *Query |
| 59 |
| 60 // gql is the expected generated GQL. |
| 61 gql string |
| 62 |
| 63 // err is the error to expect after prepping the query (error, string or
nil) |
| 64 err interface{} |
| 65 |
| 66 // equivalentQuery is another query which ShouldResemble q. This is usef
ul to |
| 67 // see the effects of redundancy pruning on e.g. filters. |
| 68 equivalentQuery *Query |
| 69 } |
| 70 |
| 71 type sillyCursor string |
| 72 |
| 73 func (s sillyCursor) String() string { return string(s) } |
| 74 |
| 75 func nq(kinds ...string) *Query { |
| 76 kind := "Foo" |
| 77 if len(kinds) > 0 { |
| 78 kind = kinds[0] |
| 79 } |
| 80 return NewQuery(kind) |
| 81 } |
| 82 |
| 83 func mkKey(elems ...interface{}) *Key { |
| 84 return MakeKey("s~aid", "ns", elems...) |
| 85 } |
| 86 |
| 87 var queryTests = []queryTest{ |
| 88 {"only one inequality", |
| 89 nq().Order("bob", "wat").Gt("bob", 10).Lt("wat", 29), |
| 90 "", |
| 91 "inequality filters on multiple properties", nil}, |
| 92 |
| 93 {"bad order", |
| 94 nq().Order("+Bob"), |
| 95 "", |
| 96 "invalid order", nil}, |
| 97 |
| 98 {"empty order", |
| 99 nq().Order(""), |
| 100 "", |
| 101 "empty order", nil}, |
| 102 |
| 103 {"negative offset disables Offset", |
| 104 nq().Offset(100).Offset(-20), |
| 105 "SELECT * FROM `Foo` ORDER BY `__key__`", |
| 106 nil, nq()}, |
| 107 |
| 108 {"projecting a keys-only query", |
| 109 nq().Project("hello").KeysOnly(true), |
| 110 "", |
| 111 "cannot project a keysOnly query", nil}, |
| 112 |
| 113 {"projecting a keys-only query (reverse)", |
| 114 nq().KeysOnly(true).Project("hello"), |
| 115 "", |
| 116 "cannot project a keysOnly query", nil}, |
| 117 |
| 118 {"projecting an empty field", |
| 119 nq().Project("hello", ""), |
| 120 "", |
| 121 "cannot filter/project on: \"\"", nil}, |
| 122 |
| 123 {"projecting __key__", |
| 124 nq().Project("hello", "__key__"), |
| 125 "", |
| 126 "cannot project on \"__key__\"", nil}, |
| 127 |
| 128 {"projecting a duplicate", |
| 129 nq().Project("hello", "hello"), |
| 130 "SELECT `hello` FROM `Foo` ORDER BY `hello`, `__key__`", |
| 131 nil, nq().Project("hello")}, |
| 132 |
| 133 {"projecting a duplicate (style 2)", |
| 134 nq().Project("hello").Project("hello"), |
| 135 "SELECT `hello` FROM `Foo` ORDER BY `hello`, `__key__`", |
| 136 nil, nq().Project("hello")}, |
| 137 |
| 138 {"bad ancestors", |
| 139 nq().Ancestor(mkKey("goop", 0)), |
| 140 "", |
| 141 ErrInvalidKey, nil}, |
| 142 |
| 143 {"nil ancestors", |
| 144 nq().Ancestor(nil), |
| 145 "SELECT * FROM `Foo` ORDER BY `__key__`", |
| 146 nil, nq()}, |
| 147 |
| 148 {"Bad key filters", |
| 149 nq().Gt("__key__", mkKey("goop", 0)), |
| 150 "", |
| 151 ErrInvalidKey, nil}, |
| 152 |
| 153 {"filters for __key__ that aren't keys", |
| 154 nq().Gt("__key__", 10), |
| 155 "", |
| 156 "filters on \"__key__\" must have type *Key", nil}, |
| 157 |
| 158 {"multiple inequalities", |
| 159 nq().Gt("bob", 19).Lt("charlie", 20), |
| 160 "", |
| 161 "inequality filters on multiple properties", nil}, |
| 162 |
| 163 {"inequality must be first sort order", |
| 164 nq().Gt("bob", 19).Order("-charlie"), |
| 165 "", |
| 166 "first sort order", nil}, |
| 167 |
| 168 {"inequality must be first sort order (reverse)", |
| 169 nq().Order("-charlie").Gt("bob", 19), |
| 170 "", |
| 171 "first sort order", nil}, |
| 172 |
| 173 {"equality filter projected field", |
| 174 nq().Project("foo").Eq("foo", 10), |
| 175 "", |
| 176 "cannot project", nil}, |
| 177 |
| 178 {"equality filter projected field (reverse)", |
| 179 nq().Eq("foo", 10).Project("foo"), |
| 180 "", |
| 181 "cannot project", nil}, |
| 182 |
| 183 {"kindless with non-__key__ filters", |
| 184 nq("").Lt("face", 25.3), |
| 185 "", |
| 186 "kindless queries can only filter on __key__", nil}, |
| 187 |
| 188 {"kindless with non-__key__ orders", |
| 189 nq("").Order("face"), |
| 190 "", |
| 191 "invalid order for kindless query", nil}, |
| 192 |
| 193 {"kindless with descending-__key__ order", |
| 194 nq("").Order("-__key__"), |
| 195 "", |
| 196 "invalid order for kindless query", nil}, |
| 197 |
| 198 {"distinct non-projection", |
| 199 nq().Distinct(true).Gt("marla", 1), |
| 200 "SELECT * FROM `Foo` WHERE `marla` > 1 ORDER BY `marla`, `__key_
_`", |
| 201 nil, nq().Gt("marla", 1)}, |
| 202 |
| 203 {"chained errors return the first", |
| 204 nq().Eq("__reserved__", 100).Eq("hello", "wurld").Order(""), |
| 205 "", |
| 206 "__reserved__", nil}, |
| 207 |
| 208 {"multiple ancestors", |
| 209 nq().Ancestor(mkKey("something", "correct")).Ancestor(mkKey("som
ething", "else")), |
| 210 ("SELECT * FROM `Foo` " + |
| 211 "WHERE __key__ HAS ANCESTOR KEY(DATASET(\"s~aid\"), NAME
SPACE(\"ns\"), \"something\", \"else\") " + |
| 212 "ORDER BY `__key__`"), |
| 213 nil, nq().Ancestor(mkKey("something", "else"))}, |
| 214 |
| 215 {"filter with illegal type", |
| 216 nq().Eq("something", complex(1, 2)), |
| 217 "", |
| 218 "bad type complex", nil}, |
| 219 |
| 220 {"sort orders used for equality are ignored", |
| 221 nq().Order("a", "b", "c").Eq("b", 2), |
| 222 "SELECT * FROM `Foo` WHERE `b` = 2 ORDER BY `a`, `c`, `__key__`"
, |
| 223 nil, nq().Order("a", "c").Eq("b", 2)}, |
| 224 |
| 225 {"sort orders used for equality are ignored (reversed)", |
| 226 nq().Eq("b", 2).Order("a", "b", "c"), |
| 227 "SELECT * FROM `Foo` WHERE `b` = 2 ORDER BY `a`, `c`, `__key__`"
, |
| 228 nil, |
| 229 nq().Order("a", "c").Eq("b", 2)}, |
| 230 |
| 231 {"duplicate orders are ignored", |
| 232 nq().Order("a").Order("a").Order("a"), |
| 233 "SELECT * FROM `Foo` ORDER BY `a`, `__key__`", |
| 234 nil, |
| 235 nq().Order("a")}, |
| 236 |
| 237 {"Filtering on a reserved property is forbidden", |
| 238 nq().Gte("__special__", 10), |
| 239 "", |
| 240 "cannot filter/project on reserved property: \"__special__\"", |
| 241 nil}, |
| 242 |
| 243 {"in-bound key filters with ancestor OK", |
| 244 nq().Ancestor(mkKey("Hello", 10)).Lt("__key__", mkKey("Hello", 1
0, "Something", "hi")), |
| 245 ("SELECT * FROM `Foo` " + |
| 246 "WHERE `__key__` < KEY(DATASET(\"s~aid\"), NAMESPACE(\"n
s\"), \"Hello\", 10, \"Something\", \"hi\") AND " + |
| 247 "__key__ HAS ANCESTOR KEY(DATASET(\"s~aid\"), NAMESPACE(
\"ns\"), \"Hello\", 10) " + |
| 248 "ORDER BY `__key__`"), |
| 249 nil, |
| 250 nil}, |
| 251 |
| 252 {"projection elements get filled in", |
| 253 nq().Project("Foo", "Bar").Order("-Bar"), |
| 254 "SELECT `Bar`, `Foo` FROM `Foo` ORDER BY `Bar` DESC, `Foo`, `__k
ey__`", |
| 255 nil, nq().Project("Foo", "Bar").Order("-Bar").Order("Foo")}, |
| 256 |
| 257 {"query without anything is fine", |
| 258 nq(), |
| 259 "SELECT * FROM `Foo` ORDER BY `__key__`", |
| 260 nil, |
| 261 nil}, |
| 262 } |
| 263 |
| 264 func TestQueries(t *testing.T) { |
| 265 t.Parallel() |
| 266 |
| 267 Convey("queries have tons of condition checking", t, func() { |
| 268 for _, tc := range queryTests { |
| 269 Convey(tc.name, func() { |
| 270 fq, err := tc.q.Finalize() |
| 271 if err == nil { |
| 272 err = fq.Valid("s~aid", "ns") |
| 273 } |
| 274 So(err, ShouldErrLike, tc.err) |
| 275 |
| 276 if tc.gql != "" { |
| 277 So(fq.GQL(), ShouldEqual, tc.gql) |
| 278 } |
| 279 |
| 280 if tc.equivalentQuery != nil { |
| 281 fq2, err := tc.equivalentQuery.Finalize(
) |
| 282 So(err, ShouldBeNil) |
| 283 |
| 284 fq.original = nil |
| 285 fq2.original = nil |
| 286 So(fq, ShouldResemble, fq2) |
| 287 } |
| 288 }) |
| 289 } |
| 290 }) |
| 291 } |
OLD | NEW |