| 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 memory | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "math" | |
| 10 "testing" | |
| 11 | |
| 12 "github.com/luci/gae" | |
| 13 "github.com/luci/gae/helper" | |
| 14 . "github.com/smartystreets/goconvey/convey" | |
| 15 "golang.org/x/net/context" | |
| 16 ) | |
| 17 | |
| 18 func TestDatastoreKinder(t *testing.T) { | |
| 19 t.Parallel() | |
| 20 | |
| 21 Convey("Datastore keys", t, func() { | |
| 22 c := Use(context.Background()) | |
| 23 rds := gae.GetRDS(c) | |
| 24 So(rds, ShouldNotBeNil) | |
| 25 | |
| 26 Convey("implements DSNewKeyer", func() { | |
| 27 Convey("NewKey", func() { | |
| 28 key := rds.NewKey("nerd", "stringID", 0, nil) | |
| 29 So(key, ShouldNotBeNil) | |
| 30 So(key.Kind(), ShouldEqual, "nerd") | |
| 31 So(key.StringID(), ShouldEqual, "stringID") | |
| 32 So(key.IntID(), ShouldEqual, 0) | |
| 33 So(key.Parent(), ShouldBeNil) | |
| 34 So(key.AppID(), ShouldEqual, "dev~app") | |
| 35 So(key.Namespace(), ShouldEqual, "") | |
| 36 So(key.String(), ShouldEqual, "/nerd,stringID") | |
| 37 So(helper.DSKeyIncomplete(key), ShouldBeFalse) | |
| 38 So(helper.DSKeyValid(key, "", false), ShouldBeTr
ue) | |
| 39 }) | |
| 40 }) | |
| 41 | |
| 42 }) | |
| 43 } | |
| 44 | |
| 45 func testGetMeta(c context.Context, k gae.DSKey) int64 { | |
| 46 rds := gae.GetRDS(c) | |
| 47 k = rds.NewKey("__entity_group__", "", 1, helper.DSKeyRoot(k)) | |
| 48 pmap := gae.DSPropertyMap{} | |
| 49 rds.Get(k, pmap) | |
| 50 return pmap["__version__"][0].Value().(int64) | |
| 51 } | |
| 52 | |
| 53 var pls = helper.GetPLS | |
| 54 | |
| 55 func TestDatastoreSingleReadWriter(t *testing.T) { | |
| 56 t.Parallel() | |
| 57 | |
| 58 Convey("Datastore single reads and writes", t, func() { | |
| 59 c := Use(context.Background()) | |
| 60 rds := gae.GetRDS(c) | |
| 61 So(rds, ShouldNotBeNil) | |
| 62 | |
| 63 Convey("implements DSSingleReadWriter", func() { | |
| 64 type Foo struct { | |
| 65 Val int | |
| 66 } | |
| 67 | |
| 68 Convey("invalid keys break", func() { | |
| 69 k := rds.NewKey("Foo", "", 0, nil) | |
| 70 So(rds.Get(k, nil), ShouldEqual, gae.ErrDSInvali
dKey) | |
| 71 | |
| 72 _, err := rds.Put(rds.NewKey("Foo", "", 0, k), p
ls(&Foo{})) | |
| 73 So(err, ShouldEqual, gae.ErrDSInvalidKey) | |
| 74 }) | |
| 75 | |
| 76 Convey("getting objects that DNE is an error", func() { | |
| 77 k := rds.NewKey("Foo", "", 1, nil) | |
| 78 So(rds.Get(k, nil), ShouldEqual, gae.ErrDSNoSuch
Entity) | |
| 79 }) | |
| 80 | |
| 81 Convey("Can Put stuff", func() { | |
| 82 // with an incomplete key! | |
| 83 k := rds.NewKey("Foo", "", 0, nil) | |
| 84 f := &Foo{Val: 10} | |
| 85 k, err := rds.Put(k, pls(f)) | |
| 86 So(err, ShouldBeNil) | |
| 87 So(k.String(), ShouldEqual, "/Foo,1") | |
| 88 | |
| 89 Convey("and Get it back", func() { | |
| 90 newFoo := &Foo{} | |
| 91 err := rds.Get(k, pls(newFoo)) | |
| 92 So(err, ShouldBeNil) | |
| 93 So(newFoo, ShouldResemble, f) | |
| 94 | |
| 95 Convey("and we can Delete it", func() { | |
| 96 err := rds.Delete(k) | |
| 97 So(err, ShouldBeNil) | |
| 98 | |
| 99 err = rds.Get(k, pls(newFoo)) | |
| 100 So(err, ShouldEqual, gae.ErrDSNo
SuchEntity) | |
| 101 }) | |
| 102 }) | |
| 103 Convey("Deleteing with a bogus key is bad", func
() { | |
| 104 err := rds.Delete(rds.NewKey("Foo", "wat
", 100, nil)) | |
| 105 So(err, ShouldEqual, gae.ErrDSInvalidKey
) | |
| 106 }) | |
| 107 Convey("Deleteing a DNE entity is fine", func()
{ | |
| 108 err := rds.Delete(rds.NewKey("Foo", "wat
", 0, nil)) | |
| 109 So(err, ShouldBeNil) | |
| 110 }) | |
| 111 | |
| 112 Convey("with multiple puts", func() { | |
| 113 So(testGetMeta(c, k), ShouldEqual, 1) | |
| 114 | |
| 115 keys := []gae.DSKey{} | |
| 116 plss := []gae.DSPropertyLoadSaver{} | |
| 117 | |
| 118 pkey := k | |
| 119 for i := 0; i < 10; i++ { | |
| 120 keys = append(keys, rds.NewKey("
Foo", "", 0, pkey)) | |
| 121 plss = append(plss, pls(&Foo{Val
: 10})) | |
| 122 } | |
| 123 keys, err := rds.PutMulti(keys, plss) | |
| 124 So(err, ShouldBeNil) | |
| 125 So(testGetMeta(c, k), ShouldEqual, 11) | |
| 126 | |
| 127 Convey("ensure that group versions persi
st across deletes", func() { | |
| 128 So(rds.Delete(k), ShouldBeNil) | |
| 129 for i := int64(1); i < 11; i++ { | |
| 130 So(rds.Delete(rds.NewKey
("Foo", "", i, k)), ShouldBeNil) | |
| 131 } | |
| 132 // TODO(riannucci): replace with
a Count query instead of this cast | |
| 133 ents := rds.(*dsImpl).data.store
.GetCollection("ents:") | |
| 134 num, _ := ents.GetTotals() | |
| 135 // /__entity_root_ids__,Foo | |
| 136 // /Foo,1/__entity_group__,1 | |
| 137 // /Foo,1/__entity_group_ids__,1 | |
| 138 So(num, ShouldEqual, 3) | |
| 139 | |
| 140 So(curVersion(ents, groupMetaKey
(k)), ShouldEqual, 22) | |
| 141 | |
| 142 k, err := rds.Put(k, pls(f)) | |
| 143 So(err, ShouldBeNil) | |
| 144 So(testGetMeta(c, k), ShouldEqua
l, 23) | |
| 145 }) | |
| 146 | |
| 147 Convey("can GetMulti", func() { | |
| 148 plss := make([]gae.DSPropertyLoa
dSaver, len(keys)) | |
| 149 for i := range plss { | |
| 150 plss[i] = gae.DSProperty
Map{} | |
| 151 } | |
| 152 err := rds.GetMulti(keys, plss) | |
| 153 So(err, ShouldBeNil) | |
| 154 for _, pls := range plss { | |
| 155 So(pls.(gae.DSPropertyMa
p), ShouldResemble, gae.DSPropertyMap{ | |
| 156 "Val": {gae.MkDS
Property(10)}, | |
| 157 }) | |
| 158 } | |
| 159 }) | |
| 160 }) | |
| 161 }) | |
| 162 }) | |
| 163 | |
| 164 Convey("implements DSTransactioner", func() { | |
| 165 type Foo struct { | |
| 166 Val int | |
| 167 } | |
| 168 Convey("Put", func() { | |
| 169 k := rds.NewKey("Foo", "", 0, nil) | |
| 170 f := &Foo{Val: 10} | |
| 171 k, err := rds.Put(k, pls(f)) | |
| 172 So(err, ShouldBeNil) | |
| 173 So(k.String(), ShouldEqual, "/Foo,1") | |
| 174 | |
| 175 Convey("can Put new entity groups", func() { | |
| 176 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 177 rds := gae.GetRDS(c) | |
| 178 So(rds, ShouldNotBeNil) | |
| 179 | |
| 180 f1 := &Foo{Val: 100} | |
| 181 k, err := rds.Put(rds.NewKey("Fo
o", "", 0, nil), pls(f1)) | |
| 182 So(err, ShouldBeNil) | |
| 183 So(k.String(), ShouldEqual, "/Fo
o,2") | |
| 184 | |
| 185 f2 := &Foo{Val: 200} | |
| 186 k, err = rds.Put(rds.NewKey("Foo
", "", 0, nil), pls(f2)) | |
| 187 So(err, ShouldBeNil) | |
| 188 So(k.String(), ShouldEqual, "/Fo
o,3") | |
| 189 | |
| 190 return nil | |
| 191 }, &gae.DSTransactionOptions{XG: true}) | |
| 192 So(err, ShouldBeNil) | |
| 193 | |
| 194 f := &Foo{} | |
| 195 So(rds.Get(rds.NewKey("Foo", "", 2, nil)
, pls(f)), ShouldBeNil) | |
| 196 So(f.Val, ShouldEqual, 100) | |
| 197 | |
| 198 f = &Foo{} | |
| 199 So(rds.Get(rds.NewKey("Foo", "", 3, nil)
, pls(f)), ShouldBeNil) | |
| 200 So(f.Val, ShouldEqual, 200) | |
| 201 }) | |
| 202 | |
| 203 Convey("can Put new entities in a current group"
, func() { | |
| 204 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 205 rds := gae.GetRDS(c) | |
| 206 So(rds, ShouldNotBeNil) | |
| 207 | |
| 208 par := k | |
| 209 | |
| 210 f1 := &Foo{Val: 100} | |
| 211 k, err := rds.Put(rds.NewKey("Fo
o", "", 0, par), pls(f1)) | |
| 212 So(err, ShouldBeNil) | |
| 213 So(k.String(), ShouldEqual, "/Fo
o,1/Foo,1") | |
| 214 | |
| 215 f2 := &Foo{Val: 200} | |
| 216 k, err = rds.Put(rds.NewKey("Foo
", "", 0, par), pls(f2)) | |
| 217 So(err, ShouldBeNil) | |
| 218 So(k.String(), ShouldEqual, "/Fo
o,1/Foo,2") | |
| 219 | |
| 220 return nil | |
| 221 }, nil) | |
| 222 So(err, ShouldBeNil) | |
| 223 | |
| 224 f1 := &Foo{} | |
| 225 So(rds.Get(rds.NewKey("Foo", "", 1, k),
pls(f1)), ShouldBeNil) | |
| 226 So(f1.Val, ShouldEqual, 100) | |
| 227 | |
| 228 f2 := &Foo{} | |
| 229 So(rds.Get(rds.NewKey("Foo", "", 2, k),
pls(f2)), ShouldBeNil) | |
| 230 So(f2.Val, ShouldEqual, 200) | |
| 231 }) | |
| 232 | |
| 233 Convey("Deletes work too", func() { | |
| 234 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 235 rds := gae.GetRDS(c) | |
| 236 So(rds, ShouldNotBeNil) | |
| 237 So(rds.Delete(k), ShouldBeNil) | |
| 238 return nil | |
| 239 }, nil) | |
| 240 So(err, ShouldBeNil) | |
| 241 So(rds.Get(k, nil), ShouldEqual, gae.Err
DSNoSuchEntity) | |
| 242 }) | |
| 243 | |
| 244 Convey("A Get counts against your group count",
func() { | |
| 245 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 246 rds := gae.GetRDS(c) | |
| 247 So(rds.Get(rds.NewKey("Foo", "",
20, nil), nil), ShouldEqual, gae.ErrDSNoSuchEntity) | |
| 248 So(rds.Get(k, nil).Error(), Shou
ldContainSubstring, "cross-group") | |
| 249 return nil | |
| 250 }, nil) | |
| 251 So(err, ShouldBeNil) | |
| 252 }) | |
| 253 | |
| 254 Convey("Get takes a snapshot", func() { | |
| 255 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 256 txnDS := gae.GetRDS(c) | |
| 257 So(txnDS, ShouldNotBeNil) | |
| 258 | |
| 259 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
| 260 So(f.Val, ShouldEqual, 10) | |
| 261 | |
| 262 // Don't ever do this in a real
program unless you want to guarantee | |
| 263 // a failed transaction :) | |
| 264 f.Val = 11 | |
| 265 _, err := rds.Put(k, pls(f)) | |
| 266 So(err, ShouldBeNil) | |
| 267 | |
| 268 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
| 269 So(f.Val, ShouldEqual, 10) | |
| 270 | |
| 271 return nil | |
| 272 }, nil) | |
| 273 So(err, ShouldBeNil) | |
| 274 | |
| 275 f := &Foo{} | |
| 276 So(rds.Get(k, pls(f)), ShouldBeNil) | |
| 277 So(f.Val, ShouldEqual, 11) | |
| 278 }) | |
| 279 | |
| 280 Convey("and snapshots are consistent even after
Puts", func() { | |
| 281 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 282 txnDS := gae.GetRDS(c) | |
| 283 So(txnDS, ShouldNotBeNil) | |
| 284 | |
| 285 f := &Foo{} | |
| 286 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
| 287 So(f.Val, ShouldEqual, 10) | |
| 288 | |
| 289 // Don't ever do this in a real
program unless you want to guarantee | |
| 290 // a failed transaction :) | |
| 291 f.Val = 11 | |
| 292 | |
| 293 _, err := rds.Put(k, pls(f)) | |
| 294 So(err, ShouldBeNil) | |
| 295 | |
| 296 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
| 297 So(f.Val, ShouldEqual, 10) | |
| 298 | |
| 299 f.Val = 20 | |
| 300 _, err = txnDS.Put(k, pls(f)) | |
| 301 So(err, ShouldBeNil) | |
| 302 | |
| 303 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
| 304 So(f.Val, ShouldEqual, 10) // st
ill gets 10 | |
| 305 | |
| 306 return nil | |
| 307 }, nil) | |
| 308 So(err.Error(), ShouldContainSubstring,
"concurrent") | |
| 309 | |
| 310 f := &Foo{} | |
| 311 So(rds.Get(k, pls(f)), ShouldBeNil) | |
| 312 So(f.Val, ShouldEqual, 11) | |
| 313 }) | |
| 314 | |
| 315 Convey("Reusing a transaction context is bad new
s", func() { | |
| 316 k := rds.NewKey("Foo", "", 1, nil) | |
| 317 txnDS := gae.RawDatastore(nil) | |
| 318 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 319 txnDS = gae.GetRDS(c) | |
| 320 So(txnDS.Get(k, gae.DSPropertyMa
p{}), ShouldBeNil) | |
| 321 return nil | |
| 322 }, nil) | |
| 323 So(err, ShouldBeNil) | |
| 324 So(txnDS.Get(k, gae.DSPropertyMap{}).Err
or(), ShouldContainSubstring, "expired") | |
| 325 }) | |
| 326 | |
| 327 Convey("Nested transactions are rejected", func(
) { | |
| 328 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 329 err := gae.GetRDS(c).RunInTransa
ction(func(c context.Context) error { | |
| 330 panic("noooo") | |
| 331 }, nil) | |
| 332 So(err.Error(), ShouldContainSub
string, "nested transactions") | |
| 333 return nil | |
| 334 }, nil) | |
| 335 So(err, ShouldBeNil) | |
| 336 }) | |
| 337 | |
| 338 Convey("Concurrent transactions only accept one
set of changes", func() { | |
| 339 // Note: I think this implementation is
actually /slightly/ wrong. | |
| 340 // Accorting to my read of the docs for
appengine, when you open a | |
| 341 // transaction it actually (essentially)
holds a reference to the | |
| 342 // entire datastore. Our implementation
takes a snapshot of the | |
| 343 // entity group as soon as something obs
erves/affects it. | |
| 344 // | |
| 345 // That said... I'm not sure if there's
really a semantic difference. | |
| 346 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
| 347 txnDS := gae.GetRDS(c) | |
| 348 f := &Foo{Val: 21} | |
| 349 _, err = txnDS.Put(k, pls(f)) | |
| 350 So(err, ShouldBeNil) | |
| 351 | |
| 352 err := rds.RunInTransaction(func
(c context.Context) error { | |
| 353 txnDS := gae.GetRDS(c) | |
| 354 f := &Foo{Val: 27} | |
| 355 _, err := txnDS.Put(k, p
ls(f)) | |
| 356 So(err, ShouldBeNil) | |
| 357 return nil | |
| 358 }, nil) | |
| 359 So(err, ShouldBeNil) | |
| 360 | |
| 361 return nil | |
| 362 }, nil) | |
| 363 So(err.Error(), ShouldContainSubstring,
"concurrent") | |
| 364 | |
| 365 f := &Foo{} | |
| 366 So(rds.Get(k, pls(f)), ShouldBeNil) | |
| 367 So(f.Val, ShouldEqual, 27) | |
| 368 }) | |
| 369 | |
| 370 Convey("XG", func() { | |
| 371 Convey("Modifying two groups with XG=fal
se is invalid", func() { | |
| 372 err := rds.RunInTransaction(func
(c context.Context) error { | |
| 373 rds := gae.GetRDS(c) | |
| 374 f := &Foo{Val: 200} | |
| 375 _, err := rds.Put(k, pls
(f)) | |
| 376 So(err, ShouldBeNil) | |
| 377 | |
| 378 _, err = rds.Put(rds.New
Key("Foo", "", 2, nil), pls(f)) | |
| 379 So(err.Error(), ShouldCo
ntainSubstring, "cross-group") | |
| 380 return err | |
| 381 }, nil) | |
| 382 So(err.Error(), ShouldContainSub
string, "cross-group") | |
| 383 }) | |
| 384 | |
| 385 Convey("Modifying >25 groups with XG=tru
e is invald", func() { | |
| 386 err := rds.RunInTransaction(func
(c context.Context) error { | |
| 387 rds := gae.GetRDS(c) | |
| 388 for i := int64(1); i < 2
6; i++ { | |
| 389 k := rds.NewKey(
"Foo", "", i, nil) | |
| 390 f := &Foo{Val: 2
00} | |
| 391 _, err := rds.Pu
t(k, pls(f)) | |
| 392 So(err, ShouldBe
Nil) | |
| 393 } | |
| 394 f := &Foo{Val: 200} | |
| 395 _, err := rds.Put(rds.Ne
wKey("Foo", "", 27, nil), pls(f)) | |
| 396 So(err.Error(), ShouldCo
ntainSubstring, "too many entity groups") | |
| 397 return err | |
| 398 }, &gae.DSTransactionOptions{XG:
true}) | |
| 399 So(err.Error(), ShouldContainSub
string, "too many entity groups") | |
| 400 }) | |
| 401 }) | |
| 402 | |
| 403 Convey("Errors and panics", func() { | |
| 404 Convey("returning an error aborts", func
() { | |
| 405 err := rds.RunInTransaction(func
(c context.Context) error { | |
| 406 rds := gae.GetRDS(c) | |
| 407 f := &Foo{Val: 200} | |
| 408 _, err := rds.Put(k, pls
(f)) | |
| 409 So(err, ShouldBeNil) | |
| 410 | |
| 411 return fmt.Errorf("thing
y") | |
| 412 }, nil) | |
| 413 So(err.Error(), ShouldEqual, "th
ingy") | |
| 414 | |
| 415 f := &Foo{} | |
| 416 So(rds.Get(k, pls(f)), ShouldBeN
il) | |
| 417 So(f.Val, ShouldEqual, 10) | |
| 418 }) | |
| 419 | |
| 420 Convey("panicing aborts", func() { | |
| 421 So(func() { | |
| 422 rds.RunInTransaction(fun
c(c context.Context) error { | |
| 423 rds := gae.GetRD
S(c) | |
| 424 f := &Foo{Val: 2
00} | |
| 425 _, err := rds.Pu
t(k, pls(f)) | |
| 426 So(err, ShouldBe
Nil) | |
| 427 panic("wheeeeee"
) | |
| 428 }, nil) | |
| 429 }, ShouldPanic) | |
| 430 | |
| 431 f := &Foo{} | |
| 432 So(rds.Get(k, pls(f)), ShouldBeN
il) | |
| 433 So(f.Val, ShouldEqual, 10) | |
| 434 }) | |
| 435 }) | |
| 436 }) | |
| 437 }) | |
| 438 | |
| 439 }) | |
| 440 } | |
| 441 | |
| 442 const MaxUint = ^uint(0) | |
| 443 const MaxInt = int(MaxUint >> 1) | |
| 444 const IntIs32Bits = int64(MaxInt) < math.MaxInt64 | |
| 445 | |
| 446 func TestDatastoreQueryer(t *testing.T) { | |
| 447 Convey("Datastore Query suport", t, func() { | |
| 448 c := Use(context.Background()) | |
| 449 rds := gae.GetRDS(c) | |
| 450 So(rds, ShouldNotBeNil) | |
| 451 | |
| 452 Convey("can create good queries", func() { | |
| 453 q := rds.NewQuery("Foo").KeysOnly().Limit(10).Offset(39) | |
| 454 q = q.Start(queryCursor("kosmik")).End(queryCursor("krab
s")) | |
| 455 So(q, ShouldNotBeNil) | |
| 456 So(q.(*queryImpl).err, ShouldBeNil) | |
| 457 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 458 So(qi.err, ShouldBeNil) | |
| 459 }) | |
| 460 | |
| 461 Convey("normalize ensures orders make sense", func() { | |
| 462 q := rds.NewQuery("Cool") | |
| 463 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") | |
| 464 | |
| 465 Convey("removes dups and equality orders", func() { | |
| 466 q = q.Order("wat") | |
| 467 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
| 468 So(qi.err, ShouldBeNil) | |
| 469 So(qi.order, ShouldResemble, []queryOrder{{"wat"
, qASC}}) | |
| 470 }) | |
| 471 | |
| 472 Convey("keeps inequality orders", func() { | |
| 473 q = q.Order("wat") | |
| 474 q := q.Filter("bob >", 10).Filter("wat <", 29) | |
| 475 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
| 476 So(qi.order, ShouldResemble, []queryOrder{{"bob"
, qASC}, {"wat", qASC}}) | |
| 477 So(qi.err.Error(), ShouldContainSubstring, "Only
one inequality") | |
| 478 }) | |
| 479 | |
| 480 Convey("if we equality-filter on __key__, order is ditch
ed", func() { | |
| 481 q = q.Order("wat") | |
| 482 q := q.Filter("__key__ =", rds.NewKey("Foo", "wa
t", 0, nil)) | |
| 483 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
| 484 So(qi.order, ShouldResemble, []queryOrder(nil)) | |
| 485 So(qi.err, ShouldBeNil) | |
| 486 }) | |
| 487 | |
| 488 Convey("if we order by key and something else, key domin
ates", func() { | |
| 489 q := q.Order("__key__").Order("wat") | |
| 490 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
| 491 So(qi.order, ShouldResemble, []queryOrder{{"__ke
y__", qASC}}) | |
| 492 So(qi.err, ShouldBeNil) | |
| 493 }) | |
| 494 }) | |
| 495 | |
| 496 Convey("can create bad queries", func() { | |
| 497 q := rds.NewQuery("Foo") | |
| 498 | |
| 499 Convey("bad filter ops", func() { | |
| 500 q := q.Filter("Bob !", "value") | |
| 501 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid operator \"!\"") | |
| 502 }) | |
| 503 Convey("bad filter", func() { | |
| 504 q := q.Filter("Bob", "value") | |
| 505 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid filter") | |
| 506 }) | |
| 507 Convey("bad order", func() { | |
| 508 q := q.Order("+Bob") | |
| 509 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid order") | |
| 510 }) | |
| 511 Convey("empty", func() { | |
| 512 q := q.Order("") | |
| 513 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "empty order") | |
| 514 }) | |
| 515 Convey("OOB limit", func() { | |
| 516 // this is supremely stupid. The SDK uses 'int'
which measn we have to | |
| 517 // use it too, but then THEY BOUNDS CHECK IT FOR
32 BITS... *sigh* | |
| 518 if !IntIs32Bits { | |
| 519 q := q.Limit(MaxInt) | |
| 520 So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query limit overflow") | |
| 521 } | |
| 522 }) | |
| 523 Convey("underflow offset", func() { | |
| 524 q := q.Offset(-29) | |
| 525 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "negative query offset") | |
| 526 }) | |
| 527 Convey("OOB offset", func() { | |
| 528 if !IntIs32Bits { | |
| 529 q := q.Offset(MaxInt) | |
| 530 So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query offset overflow") | |
| 531 } | |
| 532 }) | |
| 533 Convey("Bad cursors", func() { | |
| 534 q := q.Start(queryCursor("")).End(queryCursor(""
)) | |
| 535 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid cursor") | |
| 536 }) | |
| 537 Convey("Bad ancestors", func() { | |
| 538 q := q.Ancestor(rds.NewKey("Goop", "wat", 10, ni
l)) | |
| 539 So(q, ShouldNotBeNil) | |
| 540 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 541 So(qi.err, ShouldEqual, gae.ErrDSInvalidKey) | |
| 542 }) | |
| 543 Convey("nil ancestors", func() { | |
| 544 qi := q.Ancestor(nil).(*queryImpl).checkCorrectn
ess("", false) | |
| 545 So(qi.err.Error(), ShouldContainSubstring, "nil
query ancestor") | |
| 546 }) | |
| 547 Convey("Bad key filters", func() { | |
| 548 q := q.Filter("__key__ =", rds.NewKey("Goop", "w
at", 10, nil)) | |
| 549 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 550 So(qi.err, ShouldEqual, gae.ErrDSInvalidKey) | |
| 551 }) | |
| 552 Convey("non-ancestor queries in a transaction", func() { | |
| 553 qi := q.(*queryImpl).checkCorrectness("", true) | |
| 554 So(qi.err.Error(), ShouldContainSubstring, "Only
ancestor queries") | |
| 555 }) | |
| 556 Convey("absurd numbers of filters are prohibited", func(
) { | |
| 557 q := q.Ancestor(rds.NewKey("thing", "wat", 0, ni
l)) | |
| 558 for i := 0; i < 100; i++ { | |
| 559 q = q.Filter("something =", 10) | |
| 560 } | |
| 561 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 562 So(qi.err.Error(), ShouldContainSubstring, "quer
y is too large") | |
| 563 }) | |
| 564 Convey("filters for __key__ that aren't keys", func() { | |
| 565 q := q.Filter("__key__ = ", 10) | |
| 566 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 567 So(qi.err.Error(), ShouldContainSubstring, "must
be a Key") | |
| 568 }) | |
| 569 Convey("multiple inequalities", func() { | |
| 570 q := q.Filter("bob > ", 19).Filter("charlie < ",
20) | |
| 571 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 572 So(qi.err.Error(), ShouldContainSubstring, "one
inequality filter") | |
| 573 }) | |
| 574 Convey("bad sort orders", func() { | |
| 575 q := q.Filter("bob > ", 19).Order("-charlie") | |
| 576 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 577 So(qi.err.Error(), ShouldContainSubstring, "firs
t sort property") | |
| 578 }) | |
| 579 Convey("kindless with non-__key__ filters", func() { | |
| 580 q := rds.NewQuery("").Filter("face <", 25.3) | |
| 581 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 582 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for non-__key__") | |
| 583 }) | |
| 584 Convey("kindless with non-__key__ orders", func() { | |
| 585 q := rds.NewQuery("").Order("face") | |
| 586 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 587 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for all orders") | |
| 588 }) | |
| 589 Convey("kindless with decending-__key__ orders", func()
{ | |
| 590 q := rds.NewQuery("").Order("-__key__") | |
| 591 qi := q.(*queryImpl).checkCorrectness("", false) | |
| 592 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for all orders") | |
| 593 }) | |
| 594 }) | |
| 595 | |
| 596 }) | |
| 597 } | |
| OLD | NEW |