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{ MakeKey("s~aid", "ns", "FailAll", 1), MakeKey("s~aid", "ns", "Ok", 1), } So(ds.DeleteMulti(keys).Error(), ShouldEqual, "DeleteMulti fail all") }) Convey("get multi error for individual failure", func() { keys := []*Key{ ds.MakeKey("Ok", 1), ds.MakeKey("Fail", 2), } So(ds.DeleteMulti(keys).Error(), ShouldEqual, "DeleteMulti fail") }) Convey("get single error when deleting a single", func() { k := ds.MakeKey("Fail", 1) So(ds.Delete(k).Error(), ShouldEqual, "DeleteMulti fail") }) }) }) }
func TestServices(t *testing.T) { t.Parallel() Convey("Test service interfaces", t, func() { c := context.Background() Convey("without adding anything", func() { So(GetRaw(c), ShouldBeNil) }) Convey("adding a basic implementation", func() { c = SetRaw(info.Set(c, fakeInfo{}), fakeService{}) Convey("lets you pull them back out", func() { So(GetRaw(c), ShouldResemble, &checkFilter{fakeService{}, "s~aid", "ns"}) }) Convey("and lets you add filters", func() { c = AddRawFilters(c, func(ic context.Context, rds RawInterface) RawInterface { return fakeFilt{rds} }) k := Get(c).NewKey("Kind", "", 1, nil) So(k.Kind(), ShouldEqual, "filteredKind") }) }) Convey("adding zero filters does nothing", func() { So(AddRawFilters(c), ShouldEqual, c) }) }) }
func TestCheckFilter(t *testing.T) { t.Parallel() Convey("Test checkFilter", t, func() { // Note that the way we have this context set up, any calls which aren't // stopped at the checkFilter will nil-pointer panic. We use this panic // behavior to indicate that the checkfilter has allowed a call to pass // through to the implementation in the tests below. In a real application // the panics observed in the tests below would actually be sucessful calls // to the implementation. c := SetRaw(info.Set(context.Background(), fakeInfo{}), fakeRDS{}) rds := GetRaw(c) // has checkFilter So(rds, ShouldNotBeNil) Convey("RunInTransaction", func() { So(rds.RunInTransaction(nil, nil).Error(), ShouldContainSubstring, "is nil") hit := false So(func() { So(rds.RunInTransaction(func(context.Context) error { hit = true return nil }, nil), ShouldBeNil) }, ShouldPanic) So(hit, ShouldBeFalse) }) Convey("Run", func() { So(rds.Run(nil, nil).Error(), ShouldContainSubstring, "query is nil") fq, err := NewQuery("sup").Finalize() So(err, ShouldBeNil) So(rds.Run(fq, nil).Error(), ShouldContainSubstring, "callback is nil") hit := false So(func() { So(rds.Run(fq, func(*Key, PropertyMap, CursorCB) error { hit = true return nil }), ShouldBeNil) }, ShouldPanic) So(hit, ShouldBeFalse) }) Convey("GetMulti", func() { So(rds.GetMulti(nil, nil, nil), ShouldBeNil) So(rds.GetMulti([]*Key{mkKey("", "", "", "")}, nil, nil).Error(), ShouldContainSubstring, "is nil") // this is in the wrong aid/ns keys := []*Key{MakeKey("wut", "wrong", "Kind", 1)} So(rds.GetMulti(keys, nil, func(pm PropertyMap, err error) error { So(pm, ShouldBeNil) So(err, ShouldEqual, ErrInvalidKey) return nil }), ShouldBeNil) keys[0] = mkKey("Kind", 1) hit := false So(func() { So(rds.GetMulti(keys, nil, func(pm PropertyMap, err error) error { hit = true return nil }), ShouldBeNil) }, ShouldPanic) So(hit, ShouldBeFalse) }) Convey("PutMulti", func() { keys := []*Key{} vals := []PropertyMap{{}} So(rds.PutMulti(keys, vals, nil).Error(), ShouldContainSubstring, "mismatched keys/vals") So(rds.PutMulti(nil, nil, nil), ShouldBeNil) keys = append(keys, mkKey("aid", "ns", "Wut", 0, "Kind", 0)) So(rds.PutMulti(keys, vals, nil).Error(), ShouldContainSubstring, "callback is nil") So(rds.PutMulti(keys, vals, func(k *Key, err error) error { So(k, ShouldBeNil) So(err, ShouldEqual, ErrInvalidKey) return nil }), ShouldBeNil) keys = []*Key{mkKey("s~aid", "ns", "Kind", 0)} vals = []PropertyMap{nil} So(rds.PutMulti(keys, vals, func(k *Key, err error) error { So(k, ShouldBeNil) So(err.Error(), ShouldContainSubstring, "nil vals entry") return nil }), ShouldBeNil) vals = []PropertyMap{{}} hit := false So(func() { So(rds.PutMulti(keys, vals, func(k *Key, err error) error { hit = true return nil }), ShouldBeNil) }, ShouldPanic) So(hit, ShouldBeFalse) }) Convey("DeleteMulti", func() { So(rds.DeleteMulti(nil, nil), ShouldBeNil) So(rds.DeleteMulti([]*Key{mkKey("", "", "", "")}, nil).Error(), ShouldContainSubstring, "is nil") So(rds.DeleteMulti([]*Key{mkKey("", "", "", "")}, func(err error) error { So(err, ShouldEqual, ErrInvalidKey) return nil }), ShouldBeNil) hit := false So(func() { So(rds.DeleteMulti([]*Key{mkKey("s~aid", "ns", "Kind", 1)}, func(error) error { hit = true return nil }), ShouldBeNil) }, ShouldPanic) So(hit, ShouldBeFalse) }) }) }
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 := NewQuery("kind").Limit(5) Convey("bad", func() { assertBadTypePanics := func(cb interface{}) { So(func() { ds.Run(q, cb) }, ShouldPanicLike, "cb does not match the required callback signature") } Convey("not a function", func() { assertBadTypePanics("I am a potato") }) Convey("nil", func() { assertBadTypePanics(nil) }) Convey("interface", func() { assertBadTypePanics(func(pls PropertyLoadSaver) {}) }) Convey("bad proto type", func() { cb := func(v int) { panic("never here!") } So(func() { ds.Run(q, cb) }, ShouldPanicLike, "invalid argument type: int") }) Convey("wrong # args", func() { assertBadTypePanics(func(v CommonStruct, _ CursorCB, _ int) { panic("never here!") }) }) Convey("wrong ret type", func() { assertBadTypePanics(func(v CommonStruct) bool { panic("never here!") }) }) Convey("wrong # rets", func() { assertBadTypePanics(func(v CommonStruct) (int, error) { panic("never here!") }) }) Convey("bad 2nd arg", func() { assertBadTypePanics(func(v CommonStruct, _ Cursor) error { panic("never here!") }) }) Convey("early abort on error", func() { q = q.Eq("$err_single", "Query fail").Eq("$err_single_idx", 3) i := 0 So(ds.Run(q, func(c CommonStruct) { i++ }), ShouldErrLike, "Query fail") So(i, ShouldEqual, 3) }) Convey("return error on serialization failure", func() { So(ds.Run(q, func(_ permaBad) { panic("never here") }).Error(), ShouldEqual, "permaBad") }) }) Convey("ok", func() { Convey("can return error to stop", func() { i := 0 So(ds.Run(q, func(c CommonStruct) error { i++ return Stop }), ShouldBeNil) So(i, ShouldEqual, 1) i = 0 So(ds.Run(q, func(c CommonStruct, _ CursorCB) error { i++ return fmt.Errorf("my error") }), ShouldErrLike, "my error") So(i, ShouldEqual, 1) }) Convey("Can optionally get cursor function", func() { i := 0 So(ds.Run(q, func(c CommonStruct, ccb CursorCB) { i++ curs, err := ccb() So(err, ShouldBeNil) So(curs.String(), ShouldEqual, "CURSOR") }), ShouldBeNil) So(i, ShouldEqual, 5) }) Convey("*S", func() { i := 0 So(ds.Run(q, func(cs *CommonStruct) { So(cs.ID, ShouldEqual, i+1) So(cs.Value, ShouldEqual, i) i++ }), ShouldBeNil) }) Convey("*P", func() { i := 0 So(ds.Run(q.Limit(12), func(fpls *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++ }), ShouldBeNil) }) Convey("*P (map)", func() { i := 0 So(ds.Run(q, func(pm *PropertyMap) { k, ok := pm.GetMeta("key") So(ok, ShouldBeTrue) So(k.(*Key).IntID(), ShouldEqual, i+1) So((*pm)["Value"][0].Value(), ShouldEqual, i) i++ }), ShouldBeNil) }) Convey("S", func() { i := 0 So(ds.Run(q, func(cs CommonStruct) { So(cs.ID, ShouldEqual, i+1) So(cs.Value, ShouldEqual, i) i++ }), ShouldBeNil) }) Convey("P", func() { i := 0 So(ds.Run(q, func(fpls FakePLS) { So(fpls.gotLoaded, ShouldBeTrue) So(fpls.IntID, ShouldEqual, i+1) So(fpls.Value, ShouldEqual, i) i++ }), ShouldBeNil) }) Convey("P (map)", func() { i := 0 So(ds.Run(q, func(pm PropertyMap) { k, ok := pm.GetMeta("key") So(ok, ShouldBeTrue) So(k.(*Key).IntID(), ShouldEqual, i+1) So(pm["Value"][0].Value(), ShouldEqual, i) i++ }), ShouldBeNil) }) Convey("Key", func() { i := 0 So(ds.Run(q, func(k *Key) { So(k.IntID(), ShouldEqual, i+1) i++ }), 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 := NewQuery("").Limit(5) Convey("bad", func() { Convey("nil target", func() { So(func() { ds.GetAll(q, (*[]PropertyMap)(nil)) }, ShouldPanicLike, "invalid GetAll dst: <nil>") }) Convey("bad type", func() { output := 100 So(func() { ds.GetAll(q, &output) }, ShouldPanicLike, "invalid argument type: expected slice, got int") }) Convey("bad type (non pointer)", func() { So(func() { ds.GetAll(q, "moo") }, ShouldPanicLike, "invalid GetAll dst: must have a ptr-to-slice") }) Convey("bad type (underspecified)", func() { output := []PropertyLoadSaver(nil) So(func() { ds.GetAll(q, &output) }, ShouldPanicLike, "invalid GetAll dst (non-concrete element type): *[]datastore.PropertyLoadSaver") }) }) 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, ok := o.GetMeta("key") So(ok, ShouldBeTrue) 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, ok := o.GetMeta("key") So(ok, ShouldBeTrue) 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 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(func() { ds.GetMulti(toGet) }, ShouldPanicLike, `field "Compy" has invalid type: complex64`) }) Convey("can't get keys", func() { fplss := []FakePLS{{failGetMeta: true}, {}} So(ds.GetMulti(fplss), ShouldErrLike, "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(func() { ds.Get(cs) }, ShouldPanicLike, "invalid Get input type (datastore.CommonStruct): not a pointer") }) Convey("get with nil is an error", func() { So(func() { ds.Get(nil) }, ShouldPanicLike, "invalid Get input type (<nil>): no type information") }) Convey("get with ptr-to-nonstruct is an error", func() { val := 100 So(func() { ds.Get(&val) }, ShouldPanicLike, "invalid Get input type (*int): does not point to a struct") }) Convey("failure to save metadata is no problem though", func() { // It just won't save the key cs := &FakePLS{IntID: 10, failSetMeta: true} So(ds.Get(cs), ShouldBeNil) }) }) 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() keys := []*Key{ds.MakeKey("Kind", 1)} So(rds.GetMulti(keys, nil, func(pm PropertyMap, err error) error { So(err, ShouldBeNil) So(pm["Value"][0].Value(), ShouldEqual, 1) return nil }), ShouldBeNil) }) Convey("but general failure to save is fine on a Get", func() { cs := &FakePLS{failSave: true, IntID: 7} So(ds.Get(cs), ShouldBeNil) }) }) }) }
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(func() { ds.PutMulti(bss) }, ShouldPanicLike, `field "Compy" has invalid type`) }) Convey("static ptr can't serialize", func() { bss := []*badStruct{{}, {}} So(func() { ds.PutMulti(bss) }, ShouldPanicLike, `field "Compy" has invalid type: complex64`) }) Convey("static bad type (non-slice)", func() { So(func() { ds.PutMulti(100) }, ShouldPanicLike, "invalid argument type: expected slice, got int") }) Convey("static bad type (slice of bad type)", func() { So(func() { ds.PutMulti([]int{}) }, ShouldPanicLike, "invalid argument type: []int") }) Convey("dynamic can't serialize", func() { fplss := []FakePLS{{failSave: true}, {}} So(ds.PutMulti(fplss), ShouldErrLike, "FakePLS.Save") }) Convey("can't get keys", func() { fplss := []FakePLS{{failGetMeta: true}, {}} So(ds.PutMulti(fplss), ShouldErrLike, "unable to extract $kind") }) Convey("get single error for RPC failure", func() { fplss := []FakePLS{{Kind: "FailAll"}, {}} So(ds.PutMulti(fplss), ShouldErrLike, "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(func() { ds.Put(cs) }, ShouldPanicLike, "invalid Put input type (datastore.CommonStruct): not a pointer") }) Convey("get with *Key is an error", func() { So(func() { ds.Get(&Key{}) }, ShouldPanicLike, "invalid Get input type (*datastore.Key): not user datatype") }) Convey("struct with no $kind is an error", func() { s := MGSWithNoKind{} So(ds.Put(&s), ShouldErrLike, "unable to extract $kind") }) Convey("struct with invalid but non-nil key is an error", func() { type BadParent struct { ID int64 `gae:"$id"` Parent *Key `gae:"$parent"` } // having an Incomplete parent makes an invalid key bp := &BadParent{ID: 1, Parent: ds.MakeKey("Something", 0)} So(ds.Put(bp), ShouldErrLike, ErrInvalidKey) }) }) 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 { So(pms[i].SetMeta("id", int64(200)), ShouldBeTrue) } } 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("s~aid:ns:/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 { So(pms[i].SetMeta("id", int64(200)), ShouldBeTrue) } } 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("s~aid:ns:/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, "s~aid:ns:/Pmap,3") case 3: So(ds.KeyForObj(*ifs[i].(*PropertyMap)).String(), ShouldEqual, "s~aid:ns:/Pmap,4") } } }) }) }) }
func TestContextAccess(t *testing.T) { t.Parallel() // p is a function which recovers an error and then immediately panics with // the contained string. It's defer'd in each test so that we can use the // ShouldPanicWith assertion (which does an == comparison and not // a reflect.DeepEquals comparison). p := func() { panic(recover().(error).Error()) } Convey("Context Access", t, func() { c := context.Background() Convey("blank", func() { So(dsS.GetRaw(c), ShouldBeNil) So(mcS.GetRaw(c), ShouldBeNil) So(tqS.GetRaw(c), ShouldBeNil) So(infoS.Get(c), ShouldBeNil) }) // needed for everything else c = infoS.Set(c, Info()) Convey("Info", func() { So(infoS.Get(c), ShouldNotBeNil) So(func() { defer p() infoS.Get(c).Datacenter() }, ShouldPanicWith, "dummy: method Info.Datacenter is not implemented") }) Convey("Datastore", func() { c = dsS.SetRaw(c, Datastore()) So(dsS.Get(c), ShouldNotBeNil) So(func() { defer p() _, _ = dsS.Get(c).DecodeCursor("wut") }, ShouldPanicWith, "dummy: method Datastore.DecodeCursor is not implemented") }) Convey("Memcache", func() { c = mcS.SetRaw(c, Memcache()) So(mcS.Get(c), ShouldNotBeNil) So(func() { defer p() _ = mcS.Get(c).Add(nil) }, ShouldPanicWith, "dummy: method Memcache.AddMulti is not implemented") }) Convey("TaskQueue", func() { c = tqS.SetRaw(c, TaskQueue()) So(tqS.Get(c), ShouldNotBeNil) So(func() { defer p() _ = tqS.Get(c).Purge("") }, ShouldPanicWith, "dummy: method TaskQueue.Purge is not implemented") }) Convey("User", func() { c = userS.Set(c, User()) So(userS.Get(c), ShouldNotBeNil) So(func() { defer p() _ = userS.Get(c).IsAdmin() }, ShouldPanicWith, "dummy: method User.IsAdmin is not implemented") }) Convey("Mail", func() { c = mailS.Set(c, Mail()) So(mailS.Get(c), ShouldNotBeNil) So(func() { defer p() _ = mailS.Get(c).Send(nil) }, ShouldPanicWith, "dummy: method Mail.Send is not implemented") }) }) }
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.MakeKey("Hello", "world") 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, `s~aid:ns:/SuperKind,"wut"`) }) Convey("struct containing $id and $parent", func() { So(ds.KeyForObj(&CommonStruct{ID: 4}).String(), ShouldEqual, `s~aid:ns:/CommonStruct,4`) So(ds.KeyForObj(&CommonStruct{ID: 4, Parent: k}).String(), ShouldEqual, `s~aid:ns:/Hello,"world"/CommonStruct,4`) }) Convey("a propmap with $key", func() { pm := PropertyMap{} So(pm.SetMeta("key", k), ShouldBeTrue) So(ds.KeyForObj(pm).String(), ShouldEqual, `s~aid:ns:/Hello,"world"`) }) Convey("a propmap with $id, $kind, $parent", func() { pm := PropertyMap{} So(pm.SetMeta("id", 100), ShouldBeTrue) So(pm.SetMeta("kind", "Sup"), ShouldBeTrue) So(ds.KeyForObj(pm).String(), ShouldEqual, `s~aid:ns:/Sup,100`) So(pm.SetMeta("parent", k), ShouldBeTrue) So(ds.KeyForObj(pm).String(), ShouldEqual, `s~aid:ns:/Hello,"world"/Sup,100`) }) Convey("a pls with $id, $parent", func() { pls := GetPLS(&CommonStruct{ID: 1}) So(ds.KeyForObj(pls).String(), ShouldEqual, `s~aid:ns:/CommonStruct,1`) So(pls.SetMeta("parent", k), ShouldBeTrue) So(ds.KeyForObj(pls).String(), ShouldEqual, `s~aid:ns:/Hello,"world"/CommonStruct,1`) }) Convey("can see if things exist", func() { e, err := ds.Exists(k) So(err, ShouldBeNil) So(e, ShouldBeTrue) e, err = ds.Exists(ds.MakeKey("DNE", "nope")) So(err, ShouldBeNil) So(e, ShouldBeFalse) _, err = ds.Exists(ds.MakeKey("Fail", "boom")) So(err, ShouldErrLike, "GetMulti fail") }) }) Convey("bad", func() { Convey("a propmap without $kind", func() { pm := PropertyMap{} So(pm.SetMeta("id", 100), ShouldBeTrue) So(func() { ds.KeyForObj(pm) }, ShouldPanic) }) Convey("a bad object", func() { type BadObj struct { ID int64 `gae:"$id"` NonSerializableField complex64 } So(func() { ds.KeyForObjErr(&BadObj{ID: 1}) }, ShouldPanicLike, `field "NonSerializableField" has invalid type: complex64`) }) }) }) }
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() { assertBadTypePanics := func(cb interface{}) { defer func() { err, _ := recover().(error) So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "cb does not match the required callback signature") }() ds.Run(q, cb) } Convey("not a function", func() { assertBadTypePanics("I am a potato") }) Convey("bad proto type", func() { assertBadTypePanics(func(v int, _ CursorCB) bool { panic("never here!") }) }) Convey("wrong # args", func() { assertBadTypePanics(func(v CommonStruct, _ CursorCB) { panic("never here!") }) }) Convey("wrong ret type", func() { assertBadTypePanics(func(v CommonStruct, _ CursorCB) error { panic("never here!") }) }) Convey("bad 2nd arg", func() { assertBadTypePanics(func(v CommonStruct, _ Cursor) bool { panic("never here!") }) }) Convey("early abort on error", func() { rq := q.(*fakeQuery).Fail(3) i := 0 So(ds.Run(rq, func(c CommonStruct, _ CursorCB) bool { i++ return true }).Error(), ShouldEqual, "Query fail") So(i, ShouldEqual, 3) }) Convey("return error on serialization failure", func() { So(ds.Run(q, func(_ permaBad, _ CursorCB) bool { panic("never here") }).Error(), ShouldEqual, "permaBad") }) }) Convey("ok", func() { Convey("*S", func() { i := 0 So(ds.Run(q, func(cs *CommonStruct, _ CursorCB) bool { 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), func(fpls *FakePLS, _ CursorCB) bool { 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, func(pm *PropertyMap, _ CursorCB) bool { 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, func(cs CommonStruct, _ CursorCB) bool { So(cs.ID, ShouldEqual, i+1) So(cs.Value, ShouldEqual, i) i++ return true }), ShouldBeNil) }) Convey("P", func() { i := 0 So(ds.Run(q, func(fpls FakePLS, _ CursorCB) bool { 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, func(pm PropertyMap, _ CursorCB) bool { 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, func(k Key, _ CursorCB) bool { So(k.IntID(), ShouldEqual, i+1) i++ return true }), 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 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("failure to save metadata is an issue too", func() { cs := &FakePLS{failSave: true} So(ds.Get(cs).Error(), ShouldContainSubstring, "FakePLS.Save") }) }) 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() keys := []Key{rds.NewKey("Kind", "", 1, nil)} So(rds.GetMulti(keys, nil, func(pm PropertyMap, err error) { So(err, ShouldBeNil) So(pm["Value"][0].Value(), ShouldEqual, 1) }), ShouldBeNil) }) }) }) }
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 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) }) }) }) }