| Index: go/src/infra/gae/libs/gae/helper/datastore_test.go
|
| diff --git a/go/src/infra/gae/libs/gae/helper/datastore_test.go b/go/src/infra/gae/libs/gae/helper/datastore_test.go
|
| index 9fd3b49de82e4d75d2ac26ccb1f6e65ef84ed58d..43f9ee8c6f861959310c5929999b207b83b3d182 100644
|
| --- a/go/src/infra/gae/libs/gae/helper/datastore_test.go
|
| +++ b/go/src/infra/gae/libs/gae/helper/datastore_test.go
|
| @@ -22,18 +22,10 @@ import (
|
| . "github.com/smartystreets/goconvey/convey"
|
| )
|
|
|
| -func mp(value interface{}, noIndexes ...bool) (ret gae.DSProperty) {
|
| - ni := false
|
| - if len(noIndexes) > 1 {
|
| - panic("YOU FOOL! YOU CANNOT HOPE TO PASS THAT MANY VALUES!")
|
| - } else if len(noIndexes) == 1 {
|
| - ni = noIndexes[0]
|
| - }
|
| - if err := ret.SetValue(value, ni); err != nil {
|
| - panic(err)
|
| - }
|
| - return
|
| -}
|
| +var (
|
| + mp = gae.MkDSProperty
|
| + mpNI = gae.MkDSPropertyNI
|
| +)
|
|
|
| const testAppID = "testApp"
|
|
|
| @@ -347,7 +339,7 @@ type MismatchTypes struct {
|
| }
|
|
|
| type BadSpecial struct {
|
| - ID int `gae:"$id"`
|
| + ID int64 `gae:"$id"`
|
| id string `gae:"$id"`
|
| }
|
|
|
| @@ -357,19 +349,16 @@ type Doubler struct {
|
| B bool
|
| }
|
|
|
| -func (d *Doubler) Load(props gae.DSPropertyMap) ([]string, error) {
|
| - return GetStructPLS(d).Load(props)
|
| +func (d *Doubler) Load(props gae.DSPropertyMap) error {
|
| + return GetPLS(d).Load(props)
|
| }
|
|
|
| -func (d *Doubler) Save() (gae.DSPropertyMap, error) {
|
| - // Save the default gae.DSProperty slice to an in-memory buffer (a gae.DSPropertyList).
|
| - pls := GetStructPLS(d)
|
| - props, err := pls.Save()
|
| +func (d *Doubler) Save(withMeta bool) (gae.DSPropertyMap, error) {
|
| + pls := GetPLS(d)
|
| + propMap, err := pls.Save(withMeta)
|
| if err != nil {
|
| return nil, err
|
| }
|
| - var propMap gae.DSPropertyMap
|
| - propMap.Load(props) // we know this returns nil/nil
|
|
|
| // Edit that map and send it on.
|
| for _, props := range propMap {
|
| @@ -377,23 +366,27 @@ func (d *Doubler) Save() (gae.DSPropertyMap, error) {
|
| switch v := props[i].Value().(type) {
|
| case string:
|
| // + means string concatenation.
|
| - props[i].SetValue(v+v, props[i].NoIndex())
|
| + props[i].SetValue(v+v, props[i].IndexSetting())
|
| case int64:
|
| // + means integer addition.
|
| - props[i].SetValue(v+v, props[i].NoIndex())
|
| + props[i].SetValue(v+v, props[i].IndexSetting())
|
| }
|
| }
|
| }
|
| - return propMap.Save()
|
| + return propMap, nil
|
| }
|
|
|
| +func (d *Doubler) GetMeta(string) (interface{}, error) { return nil, gae.ErrDSMetaFieldUnset }
|
| +func (d *Doubler) SetMeta(string, interface{}) error { return gae.ErrDSMetaFieldUnset }
|
| +func (d *Doubler) Problem() error { return nil }
|
| +
|
| var _ gae.DSPropertyLoadSaver = (*Doubler)(nil)
|
|
|
| type Deriver struct {
|
| S, Derived, Ignored string
|
| }
|
|
|
| -func (e *Deriver) Load(props gae.DSPropertyMap) ([]string, error) {
|
| +func (e *Deriver) Load(props gae.DSPropertyMap) error {
|
| for name, p := range props {
|
| if name != "S" {
|
| continue
|
| @@ -401,15 +394,19 @@ func (e *Deriver) Load(props gae.DSPropertyMap) ([]string, error) {
|
| e.S = p[0].Value().(string)
|
| e.Derived = "derived+" + e.S
|
| }
|
| - return nil, nil
|
| + return nil
|
| }
|
|
|
| -func (e *Deriver) Save() (gae.DSPropertyMap, error) {
|
| +func (e *Deriver) Save(withMeta bool) (gae.DSPropertyMap, error) {
|
| return map[string][]gae.DSProperty{
|
| "S": {mp(e.S)},
|
| }, nil
|
| }
|
|
|
| +func (d *Deriver) GetMeta(string) (interface{}, error) { return nil, gae.ErrDSMetaFieldUnset }
|
| +func (d *Deriver) SetMeta(string, interface{}) error { return gae.ErrDSMetaFieldUnset }
|
| +func (d *Deriver) Problem() error { return nil }
|
| +
|
| var _ gae.DSPropertyLoadSaver = (*Deriver)(nil)
|
|
|
| type BK struct {
|
| @@ -425,7 +422,7 @@ func (c *Convertable) ToDSProperty() (ret gae.DSProperty, err error) {
|
| for i, v := range *c {
|
| buf[i] = strconv.FormatInt(v, 10)
|
| }
|
| - err = ret.SetValue(strings.Join(buf, ","), true)
|
| + err = ret.SetValue(strings.Join(buf, ","), gae.NoIndex)
|
| return
|
| }
|
|
|
| @@ -457,7 +454,7 @@ type Convertable2 struct {
|
| }
|
|
|
| func (c *Convertable2) ToDSProperty() (ret gae.DSProperty, err error) {
|
| - err = ret.SetValue(c.Data, false)
|
| + err = ret.SetValue(c.Data, gae.ShouldIndex)
|
| return
|
| }
|
|
|
| @@ -487,7 +484,7 @@ func (j *JSONKVProp) ToDSProperty() (ret gae.DSProperty, err error) {
|
| if err != nil {
|
| return
|
| }
|
| - err = ret.SetValue(data, true)
|
| + err = ret.SetValue(data, gae.NoIndex)
|
| return
|
| }
|
|
|
| @@ -513,7 +510,7 @@ func (c *Complex) ToDSProperty() (ret gae.DSProperty, err error) {
|
| // (note that this won't REALLY work, since GeoPoints are limited to a very
|
| // limited range of values, but it's nice to pretend ;)). You'd probably
|
| // really end up with a packed binary representation.
|
| - err = ret.SetValue(gae.DSGeoPoint{Lat: real(*c), Lng: imag(*c)}, false)
|
| + err = ret.SetValue(gae.DSGeoPoint{Lat: real(*c), Lng: imag(*c)}, gae.ShouldIndex)
|
| return
|
| }
|
|
|
| @@ -545,7 +542,6 @@ type testCase struct {
|
| saveErr string
|
| actualNoIndex bool
|
| plsLoadErr string
|
| - convErr string
|
| loadErr string
|
| }
|
|
|
| @@ -601,7 +597,7 @@ var testCases = []testCase{
|
| {
|
| desc: "geopoint as props",
|
| src: &G0{G: testGeoPt0},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "G": {mp(testGeoPt0)},
|
| },
|
| },
|
| @@ -639,7 +635,7 @@ var testCases = []testCase{
|
| desc: "overflow",
|
| src: &O0{I: 1 << 48},
|
| want: &O1{},
|
| - convErr: "overflow",
|
| + loadErr: "overflow",
|
| },
|
| {
|
| desc: "time",
|
| @@ -649,8 +645,8 @@ var testCases = []testCase{
|
| {
|
| desc: "time as props",
|
| src: &T{T: time.Unix(1e9, 0).UTC()},
|
| - want: &gae.DSPropertyMap{
|
| - "T": {mp(time.Unix(1e9, 0).UTC(), false)},
|
| + want: gae.DSPropertyMap{
|
| + "T": {mp(time.Unix(1e9, 0).UTC())},
|
| },
|
| },
|
| {
|
| @@ -683,13 +679,13 @@ var testCases = []testCase{
|
| desc: "missing fields",
|
| src: &X0{S: "one", I: 2, i: 3},
|
| want: &X2{},
|
| - convErr: "no such struct field",
|
| + loadErr: "no such struct field",
|
| },
|
| {
|
| desc: "save string load bool",
|
| src: &X0{S: "one", I: 2, i: 3},
|
| want: &X3{I: 2},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "basic slice",
|
| @@ -700,7 +696,7 @@ var testCases = []testCase{
|
| desc: "save []float64 load float64",
|
| src: &Y0{B: true, F: []float64{7, 8, 9}},
|
| want: &Y1{B: true},
|
| - convErr: "requires a slice",
|
| + loadErr: "requires a slice",
|
| },
|
| {
|
| desc: "save single []int64 load int64",
|
| @@ -720,15 +716,15 @@ var testCases = []testCase{
|
| {
|
| desc: "use convertable slice (to map)",
|
| src: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Convertable{2, 4, 6}}}},
|
| - want: &gae.DSPropertyMap{
|
| - "Nested.wot": {mp("1,5,9", true), mp("2,4,6", true)},
|
| + want: gae.DSPropertyMap{
|
| + "Nested.wot": {mpNI("1,5,9"), mpNI("2,4,6")},
|
| },
|
| },
|
| {
|
| desc: "convertable slice (bad load)",
|
| - src: &gae.DSPropertyMap{"Nested.wot": {mp([]byte("ohai"), true)}},
|
| + src: gae.DSPropertyMap{"Nested.wot": {mpNI([]byte("ohai"))}},
|
| want: &Impossible{[]ImpossibleInner{{}}},
|
| - convErr: "nope",
|
| + loadErr: "nope",
|
| },
|
| {
|
| desc: "use convertable struct",
|
| @@ -769,11 +765,10 @@ var testCases = []testCase{
|
| "what": []interface{}{"is", "really", 100},
|
| },
|
| },
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "kewelmap": {
|
| - mp([]byte(
|
| - `{"epic":"success","no_way!":[true,"story"],"what":["is","really",100]}`),
|
| - true)},
|
| + mpNI([]byte(
|
| + `{"epic":"success","no_way!":[true,"story"],"what":["is","really",100]}`))},
|
| },
|
| },
|
| {
|
| @@ -790,16 +785,16 @@ var testCases = []testCase{
|
| src: &Impossible4{
|
| []Complex{complex(1, 2), complex(3, 4)},
|
| },
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "Values": {
|
| mp(gae.DSGeoPoint{Lat: 1, Lng: 2}), mp(gae.DSGeoPoint{Lat: 3, Lng: 4})},
|
| },
|
| },
|
| {
|
| desc: "convertable complex slice (bad load)",
|
| - src: &gae.DSPropertyMap{"Values": {mp("hello")}},
|
| + src: gae.DSPropertyMap{"Values": {mp("hello")}},
|
| want: &Impossible4{[]Complex(nil)},
|
| - convErr: "nope",
|
| + loadErr: "nope",
|
| },
|
| {
|
| desc: "allow concrete gae.DSKey implementors (save)",
|
| @@ -815,7 +810,7 @@ var testCases = []testCase{
|
| desc: "save []float64 load []int64",
|
| src: &Y0{B: true, F: []float64{7, 8, 9}},
|
| want: &Y2{B: true},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "single slice is too long",
|
| @@ -892,13 +887,13 @@ var testCases = []testCase{
|
| {
|
| desc: "short gae.DSByteString as props",
|
| src: &B5{B: gae.DSByteString(makeUint8Slice(3))},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "B": {mp(gae.DSByteString(makeUint8Slice(3)))},
|
| },
|
| },
|
| {
|
| desc: "[]byte must be noindex",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "B": {mp(makeUint8Slice(3))},
|
| },
|
| actualNoIndex: true,
|
| @@ -906,7 +901,7 @@ var testCases = []testCase{
|
| {
|
| desc: "save tagged load props",
|
| src: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6, J: 7},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| // A and B are renamed to a and b; A and C are noindex, I is ignored.
|
| // Indexed properties are loaded before raw properties. Thus, the
|
| // result is: b, b, b, D, E, a, c.
|
| @@ -917,9 +912,9 @@ var testCases = []testCase{
|
| },
|
| "D": {mp(4)},
|
| "E": {mp(5)},
|
| - "a": {mp(1, true)},
|
| - "C": {mp(3, true)},
|
| - "J": {mp(7, true)},
|
| + "a": {mpNI(1)},
|
| + "C": {mpNI(3)},
|
| + "J": {mpNI(7)},
|
| },
|
| },
|
| {
|
| @@ -929,12 +924,12 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "save props load tagged",
|
| - src: &gae.DSPropertyMap{
|
| - "A": {mp(11, true)},
|
| - "a": {mp(12, true)},
|
| + src: gae.DSPropertyMap{
|
| + "A": {mpNI(11)},
|
| + "a": {mpNI(12)},
|
| },
|
| want: &Tagged{A: 12},
|
| - convErr: `cannot load field "A"`,
|
| + loadErr: `cannot load field "A"`,
|
| },
|
| {
|
| desc: "invalid tagged1",
|
| @@ -970,14 +965,14 @@ var testCases = []testCase{
|
| {
|
| desc: "save struct load props",
|
| src: &X0{S: "s", I: 1},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "S": {mp("s")},
|
| "I": {mp(1)},
|
| },
|
| },
|
| {
|
| desc: "save props load struct",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "S": {mp("s")},
|
| "I": {mp(1)},
|
| },
|
| @@ -985,7 +980,7 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "nil-value props",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "I": {mp(nil)},
|
| "B": {mp(nil)},
|
| "S": {mp(nil)},
|
| @@ -1026,7 +1021,7 @@ var testCases = []testCase{
|
| Z: true,
|
| },
|
| },
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "A": {mp(1)},
|
| "I.W": {
|
| mp(10),
|
| @@ -1044,7 +1039,7 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "save props load outer-equivalent",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "A": {mp(1)},
|
| "I.W": {
|
| mp(10),
|
| @@ -1094,13 +1089,13 @@ var testCases = []testCase{
|
| {
|
| desc: "dotted names save",
|
| src: &Dotted{A: DottedA{B: DottedB{C: 88}}},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "A0.A1.A2.B3.C4.C5": {mp(88)},
|
| },
|
| },
|
| {
|
| desc: "dotted names load",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "A0.A1.A2.B3.C4.C5": {mp(99)},
|
| },
|
| want: &Dotted{A: DottedA{B: DottedB{C: 99}}},
|
| @@ -1180,49 +1175,49 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "mismatch (string)",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "K": {mp(199)},
|
| "S": {mp([]byte("cats"))},
|
| "F": {mp(gae.DSByteString("nurbs"))},
|
| },
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "mismatch (float)",
|
| - src: &gae.DSPropertyMap{"F": {mp(gae.BSKey("wot"))}},
|
| + src: gae.DSPropertyMap{"F": {mp(gae.BSKey("wot"))}},
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "mismatch (float/overflow)",
|
| - src: &gae.DSPropertyMap{"F": {mp(math.MaxFloat64)}},
|
| + src: gae.DSPropertyMap{"F": {mp(math.MaxFloat64)}},
|
| want: &MismatchTypes{},
|
| - convErr: "overflows",
|
| + loadErr: "overflows",
|
| },
|
| {
|
| desc: "mismatch (key)",
|
| - src: &gae.DSPropertyMap{"K": {mp(false)}},
|
| + src: gae.DSPropertyMap{"K": {mp(false)}},
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "mismatch (bool)",
|
| - src: &gae.DSPropertyMap{"B": {mp(testKey0)}},
|
| + src: gae.DSPropertyMap{"B": {mp(testKey0)}},
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "mismatch (time)",
|
| - src: &gae.DSPropertyMap{"T": {mp(gae.DSGeoPoint{})}},
|
| + src: gae.DSPropertyMap{"T": {mp(gae.DSGeoPoint{})}},
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "mismatch (geopoint)",
|
| - src: &gae.DSPropertyMap{"G": {mp(time.Now().UTC())}},
|
| + src: gae.DSPropertyMap{"G": {mp(time.Now().UTC())}},
|
| want: &MismatchTypes{},
|
| - convErr: "type mismatch",
|
| + loadErr: "type mismatch",
|
| },
|
| {
|
| desc: "slice of structs",
|
| @@ -1331,7 +1326,7 @@ var testCases = []testCase{
|
| },
|
| },
|
| },
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "red.S": {mp("rouge")},
|
| "red.I": {mp(0)},
|
| "red.Nonymous.S": {mp("rosso0"), mp("rosso1")},
|
| @@ -1351,7 +1346,7 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "save props load structs with ragged fields",
|
| - src: &gae.DSPropertyMap{
|
| + src: gae.DSPropertyMap{
|
| "red.S": {mp("rot")},
|
| "green.Nonymous.I": {mp(10), mp(11), mp(12), mp(13)},
|
| "Blue.Nonymous.S": {mp("blau0"), mp("blau1"), mp("blau2")},
|
| @@ -1390,11 +1385,11 @@ var testCases = []testCase{
|
| Y string
|
| }
|
| }{},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "B.Y": {mp("")},
|
| - "A.X": {mp("", true)},
|
| - "A.Y": {mp("", true)},
|
| - "B.X": {mp("", true)},
|
| + "A.X": {mpNI("")},
|
| + "A.Y": {mpNI("")},
|
| + "B.X": {mpNI("")},
|
| },
|
| },
|
| {
|
| @@ -1402,7 +1397,7 @@ var testCases = []testCase{
|
| src: &struct {
|
| Inner1 `gae:"foo"`
|
| }{},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "foo.W": {mp(0)},
|
| "foo.X": {mp("")},
|
| },
|
| @@ -1427,7 +1422,7 @@ var testCases = []testCase{
|
| src: &struct {
|
| i, J int64
|
| }{i: 1, J: 2},
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "J": {mp(2)},
|
| },
|
| },
|
| @@ -1438,7 +1433,7 @@ var testCases = []testCase{
|
| }{
|
| J: json.RawMessage("rawr"),
|
| },
|
| - want: &gae.DSPropertyMap{
|
| + want: gae.DSPropertyMap{
|
| "J": {mp([]byte("rawr"))},
|
| },
|
| },
|
| @@ -1511,13 +1506,16 @@ func TestRoundTrip(t *testing.T) {
|
| for _, tc := range testCases {
|
| tc := tc
|
| Convey(tc.desc, func() {
|
| - pls, err := GetPLS(tc.src)
|
| - if checkErr(err, tc.plsErr) {
|
| + pls, ok := tc.src.(gae.DSPropertyLoadSaver)
|
| + if !ok {
|
| + pls = GetPLS(tc.src)
|
| + }
|
| + if checkErr(pls.Problem(), tc.plsErr) {
|
| return
|
| }
|
| So(pls, ShouldNotBeNil)
|
|
|
| - savedProps, err := pls.Save()
|
| + savedProps, err := pls.Save(false)
|
| if checkErr(err, tc.saveErr) {
|
| return
|
| }
|
| @@ -1525,34 +1523,32 @@ func TestRoundTrip(t *testing.T) {
|
|
|
| if tc.actualNoIndex {
|
| for _, props := range savedProps {
|
| - So(props[0].NoIndex(), ShouldBeTrue)
|
| + So(props[0].IndexSetting(), ShouldEqual, gae.NoIndex)
|
| return
|
| }
|
| So(true, ShouldBeFalse) // shouldn't get here
|
| }
|
|
|
| var got interface{}
|
| - if _, ok := tc.want.(*gae.DSPropertyMap); ok {
|
| - got = &gae.DSPropertyMap{}
|
| + if _, ok := tc.want.(gae.DSPropertyMap); ok {
|
| + pls = gae.DSPropertyMap{}
|
| + got = pls
|
| } else {
|
| got = reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
|
| + if pls, ok = got.(gae.DSPropertyLoadSaver); !ok {
|
| + pls = GetPLS(got)
|
| + }
|
| }
|
|
|
| - pls, err = GetPLS(got)
|
| - if checkErr(err, tc.plsLoadErr) {
|
| + if checkErr(pls.Problem(), tc.plsLoadErr) {
|
| return
|
| }
|
| So(pls, ShouldNotBeNil)
|
|
|
| - convErrs, err := pls.Load(savedProps)
|
| + err = pls.Load(savedProps)
|
| if checkErr(err, tc.loadErr) {
|
| return
|
| }
|
| - if len(tc.convErr) == 0 {
|
| - So(convErrs, ShouldBeNil)
|
| - } else {
|
| - So(convErrs[0], ShouldContainSubstring, tc.convErr)
|
| - }
|
| if tc.want == nil {
|
| return
|
| }
|
| @@ -1575,39 +1571,37 @@ func TestSpecial(t *testing.T) {
|
| Convey("Test special fields", t, func() {
|
| Convey("Can retrieve from struct", func() {
|
| o := &N0{ID: 100}
|
| - pls := GetStructPLS(o)
|
| - val, current, err := pls.GetSpecial("id")
|
| + pls := GetPLS(o)
|
| + val, err := pls.GetMeta("id")
|
| So(err, ShouldBeNil)
|
| - So(val, ShouldEqual, "")
|
| - So(current, ShouldEqual, 100)
|
| + So(val, ShouldEqual, 100)
|
|
|
| - val, current, err = pls.GetSpecial("kind")
|
| + val, err = pls.GetMeta("kind")
|
| So(err, ShouldBeNil)
|
| So(val, ShouldEqual, "whatnow")
|
| - So(current, ShouldEqual, nil)
|
| })
|
|
|
| Convey("Getting something not there is an error", func() {
|
| o := &N0{ID: 100}
|
| - pls := GetStructPLS(o)
|
| - _, _, err := pls.GetSpecial("wat")
|
| - So(err, ShouldEqual, gae.ErrDSSpecialFieldUnset)
|
| + pls := GetPLS(o)
|
| + _, err := pls.GetMeta("wat")
|
| + So(err, ShouldEqual, gae.ErrDSMetaFieldUnset)
|
| })
|
|
|
| Convey("getting/setting from a bad struct is an error", func() {
|
| o := &Recursive{}
|
| - pls := GetStructPLS(o)
|
| - _, _, err := pls.GetSpecial("wat")
|
| + pls := GetPLS(o)
|
| + _, err := pls.GetMeta("wat")
|
| So(err, ShouldNotBeNil)
|
|
|
| - err = pls.SetSpecial("wat", 100)
|
| + err = pls.SetMeta("wat", 100)
|
| So(err, ShouldNotBeNil)
|
| })
|
|
|
| Convey("can assign values to exported special fields", func() {
|
| o := &N0{ID: 100}
|
| - pls := GetStructPLS(o)
|
| - err := pls.SetSpecial("id", int64(200))
|
| + pls := GetPLS(o)
|
| + err := pls.SetMeta("id", int64(200))
|
| So(err, ShouldBeNil)
|
| So(o.ID, ShouldEqual, 200)
|
|
|
| @@ -1615,74 +1609,111 @@ func TestSpecial(t *testing.T) {
|
|
|
| Convey("assigning to unsassiagnable fields is a simple error", func() {
|
| o := &N0{ID: 100}
|
| - pls := GetStructPLS(o)
|
| - err := pls.SetSpecial("kind", "hi")
|
| + pls := GetPLS(o)
|
| + err := pls.SetMeta("kind", "hi")
|
| So(err.Error(), ShouldContainSubstring, "unexported field")
|
|
|
| - err = pls.SetSpecial("noob", "hi")
|
| - So(err, ShouldEqual, gae.ErrDSSpecialFieldUnset)
|
| + err = pls.SetMeta("noob", "hi")
|
| + So(err, ShouldEqual, gae.ErrDSMetaFieldUnset)
|
| })
|
| })
|
|
|
| Convey("StructPLS Miscellaneous", t, func() {
|
| Convey("multiple overlapping fields is an error", func() {
|
| o := &BadSpecial{}
|
| - pls := GetStructPLS(o)
|
| - convErr, err := pls.Load(nil)
|
| - So(convErr, ShouldBeNil)
|
| + pls := GetPLS(o)
|
| + err := pls.Load(nil)
|
| So(err, ShouldErrLike, "multiple times")
|
| e := pls.Problem()
|
| - _, err = pls.Save()
|
| + _, err = pls.Save(true)
|
| So(err, ShouldEqual, e)
|
| - _, err = pls.Load(nil)
|
| + err = pls.Load(nil)
|
| So(err, ShouldEqual, e)
|
| })
|
|
|
| - Convey("can transform a list of things into a list of PLSs", func() {
|
| - o := []interface{}{
|
| - &N0{X0: X0{S: "hi", I: 5}},
|
| - &N0{Nonymous: X0{S: "hi", I: 5}},
|
| - &gae.DSPropertyMap{
|
| - "Nerd": {mp(10), mp(false)},
|
| - "What": {mp("is"), mp("up")},
|
| - },
|
| + Convey("empty property names are invalid", func() {
|
| + So(validPropertyName(""), ShouldBeFalse)
|
| + })
|
| +
|
| + Convey("attempting to get a PLS for a non *struct is an error", func() {
|
| + pls := GetPLS((*[]string)(nil))
|
| + So(pls.Problem(), ShouldEqual, gae.ErrDSInvalidEntityType)
|
| + })
|
| +
|
| + Convey("convertible meta default types", func() {
|
| + type OKDefaults struct {
|
| + When string `gae:"$when,tomorrow"`
|
| + Amount int64 `gae:"$amt,100"`
|
| }
|
| - plss, err := MultiGetPLS(o)
|
| + pls := GetPLS(&OKDefaults{})
|
| + So(pls.Problem(), ShouldBeNil)
|
| +
|
| + v, err := pls.GetMeta("when")
|
| So(err, ShouldBeNil)
|
| - for i, pls := range plss {
|
| - pmap, err := pls.Save()
|
| - targ := gae.DSPropertyLoadSaver(&gae.DSPropertyMap{})
|
| - obj := interface{}(targ)
|
| - if i < 2 {
|
| - obj = &N0{}
|
| - targ = GetStructPLS(obj)
|
| - }
|
| - convErr, err := targ.Load(pmap)
|
| - So(err, ShouldBeNil)
|
| - So(convErr, ShouldBeNil)
|
| - So(obj, ShouldResemble, o[i])
|
| + So(v, ShouldEqual, "tomorrow")
|
| +
|
| + v, err = pls.GetMeta("amt")
|
| + So(err, ShouldBeNil)
|
| + So(v, ShouldEqual, int64(100))
|
| + })
|
| +
|
| + Convey("meta fields can be saved", func() {
|
| + type OKDefaults struct {
|
| + When string `gae:"$when,tomorrow"`
|
| + Amount int64 `gae:"$amt,100"`
|
| }
|
| + pls := GetPLS(&OKDefaults{})
|
| + pm, err := pls.Save(true)
|
| + So(err, ShouldBeNil)
|
| + So(pm, ShouldResemble, gae.DSPropertyMap{
|
| + "$when": {gae.MkDSPropertyNI("tomorrow")},
|
| + "$amt": {gae.MkDSPropertyNI(100)},
|
| + })
|
| +
|
| + v, err := pm.GetMeta("when")
|
| + So(err, ShouldBeNil)
|
| + So(v, ShouldEqual, "tomorrow")
|
| +
|
| + v, err = pm.GetMeta("amt")
|
| + So(err, ShouldBeNil)
|
| + So(v, ShouldEqual, int64(100))
|
| })
|
|
|
| - Convey("list of DSPropertyLoadSavers is a shortcut", func() {
|
| - o := []gae.DSPropertyLoadSaver{&gae.DSPropertyMap{}}
|
| - plss, err := MultiGetPLS(o)
|
| + Convey("default are optional", func() {
|
| + type OverrideDefault struct {
|
| + Val int64 `gae:"$val"`
|
| + }
|
| + o := &OverrideDefault{}
|
| + pls := GetPLS(o)
|
| +
|
| + v, err := pls.GetMeta("val")
|
| So(err, ShouldBeNil)
|
| - So(&plss[0], ShouldEqual, &o[0]) // identical underlying array
|
| + So(v, ShouldEqual, int64(0))
|
| })
|
|
|
| - Convey("attempting to transform a bad object is an error", func() {
|
| - o := []int{100}
|
| - _, err := MultiGetPLS(o)
|
| - So(err, ShouldEqual, gae.ErrDSInvalidEntityType)
|
| + Convey("overridable defaults", func() {
|
| + type OverrideDefault struct {
|
| + Val int64 `gae:"$val,100"`
|
| + }
|
| + o := &OverrideDefault{}
|
| + pls := GetPLS(o)
|
| +
|
| + v, err := pls.GetMeta("val")
|
| + So(err, ShouldBeNil)
|
| + So(v, ShouldEqual, int64(100))
|
|
|
| - f := false
|
| - _, err = MultiGetPLS(&f)
|
| - So(err, ShouldErrLike, "bad type")
|
| + o.Val = 10
|
| + v, err = pls.GetMeta("val")
|
| + So(err, ShouldBeNil)
|
| + So(v, ShouldEqual, int64(10))
|
| })
|
|
|
| - Convey("empty property names are invalid", func() {
|
| - So(validPropertyName(""), ShouldBeFalse)
|
| + Convey("Bad default meta type", func() {
|
| + type BadDefault struct {
|
| + Val time.Time `gae:"$meta,tomorrow"`
|
| + }
|
| + pls := GetPLS(&BadDefault{})
|
| + So(pls.Problem().Error(), ShouldContainSubstring, "bad type")
|
| })
|
| })
|
| }
|
|
|