Index: service/datastore/datastore_test.go |
diff --git a/service/datastore/datastore_test.go b/service/datastore/datastore_test.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..383a39ef0f1fd713522a77e9ce2369f5b37c22e4 |
--- /dev/null |
+++ b/service/datastore/datastore_test.go |
@@ -0,0 +1,861 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// adapted from github.com/golang/appengine/datastore |
+ |
+package datastore |
+ |
+import ( |
+ "fmt" |
+ "testing" |
+ |
+ "github.com/luci/gae/service/info" |
+ "github.com/luci/luci-go/common/errors" |
+ . "github.com/smartystreets/goconvey/convey" |
+ "golang.org/x/net/context" |
+) |
+ |
+func fakeDatastoreFactory(c context.Context) RawInterface { |
+ i := info.Get(c) |
+ return &fakeDatastore{ |
+ aid: i.AppID(), |
+ ns: i.GetNamespace(), |
+ } |
+} |
+ |
+type fakeQuery struct { |
+ Query |
+ |
+ limit int |
+ |
+ err error |
+ errSingle error |
+ errSingleIdx int |
+} |
+ |
+func (q *fakeQuery) KeysOnly() Query { |
+ return q |
+} |
+ |
+func (q *fakeQuery) Limit(i int) Query { |
+ q.limit = i |
+ return q |
+} |
+ |
+func (q *fakeQuery) FailAll() Query { |
+ q.err = errors.New("Query fail all") |
+ return q |
+} |
+ |
+func (q *fakeQuery) Fail(i int) Query { |
+ q.errSingleIdx = i |
+ q.errSingle = errors.New("Query fail") |
+ return q |
+} |
+ |
+type fakeDatastore struct { |
+ RawInterface |
+ aid string |
+ ns string |
+} |
+ |
+func (f *fakeDatastore) NewKey(kind, stringID string, intID int64, parent Key) Key { |
+ return NewKey(f.aid, f.ns, kind, stringID, intID, parent) |
+} |
+ |
+func (f *fakeDatastore) NewQuery(string) Query { |
+ return &fakeQuery{} |
+} |
+ |
+func (f *fakeDatastore) Run(q Query, cb RawRunCB) error { |
+ rq := q.(*fakeQuery) |
+ if rq.err != nil { |
+ return rq.err |
+ } |
+ for i := 0; i < rq.limit; i++ { |
+ if rq.errSingle != nil && i == rq.errSingleIdx { |
+ return rq.errSingle |
+ } else { |
+ k := f.NewKey("Kind", "", int64(i+1), nil) |
+ if i == 10 { |
+ k = f.NewKey("Kind", "eleven", 0, nil) |
+ } |
+ pm := PropertyMap{"Value": {MkProperty(i)}} |
+ if !cb(k, pm, nil) { |
+ break |
+ } |
+ } |
+ } |
+ return nil |
+} |
+ |
+func (f *fakeDatastore) PutMulti(keys []Key, vals []PropertyMap, cb PutMultiCB) error { |
+ if keys[0].Kind() == "FailAll" { |
+ return errors.New("PutMulti fail all") |
+ } |
+ assertExtra := false |
+ if _, err := vals[0].GetMeta("assertExtra"); err == nil { |
+ assertExtra = true |
+ } |
+ for i, k := range keys { |
+ err := error(nil) |
+ if k.Kind() == "Fail" { |
+ err = errors.New("PutMulti fail") |
+ } else { |
+ So(vals[i]["Value"], ShouldResemble, []Property{MkProperty(i)}) |
+ if assertExtra { |
+ So(vals[i]["Extra"], ShouldResemble, []Property{MkProperty("whoa")}) |
+ } |
+ if KeyIncomplete(k) { |
+ k = NewKey(k.AppID(), k.Namespace(), k.Kind(), "", int64(i+1), k.Parent()) |
+ } |
+ } |
+ cb(k, err) |
+ } |
+ return nil |
+} |
+ |
+func (f *fakeDatastore) GetMulti(keys []Key, cb GetMultiCB) error { |
+ if keys[0].Kind() == "FailAll" { |
+ return errors.New("GetMulti fail all") |
+ } |
+ for i, k := range keys { |
+ if k.Kind() == "Fail" { |
+ cb(nil, errors.New("GetMulti fail")) |
+ } else { |
+ cb(PropertyMap{"Value": {MkProperty(i + 1)}}, nil) |
+ } |
+ } |
+ return nil |
+} |
+ |
+func (f *fakeDatastore) DeleteMulti(keys []Key, cb DeleteMultiCB) error { |
+ if keys[0].Kind() == "FailAll" { |
+ return errors.New("DeleteMulti fail all") |
+ } |
+ for _, k := range keys { |
+ if k.Kind() == "Fail" { |
+ cb(errors.New("DeleteMulti fail")) |
+ } else { |
+ cb(nil) |
+ } |
+ } |
+ return nil |
+} |
+ |
+type badStruct struct { |
+ ID int64 `gae:"$id"` |
+ Compy complex64 // bad type |
+} |
+ |
+type CommonStruct struct { |
+ ID int64 `gae:"$id"` |
+ Parent Key `gae:"$parent"` |
+ |
+ Value int64 |
+} |
+ |
+type permaBad struct { |
+ PropertyLoadSaver |
+} |
+ |
+func (f *permaBad) Load(pm PropertyMap) error { |
+ return errors.New("permaBad") |
+} |
+ |
+type FakePLS struct { |
+ IntID int64 |
+ StringID string |
+ Kind string |
+ |
+ Value int64 |
+ gotLoaded bool |
+ |
+ failGetMeta bool |
+ failLoad bool |
+ failProblem bool |
+ failSave bool |
+ failSaveMeta bool |
+} |
+ |
+func (f *FakePLS) Load(pm PropertyMap) error { |
+ if f.failLoad { |
+ return errors.New("FakePLS.Load") |
+ } |
+ f.gotLoaded = true |
+ f.Value = pm["Value"][0].Value().(int64) |
+ return nil |
+} |
+ |
+func (f *FakePLS) Save(withMeta bool) (PropertyMap, error) { |
+ if f.failSave { |
+ return nil, errors.New("FakePLS.Save") |
+ } |
+ ret := PropertyMap{ |
+ "Value": {MkProperty(f.Value)}, |
+ "Extra": {MkProperty("whoa")}, |
+ } |
+ if withMeta { |
+ id, _ := f.GetMeta("id") |
+ ret.SetMeta("id", id) |
+ if f.Kind == "" { |
+ ret.SetMeta("kind", "FakePLS") |
+ } else { |
+ ret.SetMeta("kind", f.Kind) |
+ } |
+ ret.SetMeta("assertExtra", true) |
+ } |
+ return ret, nil |
+} |
+ |
+func (f *FakePLS) GetMeta(key string) (interface{}, error) { |
+ if f.failGetMeta { |
+ return nil, errors.New("FakePLS.GetMeta") |
+ } |
+ switch key { |
+ case "id": |
+ if f.StringID != "" { |
+ return f.StringID, nil |
+ } |
+ return f.IntID, nil |
+ case "kind": |
+ if f.Kind == "" { |
+ return "FakePLS", nil |
+ } |
+ return f.Kind, nil |
+ } |
+ return nil, ErrMetaFieldUnset |
+} |
+ |
+func (f *FakePLS) SetMeta(key string, val interface{}) error { |
+ if f.failSaveMeta { |
+ return errors.New("FakePL.SetMeta") |
+ } |
+ if key == "id" { |
+ switch x := val.(type) { |
+ case int64: |
+ f.IntID = x |
+ case string: |
+ f.StringID = x |
+ } |
+ return nil |
+ } |
+ if key == "kind" { |
+ f.Kind = val.(string) |
+ return nil |
+ } |
+ return ErrMetaFieldUnset |
+} |
+ |
+func (f *FakePLS) Problem() error { |
+ if f.failProblem { |
+ return errors.New("FakePLS.Problem") |
+ } |
+ return nil |
+} |
+ |
+func TestKeyForObj(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test interface.KeyForObj", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ |
+ k := ds.NewKey("Hello", "world", 0, nil) |
+ |
+ Convey("good", func() { |
+ Convey("struct containing $key", func() { |
+ type keyStruct struct { |
+ Key Key `gae:"$key"` |
+ } |
+ |
+ ks := &keyStruct{k} |
+ So(ds.KeyForObj(ks), ShouldEqual, k) |
+ }) |
+ |
+ Convey("struct containing default $id and $kind", func() { |
+ type idStruct struct { |
+ id string `gae:"$id,wut"` |
+ knd string `gae:"$kind,SuperKind"` |
+ } |
+ |
+ So(ds.KeyForObj(&idStruct{}).String(), ShouldEqual, `/SuperKind,wut`) |
+ }) |
+ |
+ Convey("struct containing $id and $parent", func() { |
+ So(ds.KeyForObj(&CommonStruct{ID: 4}).String(), ShouldEqual, `/CommonStruct,4`) |
+ |
+ So(ds.KeyForObj(&CommonStruct{ID: 4, Parent: k}).String(), ShouldEqual, `/Hello,world/CommonStruct,4`) |
+ }) |
+ |
+ Convey("a propmap with $key", func() { |
+ pm := PropertyMap{} |
+ pm.SetMeta("key", k) |
+ So(ds.KeyForObj(pm).String(), ShouldEqual, `/Hello,world`) |
+ }) |
+ |
+ Convey("a propmap with $id, $kind, $parent", func() { |
+ pm := PropertyMap{} |
+ pm.SetMeta("id", 100) |
+ pm.SetMeta("kind", "Sup") |
+ So(ds.KeyForObj(pm).String(), ShouldEqual, `/Sup,100`) |
+ |
+ pm.SetMeta("parent", k) |
+ So(ds.KeyForObj(pm).String(), ShouldEqual, `/Hello,world/Sup,100`) |
+ }) |
+ |
+ Convey("a pls with $id, $parent", func() { |
+ pls := GetPLS(&CommonStruct{ID: 1}) |
+ So(ds.KeyForObj(pls).String(), ShouldEqual, `/CommonStruct,1`) |
+ |
+ pls.SetMeta("parent", k) |
+ So(ds.KeyForObj(pls).String(), ShouldEqual, `/Hello,world/CommonStruct,1`) |
+ }) |
+ |
+ }) |
+ |
+ Convey("bad", func() { |
+ Convey("a propmap without $kind", func() { |
+ pm := PropertyMap{} |
+ pm.SetMeta("id", 100) |
+ So(func() { ds.KeyForObj(pm) }, ShouldPanic) |
+ }) |
+ }) |
+ }) |
+} |
+ |
+func TestPut(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test Put/PutMulti", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ |
+ Convey("bad", func() { |
+ Convey("static can't serialize", func() { |
+ bss := []badStruct{{}, {}} |
+ So(ds.PutMulti(bss).Error(), ShouldContainSubstring, "invalid PutMulti input") |
+ }) |
+ |
+ Convey("static ptr can't serialize", func() { |
+ bss := []*badStruct{{}, {}} |
+ So(ds.PutMulti(bss).Error(), ShouldContainSubstring, "invalid PutMulti input") |
+ }) |
+ |
+ Convey("static bad type (non-slice)", func() { |
+ So(ds.PutMulti(100).Error(), ShouldContainSubstring, "invalid PutMulti input") |
+ }) |
+ |
+ Convey("static bad type (slice of bad type)", func() { |
+ So(ds.PutMulti([]int{}).Error(), ShouldContainSubstring, "invalid PutMulti input") |
+ }) |
+ |
+ Convey("dynamic can't serialize", func() { |
+ fplss := []FakePLS{{failSave: true}, {}} |
+ So(ds.PutMulti(fplss).Error(), ShouldContainSubstring, "FakePLS.Save") |
+ }) |
+ |
+ Convey("can't get keys", func() { |
+ fplss := []FakePLS{{failGetMeta: true}, {}} |
+ So(ds.PutMulti(fplss).Error(), ShouldContainSubstring, "unable to extract $kind") |
+ }) |
+ |
+ Convey("get single error for RPC failure", func() { |
+ fplss := []FakePLS{{Kind: "FailAll"}, {}} |
+ So(ds.PutMulti(fplss).Error(), ShouldEqual, "PutMulti fail all") |
+ }) |
+ |
+ Convey("get multi error for individual failures", func() { |
+ fplss := []FakePLS{{}, {Kind: "Fail"}} |
+ So(ds.PutMulti(fplss), ShouldResemble, errors.MultiError{nil, errors.New("PutMulti fail")}) |
+ }) |
+ |
+ Convey("put with non-modifyable type is an error", func() { |
+ cs := CommonStruct{} |
+ So(ds.Put(cs).Error(), ShouldContainSubstring, "invalid Put input type") |
+ }) |
+ }) |
+ |
+ Convey("ok", func() { |
+ Convey("[]S", func() { |
+ css := make([]CommonStruct, 7) |
+ for i := range css { |
+ if i == 4 { |
+ css[i].ID = 200 |
+ } |
+ css[i].Value = int64(i) |
+ } |
+ So(ds.PutMulti(css), ShouldBeNil) |
+ for i, cs := range css { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(cs.ID, ShouldEqual, expect) |
+ } |
+ }) |
+ |
+ Convey("[]*S", func() { |
+ css := make([]*CommonStruct, 7) |
+ for i := range css { |
+ css[i] = &CommonStruct{Value: int64(i)} |
+ if i == 4 { |
+ css[i].ID = 200 |
+ } |
+ } |
+ So(ds.PutMulti(css), ShouldBeNil) |
+ for i, cs := range css { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(cs.ID, ShouldEqual, expect) |
+ } |
+ |
+ s := &CommonStruct{} |
+ So(ds.Put(s), ShouldBeNil) |
+ So(s.ID, ShouldEqual, 1) |
+ }) |
+ |
+ Convey("[]P", func() { |
+ fplss := make([]FakePLS, 7) |
+ for i := range fplss { |
+ fplss[i].Value = int64(i) |
+ if i == 4 { |
+ fplss[i].IntID = int64(200) |
+ } |
+ } |
+ So(ds.PutMulti(fplss), ShouldBeNil) |
+ for i, fpls := range fplss { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(fpls.IntID, ShouldEqual, expect) |
+ } |
+ |
+ pm := PropertyMap{"Value": {MkProperty(0)}, "$kind": {MkPropertyNI("Pmap")}} |
+ So(ds.Put(pm), ShouldBeNil) |
+ So(ds.KeyForObj(pm).IntID(), ShouldEqual, 1) |
+ }) |
+ |
+ Convey("[]P (map)", func() { |
+ pms := make([]PropertyMap, 7) |
+ for i := range pms { |
+ pms[i] = PropertyMap{ |
+ "$kind": {MkProperty("Pmap")}, |
+ "Value": {MkProperty(i)}, |
+ } |
+ if i == 4 { |
+ pms[i].SetMeta("id", int64(200)) |
+ } |
+ } |
+ So(ds.PutMulti(pms), ShouldBeNil) |
+ for i, pm := range pms { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(ds.KeyForObj(pm).String(), ShouldEqual, fmt.Sprintf("/Pmap,%d", expect)) |
+ } |
+ }) |
+ |
+ Convey("[]*P", func() { |
+ fplss := make([]*FakePLS, 7) |
+ for i := range fplss { |
+ fplss[i] = &FakePLS{Value: int64(i)} |
+ if i == 4 { |
+ fplss[i].IntID = int64(200) |
+ } |
+ } |
+ So(ds.PutMulti(fplss), ShouldBeNil) |
+ for i, fpls := range fplss { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(fpls.IntID, ShouldEqual, expect) |
+ } |
+ }) |
+ |
+ Convey("[]*P (map)", func() { |
+ pms := make([]*PropertyMap, 7) |
+ for i := range pms { |
+ pms[i] = &PropertyMap{ |
+ "$kind": {MkProperty("Pmap")}, |
+ "Value": {MkProperty(i)}, |
+ } |
+ if i == 4 { |
+ pms[i].SetMeta("id", int64(200)) |
+ } |
+ } |
+ So(ds.PutMulti(pms), ShouldBeNil) |
+ for i, pm := range pms { |
+ expect := int64(i + 1) |
+ if i == 4 { |
+ expect = 200 |
+ } |
+ So(ds.KeyForObj(*pm).String(), ShouldEqual, fmt.Sprintf("/Pmap,%d", expect)) |
+ } |
+ }) |
+ |
+ Convey("[]I", func() { |
+ ifs := []interface{}{ |
+ &CommonStruct{Value: 0}, |
+ &FakePLS{Value: 1}, |
+ PropertyMap{"Value": {MkProperty(2)}, "$kind": {MkPropertyNI("Pmap")}}, |
+ &PropertyMap{"Value": {MkProperty(3)}, "$kind": {MkPropertyNI("Pmap")}}, |
+ } |
+ So(ds.PutMulti(ifs), ShouldBeNil) |
+ for i := range ifs { |
+ switch i { |
+ case 0: |
+ So(ifs[i].(*CommonStruct).ID, ShouldEqual, 1) |
+ case 1: |
+ fpls := ifs[i].(*FakePLS) |
+ So(fpls.IntID, ShouldEqual, 2) |
+ case 2: |
+ So(ds.KeyForObj(ifs[i].(PropertyMap)).String(), ShouldEqual, "/Pmap,3") |
+ case 3: |
+ So(ds.KeyForObj(*ifs[i].(*PropertyMap)).String(), ShouldEqual, "/Pmap,4") |
+ } |
+ } |
+ }) |
+ |
+ }) |
+ |
+ }) |
+} |
+ |
+func TestDelete(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test Delete/DeleteMulti", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ So(ds, ShouldNotBeNil) |
+ |
+ Convey("bad", func() { |
+ Convey("get single error for RPC failure", func() { |
+ keys := []Key{ |
+ NewKey("aid", "ns", "FailAll", "", 1, nil), |
+ NewKey("aid", "ns", "Ok", "", 1, nil), |
+ } |
+ So(ds.DeleteMulti(keys).Error(), ShouldEqual, "DeleteMulti fail all") |
+ }) |
+ |
+ Convey("get multi error for individual failure", func() { |
+ keys := []Key{ |
+ ds.NewKey("Ok", "", 1, nil), |
+ ds.NewKey("Fail", "", 2, nil), |
+ } |
+ So(ds.DeleteMulti(keys).Error(), ShouldEqual, "DeleteMulti fail") |
+ }) |
+ |
+ Convey("get single error when deleting a single", func() { |
+ k := ds.NewKey("Fail", "", 1, nil) |
+ So(ds.Delete(k).Error(), ShouldEqual, "DeleteMulti fail") |
+ }) |
+ }) |
+ |
+ }) |
+} |
+ |
+func TestGet(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test Get/GetMulti", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ So(ds, ShouldNotBeNil) |
+ |
+ Convey("bad", func() { |
+ Convey("static can't serialize", func() { |
+ toGet := []badStruct{{}, {}} |
+ So(ds.GetMulti(toGet).Error(), ShouldContainSubstring, "invalid GetMulti input") |
+ }) |
+ |
+ Convey("can't get keys", func() { |
+ fplss := []FakePLS{{failGetMeta: true}, {}} |
+ So(ds.GetMulti(fplss).Error(), ShouldContainSubstring, "unable to extract $kind") |
+ }) |
+ |
+ Convey("get single error for RPC failure", func() { |
+ fplss := []FakePLS{ |
+ {IntID: 1, Kind: "FailAll"}, |
+ {IntID: 2}, |
+ } |
+ So(ds.GetMulti(fplss).Error(), ShouldEqual, "GetMulti fail all") |
+ }) |
+ |
+ Convey("get multi error for individual failures", func() { |
+ fplss := []FakePLS{{IntID: 1}, {IntID: 2, Kind: "Fail"}} |
+ So(ds.GetMulti(fplss), ShouldResemble, errors.MultiError{nil, errors.New("GetMulti fail")}) |
+ }) |
+ |
+ Convey("get with non-modifiable type is an error", func() { |
+ cs := CommonStruct{} |
+ So(ds.Get(cs).Error(), ShouldContainSubstring, "invalid Get input type") |
+ }) |
+ }) |
+ |
+ Convey("ok", func() { |
+ Convey("Get", func() { |
+ cs := &CommonStruct{ID: 1} |
+ So(ds.Get(cs), ShouldBeNil) |
+ So(cs.Value, ShouldEqual, 1) |
+ }) |
+ |
+ Convey("Raw access too", func() { |
+ rds := ds.Raw() |
+ So(rds.GetMulti([]Key{rds.NewKey("Kind", "", 1, nil)}, func(pm PropertyMap, err error) { |
+ So(err, ShouldBeNil) |
+ So(pm["Value"][0].Value(), ShouldEqual, 1) |
+ }), ShouldBeNil) |
+ }) |
+ }) |
+ |
+ }) |
+} |
+ |
+func TestGetAll(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test GetAll", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ So(ds, ShouldNotBeNil) |
+ |
+ q := ds.NewQuery("").Limit(5) |
+ |
+ Convey("bad", func() { |
+ Convey("nil target", func() { |
+ So(ds.GetAll(q, (*[]PropertyMap)(nil)).Error(), ShouldContainSubstring, "dst: <nil>") |
+ }) |
+ |
+ Convey("bad type", func() { |
+ output := 100 |
+ So(ds.GetAll(q, &output).Error(), ShouldContainSubstring, "invalid GetAll input type") |
+ }) |
+ |
+ Convey("bad type (non pointer)", func() { |
+ So(ds.GetAll(q, "moo").Error(), ShouldContainSubstring, "must have a ptr-to-slice") |
+ }) |
+ |
+ Convey("bad type (underspecified)", func() { |
+ output := []PropertyLoadSaver(nil) |
+ So(ds.GetAll(q, &output).Error(), ShouldContainSubstring, "invalid GetAll input type") |
+ }) |
+ }) |
+ |
+ Convey("ok", func() { |
+ Convey("*[]S", func() { |
+ output := []CommonStruct(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, o := range output { |
+ So(o.ID, ShouldEqual, i+1) |
+ So(o.Value, ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]*S", func() { |
+ output := []*CommonStruct(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, o := range output { |
+ So(o.ID, ShouldEqual, i+1) |
+ So(o.Value, ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]P", func() { |
+ output := []FakePLS(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, o := range output { |
+ So(o.gotLoaded, ShouldBeTrue) |
+ So(o.IntID, ShouldEqual, i+1) |
+ So(o.Value, ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]P (map)", func() { |
+ output := []PropertyMap(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, o := range output { |
+ k, err := o.GetMeta("key") |
+ So(err, ShouldBeNil) |
+ So(k.(Key).IntID(), ShouldEqual, i+1) |
+ So(o["Value"][0].Value().(int64), ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]*P", func() { |
+ output := []*FakePLS(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, o := range output { |
+ So(o.gotLoaded, ShouldBeTrue) |
+ So(o.IntID, ShouldEqual, i+1) |
+ So(o.Value, ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]*P (map)", func() { |
+ output := []*PropertyMap(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, op := range output { |
+ o := *op |
+ k, err := o.GetMeta("key") |
+ So(err, ShouldBeNil) |
+ So(k.(Key).IntID(), ShouldEqual, i+1) |
+ So(o["Value"][0].Value().(int64), ShouldEqual, i) |
+ } |
+ }) |
+ |
+ Convey("*[]Key", func() { |
+ output := []Key(nil) |
+ So(ds.GetAll(q, &output), ShouldBeNil) |
+ So(len(output), ShouldEqual, 5) |
+ for i, k := range output { |
+ So(k.IntID(), ShouldEqual, i+1) |
+ } |
+ }) |
+ |
+ }) |
+ }) |
+} |
+ |
+func TestRun(t *testing.T) { |
+ t.Parallel() |
+ |
+ Convey("Test Run", t, func() { |
+ c := info.Set(context.Background(), fakeInfo{}) |
+ c = SetRawFactory(c, fakeDatastoreFactory) |
+ ds := Get(c) |
+ So(ds, ShouldNotBeNil) |
+ |
+ q := ds.NewQuery("").Limit(5) |
+ |
+ Convey("bad", func() { |
+ Convey("bad proto type", func() { |
+ So(ds.Run(q, 100, func(obj interface{}, _ func() (Cursor, error)) bool { |
+ panic("never here!") |
+ }).Error(), ShouldContainSubstring, "invalid Run proto type") |
+ }) |
+ |
+ Convey("early abort on error", func() { |
+ rq := q.(*fakeQuery).Fail(3) |
+ i := 0 |
+ So(ds.Run(rq, CommonStruct{}, func(_ interface{}, _ func() (Cursor, error)) bool { |
+ i++ |
+ return true |
+ }).Error(), ShouldEqual, "Query fail") |
+ So(i, ShouldEqual, 3) |
+ }) |
+ |
+ Convey("return error on serialization failure", func() { |
+ So(ds.Run(q, permaBad{}, func(_ interface{}, _ func() (Cursor, error)) bool { |
+ panic("never here") |
+ }).Error(), ShouldEqual, "permaBad") |
+ }) |
+ }) |
+ |
+ Convey("ok", func() { |
+ Convey("*S", func() { |
+ i := 0 |
+ So(ds.Run(q, (*CommonStruct)(nil), func(obj interface{}, _ func() (Cursor, error)) bool { |
+ cs := obj.(*CommonStruct) |
+ So(cs.ID, ShouldEqual, i+1) |
+ So(cs.Value, ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("*P", func() { |
+ i := 0 |
+ So(ds.Run(q.Limit(12), (*FakePLS)(nil), func(obj interface{}, _ func() (Cursor, error)) bool { |
+ fpls := obj.(*FakePLS) |
+ So(fpls.gotLoaded, ShouldBeTrue) |
+ if i == 10 { |
+ So(fpls.StringID, ShouldEqual, "eleven") |
+ } else { |
+ So(fpls.IntID, ShouldEqual, i+1) |
+ } |
+ So(fpls.Value, ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("*P (map)", func() { |
+ i := 0 |
+ So(ds.Run(q, (*PropertyMap)(nil), func(obj interface{}, _ func() (Cursor, error)) bool { |
+ pm := *obj.(*PropertyMap) |
+ k, err := pm.GetMeta("key") |
+ So(err, ShouldBeNil) |
+ So(k.(Key).IntID(), ShouldEqual, i+1) |
+ So(pm["Value"][0].Value(), ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("S", func() { |
+ i := 0 |
+ So(ds.Run(q, CommonStruct{}, func(obj interface{}, _ func() (Cursor, error)) bool { |
+ cs := obj.(CommonStruct) |
+ So(cs.ID, ShouldEqual, i+1) |
+ So(cs.Value, ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("P", func() { |
+ i := 0 |
+ So(ds.Run(q, FakePLS{}, func(obj interface{}, _ func() (Cursor, error)) bool { |
+ fpls := obj.(FakePLS) |
+ So(fpls.gotLoaded, ShouldBeTrue) |
+ So(fpls.IntID, ShouldEqual, i+1) |
+ So(fpls.Value, ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("P (map)", func() { |
+ i := 0 |
+ So(ds.Run(q, (PropertyMap)(nil), func(obj interface{}, _ func() (Cursor, error)) bool { |
+ pm := obj.(PropertyMap) |
+ k, err := pm.GetMeta("key") |
+ So(err, ShouldBeNil) |
+ So(k.(Key).IntID(), ShouldEqual, i+1) |
+ So(pm["Value"][0].Value(), ShouldEqual, i) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ Convey("*Key", func() { |
+ i := 0 |
+ So(ds.Run(q, (*Key)(nil), func(obj interface{}, _ func() (Cursor, error)) bool { |
+ So(obj.(Key).IntID(), ShouldEqual, i+1) |
+ i++ |
+ return true |
+ }), ShouldBeNil) |
+ }) |
+ |
+ }) |
+ }) |
+} |