| Index: service/datastore/pls_test.go
|
| diff --git a/service/datastore/pls_test.go b/service/datastore/pls_test.go
|
| index 959226af5ae02cadc8717f4aad79d4d6edc03e8b..c11cccff8d8136b571faca0ec8506177f8102038 100644
|
| --- a/service/datastore/pls_test.go
|
| +++ b/service/datastore/pls_test.go
|
| @@ -18,6 +18,7 @@ import (
|
| "time"
|
|
|
| "github.com/luci/gae/service/blobstore"
|
| + . "github.com/luci/luci-go/common/testing/assertions"
|
| . "github.com/smartystreets/goconvey/convey"
|
| )
|
|
|
| @@ -527,8 +528,120 @@ type Impossible4 struct {
|
| Values []Complex
|
| }
|
|
|
| +// TODO(riannucci): see if there's a way to NOT have this be a duplicate of
|
| +// key.Generic. I couldn't figure out the package interdependency, and this
|
| +// allows things to be in separate packages.
|
| +type genericKey struct {
|
| + kind string
|
| + sid string
|
| + iid int64
|
| +
|
| + aid string
|
| + ns string
|
| +
|
| + parent *genericKey
|
| +}
|
| +
|
| +func (g *genericKey) Kind() string { return g.kind }
|
| +func (g *genericKey) StringID() string { return g.sid }
|
| +func (g *genericKey) IntID() int64 { return g.iid }
|
| +func (g *genericKey) AppID() string { return g.aid }
|
| +func (g *genericKey) Namespace() string { return g.ns }
|
| +func (g *genericKey) Parent() Key { return g.parent }
|
| +
|
| +func marshalDSKey(b *bytes.Buffer, k *genericKey) {
|
| + if k.parent != nil {
|
| + marshalDSKey(b, k.parent)
|
| + }
|
| + b.WriteByte('/')
|
| + b.WriteString(k.kind)
|
| + b.WriteByte(',')
|
| + if k.sid != "" {
|
| + b.WriteString(k.sid)
|
| + } else {
|
| + b.WriteString(strconv.FormatInt(k.iid, 10))
|
| + }
|
| +}
|
| +
|
| +func (g *genericKey) String() string {
|
| + if g == nil {
|
| + return ""
|
| + }
|
| + b := bytes.NewBuffer(make([]byte, 0, 512))
|
| + marshalDSKey(b, g)
|
| + return b.String()
|
| +}
|
| +
|
| +func (g *genericKey) Incomplete() bool {
|
| + return g.iid == 0 && g.sid == ""
|
| +}
|
| +
|
| +func (g *genericKey) Valid(allowSpecial bool, aid, ns string) bool {
|
| + if g == nil {
|
| + return false
|
| + }
|
| + if aid != g.AppID() || ns != g.Namespace() {
|
| + return false
|
| + }
|
| + for ; g != nil; g = g.parent {
|
| + if !allowSpecial && len(g.Kind()) >= 2 && g.Kind()[:2] == "__" {
|
| + return false
|
| + }
|
| + if g.Kind() == "" || g.AppID() == "" {
|
| + return false
|
| + }
|
| + if g.StringID() != "" && g.IntID() != 0 {
|
| + return false
|
| + }
|
| + if g.parent != nil {
|
| + if g.parent.Incomplete() {
|
| + return false
|
| + }
|
| + if g.parent.AppID() != g.AppID() || g.parent.Namespace() != g.Namespace() {
|
| + return false
|
| + }
|
| + }
|
| + }
|
| + return true
|
| +}
|
| +
|
| +func (g *genericKey) PartialValid(aid, ns string) bool {
|
| + if g.Incomplete() {
|
| + g = mkKey(g.AppID(), g.Namespace(), g.Kind(), 1, g.Parent()).(*genericKey)
|
| + }
|
| + return g.Valid(false, aid, ns)
|
| +}
|
| +
|
| +var _ Key = (*genericKey)(nil)
|
| +
|
| +func mkKey(aid, ns string, pairs ...interface{}) Key {
|
| + ret := (*genericKey)(nil)
|
| + if len(pairs)%2 != 0 {
|
| + ret, _ = pairs[len(pairs)-1].(*genericKey)
|
| + pairs = pairs[:len(pairs)-1]
|
| + }
|
| + for i := 0; i < len(pairs); i += 2 {
|
| + kind := pairs[i].(string)
|
| + id := pairs[i+1]
|
| + ret = &genericKey{
|
| + kind: kind,
|
| + aid: aid,
|
| + ns: ns,
|
| + parent: ret,
|
| + }
|
| + ret.sid, _ = id.(string)
|
| + iid, ok := id.(int)
|
| + if ok {
|
| + ret.iid = int64(iid)
|
| + } else {
|
| + ret.iid, _ = id.(int64)
|
| + }
|
| + }
|
| + return ret
|
| +}
|
| +
|
| type DerivedKey struct {
|
| - K *GenericKey
|
| + K *genericKey
|
| }
|
|
|
| type IfaceKey struct {
|
| @@ -799,13 +912,13 @@ var testCases = []testCase{
|
| },
|
| {
|
| desc: "allow concrete Key implementors (save)",
|
| - src: &DerivedKey{testKey2a.(*GenericKey)},
|
| + src: &DerivedKey{testKey2a.(*genericKey)},
|
| want: &IfaceKey{testKey2b},
|
| },
|
| {
|
| desc: "allow concrete Key implementors (load)",
|
| src: &IfaceKey{testKey2b},
|
| - want: &DerivedKey{testKey2a.(*GenericKey)},
|
| + want: &DerivedKey{testKey2a.(*genericKey)},
|
| },
|
| {
|
| desc: "save []float64 load []int64",
|
| @@ -1463,43 +1576,15 @@ func checkErr(want string, err error) string {
|
| return ""
|
| }
|
|
|
| -func ShouldErrLike(actual interface{}, expected ...interface{}) string {
|
| - e2s := func(o interface{}) (string, bool) {
|
| - switch x := o.(type) {
|
| - case nil:
|
| - return "", true
|
| - case string:
|
| - return x, true
|
| - case error:
|
| - if x != nil {
|
| - return x.Error(), true
|
| - }
|
| - return "", true
|
| - }
|
| - return fmt.Sprintf("unknown argument type %T, expected string, error or nil", actual), false
|
| - }
|
| -
|
| - as, ok := e2s(actual)
|
| - if !ok {
|
| - return as
|
| - }
|
| -
|
| - if len(expected) != 1 {
|
| - return fmt.Sprintf("Assertion requires 1 expected value, got %d", len(expected))
|
| - }
|
| -
|
| - err, ok := e2s(expected[0])
|
| - if !ok {
|
| - return err
|
| - }
|
| - return ShouldContainSubstring(as, err)
|
| -}
|
| -
|
| func TestRoundTrip(t *testing.T) {
|
| t.Parallel()
|
|
|
| checkErr := func(actual interface{}, expected string) bool {
|
| - So(actual, ShouldErrLike, expected)
|
| + if expected == "" {
|
| + So(actual, ShouldErrLike, nil)
|
| + } else {
|
| + So(actual, ShouldErrLike, expected)
|
| + }
|
| return expected != ""
|
| }
|
|
|
|
|