Exemple #1
0
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")
			})
		})

	})
}
Exemple #2
0
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)
		})
	})
}
Exemple #3
0
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)
		})

	})
}
Exemple #4
0
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)
			})

		})
	})
}
Exemple #5
0
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)
				}
			})

		})
	})
}
Exemple #6
0
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)
			})
		})

	})
}
Exemple #7
0
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")
					}
				}
			})

		})

	})
}
Exemple #8
0
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")
		})

	})
}
Exemple #9
0
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`)
			})
		})
	})
}
Exemple #10
0
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)
			})

		})
	})
}
Exemple #11
0
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)
				}
			})

		})
	})
}
Exemple #12
0
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)
			})
		})

	})
}
Exemple #13
0
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")
					}
				}
			})

		})

	})
}
Exemple #14
0
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)
			})
		})
	})
}