| Index: service/datastore/serialize/serialize_test.go
|
| diff --git a/service/datastore/serialize_test.go b/service/datastore/serialize/serialize_test.go
|
| similarity index 56%
|
| rename from service/datastore/serialize_test.go
|
| rename to service/datastore/serialize/serialize_test.go
|
| index db9c9f67f4ed81f177a28f3965a9a513e41bd108..b1fced4d335b6ad19ac616988f32b96252fb28d7 100644
|
| --- a/service/datastore/serialize_test.go
|
| +++ b/service/datastore/serialize/serialize_test.go
|
| @@ -2,16 +2,20 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -package datastore
|
| +package serialize
|
|
|
| import (
|
| "bytes"
|
| + "fmt"
|
| "io"
|
| "testing"
|
| "time"
|
|
|
| "github.com/luci/gae/service/blobstore"
|
| + ds "github.com/luci/gae/service/datastore"
|
| + "github.com/luci/gae/service/datastore/dskey"
|
| "github.com/luci/luci-go/common/cmpbin"
|
| + . "github.com/luci/luci-go/common/testing/assertions"
|
| . "github.com/smartystreets/goconvey/convey"
|
| )
|
|
|
| @@ -19,9 +23,49 @@ func init() {
|
| WritePropertyMapDeterministic = true
|
| }
|
|
|
| +var (
|
| + mp = ds.MkProperty
|
| + mpNI = ds.MkPropertyNI
|
| +)
|
| +
|
| type dspmapTC struct {
|
| name string
|
| - props PropertyMap
|
| + props ds.PropertyMap
|
| +}
|
| +
|
| +// TODO(riannucci): dedup with datastore/key testing file.
|
| +func mkKey(aid, ns string, elems ...interface{}) ds.Key {
|
| + if len(elems)%2 != 0 {
|
| + panic("odd number of tokens")
|
| + }
|
| + toks := make([]ds.KeyTok, len(elems)/2)
|
| + for i := 0; i < len(elems); i += 2 {
|
| + toks[i/2].Kind = elems[i].(string)
|
| + switch x := elems[i+1].(type) {
|
| + case string:
|
| + toks[i/2].StringID = x
|
| + case int:
|
| + toks[i/2].IntID = int64(x)
|
| + default:
|
| + panic("bad token id")
|
| + }
|
| + }
|
| + return dskey.NewToks(aid, ns, toks)
|
| +}
|
| +
|
| +func mkBuf(data []byte) Buffer {
|
| + return Invertible(bytes.NewBuffer(data))
|
| +}
|
| +
|
| +// TODO(riannucci): dedup with datastore/key testing file
|
| +func ShouldEqualKey(actual interface{}, expected ...interface{}) string {
|
| + if len(expected) != 1 {
|
| + return fmt.Sprintf("Assertion requires 1 expected value, got %d", len(expected))
|
| + }
|
| + if dskey.Equal(actual.(ds.Key), expected[0].(ds.Key)) {
|
| + return ""
|
| + }
|
| + return fmt.Sprintf("Expected: %q\nActual: %q", actual, expected[0])
|
| }
|
|
|
| func TestPropertyMapSerialization(t *testing.T) {
|
| @@ -30,35 +74,35 @@ func TestPropertyMapSerialization(t *testing.T) {
|
| tests := []dspmapTC{
|
| {
|
| "basic",
|
| - PropertyMap{
|
| + ds.PropertyMap{
|
| "R": {mp(false), mp(2.1), mpNI(3)},
|
| "S": {mp("hello"), mp("world")},
|
| },
|
| },
|
| {
|
| "keys",
|
| - PropertyMap{
|
| + ds.PropertyMap{
|
| "DS": {mp(mkKey("appy", "ns", "Foo", 7)), mp(mkKey("other", "", "Yot", "wheeep"))},
|
| "blobstore": {mp(blobstore.Key("sup")), mp(blobstore.Key("nerds"))},
|
| },
|
| },
|
| {
|
| "geo",
|
| - PropertyMap{
|
| - "G": {mp(GeoPoint{Lat: 1, Lng: 2})},
|
| + ds.PropertyMap{
|
| + "G": {mp(ds.GeoPoint{Lat: 1, Lng: 2})},
|
| },
|
| },
|
| {
|
| "data",
|
| - PropertyMap{
|
| + ds.PropertyMap{
|
| "S": {mp("sup"), mp("fool"), mp("nerd")},
|
| "D.Foo.Nerd": {mp([]byte("sup")), mp([]byte("fool"))},
|
| - "B": {mp(ByteString("sup")), mp(ByteString("fool"))},
|
| + "B": {mp(ds.ByteString("sup")), mp(ds.ByteString("fool"))},
|
| },
|
| },
|
| {
|
| "time",
|
| - PropertyMap{
|
| + ds.PropertyMap{
|
| "T": {
|
| mp(time.Now().UTC()),
|
| mp(time.Now().Add(time.Second).UTC())},
|
| @@ -66,7 +110,7 @@ func TestPropertyMapSerialization(t *testing.T) {
|
| },
|
| {
|
| "empty vals",
|
| - PropertyMap{
|
| + ds.PropertyMap{
|
| "T": {mp(true), mp(true)},
|
| "F": {mp(false), mp(false)},
|
| "N": {mp(nil), mp(nil)},
|
| @@ -80,10 +124,8 @@ func TestPropertyMapSerialization(t *testing.T) {
|
| for _, tc := range tests {
|
| tc := tc
|
| Convey(tc.name, func() {
|
| - buf := &bytes.Buffer{}
|
| - tc.props.Write(buf, WithContext)
|
| - dec := PropertyMap{}
|
| - err := dec.Read(buf, WithContext, "", "")
|
| + data := ToBytesWithContext(tc.props)
|
| + dec, err := ReadPropertyMap(mkBuf(data), WithContext, "", "")
|
| So(err, ShouldBeNil)
|
| So(dec, ShouldResemble, tc.props)
|
| })
|
| @@ -96,34 +138,75 @@ func TestSerializationReadMisc(t *testing.T) {
|
| t.Parallel()
|
|
|
| Convey("Misc Serialization tests", t, func() {
|
| - buf := &bytes.Buffer{}
|
| + Convey("GeoPoint", func() {
|
| + buf := mkBuf(nil)
|
| + cmpbin.WriteFloat64(buf, 10)
|
| + cmpbin.WriteFloat64(buf, 20)
|
| + So(string(ToBytes(ds.GeoPoint{Lat: 10, Lng: 20})), ShouldEqual, buf.String())
|
| + })
|
| +
|
| + Convey("IndexColumn", func() {
|
| + buf := mkBuf(nil)
|
| + buf.WriteByte(1)
|
| + cmpbin.WriteString(buf, "hi")
|
| + So(string(ToBytes(ds.IndexColumn{Property: "hi", Direction: ds.DESCENDING})),
|
| + ShouldEqual, buf.String())
|
| + })
|
| +
|
| + Convey("KeyTok", func() {
|
| + buf := mkBuf(nil)
|
| + cmpbin.WriteString(buf, "foo")
|
| + buf.WriteByte(byte(ds.PTInt))
|
| + cmpbin.WriteInt(buf, 20)
|
| + So(string(ToBytes(ds.KeyTok{Kind: "foo", IntID: 20})),
|
| + ShouldEqual, buf.String())
|
| + })
|
| +
|
| + Convey("Property", func() {
|
| + buf := mkBuf(nil)
|
| + buf.WriteByte(byte(ds.PTString))
|
| + cmpbin.WriteString(buf, "nerp")
|
| + So(string(ToBytes(mp("nerp"))),
|
| + ShouldEqual, buf.String())
|
| + })
|
| +
|
| + Convey("Time", func() {
|
| + tp := mp(time.Now().UTC())
|
| + So(string(ToBytes(tp.Value())), ShouldEqual, string(ToBytes(tp)[1:]))
|
| + })
|
| +
|
| + Convey("Bad ToBytes", func() {
|
| + So(func() { ToBytes(100.7) }, ShouldPanic)
|
| + So(func() { ToBytesWithContext(100.7) }, ShouldPanic)
|
| + })
|
| +
|
| Convey("ReadKey", func() {
|
| Convey("good cases", func() {
|
| Convey("w/ ctx decodes normally w/ ctx", func() {
|
| k := mkKey("aid", "ns", "knd", "yo", "other", 10)
|
| - WriteKey(buf, WithContext, k)
|
| - dk, err := ReadKey(buf, WithContext, "", "")
|
| + data := ToBytesWithContext(k)
|
| + dk, err := ReadKey(mkBuf(data), WithContext, "", "")
|
| So(err, ShouldBeNil)
|
| So(dk, ShouldEqualKey, k)
|
| })
|
| Convey("w/ ctx decodes normally w/o ctx", func() {
|
| k := mkKey("aid", "ns", "knd", "yo", "other", 10)
|
| - WriteKey(buf, WithContext, k)
|
| - dk, err := ReadKey(buf, WithoutContext, "spam", "nerd")
|
| + data := ToBytesWithContext(k)
|
| + dk, err := ReadKey(mkBuf(data), WithoutContext, "spam", "nerd")
|
| So(err, ShouldBeNil)
|
| So(dk, ShouldEqualKey, mkKey("spam", "nerd", "knd", "yo", "other", 10))
|
| })
|
| Convey("w/o ctx decodes normally w/ ctx", func() {
|
| k := mkKey("aid", "ns", "knd", "yo", "other", 10)
|
| - WriteKey(buf, WithoutContext, k)
|
| - dk, err := ReadKey(buf, WithContext, "spam", "nerd")
|
| + data := ToBytes(k)
|
| + dk, err := ReadKey(mkBuf(data), WithContext, "spam", "nerd")
|
| So(err, ShouldBeNil)
|
| So(dk, ShouldEqualKey, mkKey("", "", "knd", "yo", "other", 10))
|
| })
|
| Convey("w/o ctx decodes normally w/o ctx", func() {
|
| k := mkKey("aid", "ns", "knd", "yo", "other", 10)
|
| - WriteKey(buf, WithoutContext, k)
|
| - dk, err := ReadKey(buf, WithoutContext, "spam", "nerd")
|
| + data := ToBytes(k)
|
| + dk, err := ReadKey(mkBuf(data), WithoutContext, "spam", "nerd")
|
| So(err, ShouldBeNil)
|
| So(dk, ShouldEqualKey, mkKey("spam", "nerd", "knd", "yo", "other", 10))
|
| })
|
| @@ -131,17 +214,17 @@ func TestSerializationReadMisc(t *testing.T) {
|
| // -1 writes as almost all 1's in the first byte under cmpbin, even
|
| // though it's technically not a valid key.
|
| k := mkKey("aid", "ns", "knd", -1)
|
| - WriteKey(buf, WithoutContext, k)
|
| + data := ToBytes(k)
|
|
|
| k = mkKey("aid", "ns", "knd", "hat")
|
| - buf2 := &bytes.Buffer{}
|
| - WriteKey(buf2, WithoutContext, k)
|
| + data2 := ToBytes(k)
|
|
|
| - So(bytes.Compare(buf.Bytes(), buf2.Bytes()), ShouldBeLessThan, 0)
|
| + So(string(data), ShouldBeLessThan, string(data2))
|
| })
|
| })
|
|
|
| Convey("err cases", func() {
|
| + buf := mkBuf(nil)
|
| Convey("nil", func() {
|
| _, err := ReadKey(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| @@ -200,7 +283,7 @@ func TestSerializationReadMisc(t *testing.T) {
|
| cmpbin.WriteString(buf, "ns")
|
| cmpbin.WriteUint(buf, 2)
|
| cmpbin.WriteString(buf, "hi")
|
| - buf.WriteByte(byte(PTString))
|
| + buf.WriteByte(byte(ds.PTString))
|
| _, err := ReadKey(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| @@ -210,7 +293,7 @@ func TestSerializationReadMisc(t *testing.T) {
|
| cmpbin.WriteString(buf, "ns")
|
| cmpbin.WriteUint(buf, 2)
|
| cmpbin.WriteString(buf, "hi")
|
| - buf.WriteByte(byte(PTBlobKey))
|
| + buf.WriteByte(byte(ds.PTBlobKey))
|
| _, err := ReadKey(buf, WithContext, "", "")
|
| So(err, ShouldErrLike, "invalid type PTBlobKey")
|
| })
|
| @@ -220,7 +303,7 @@ func TestSerializationReadMisc(t *testing.T) {
|
| cmpbin.WriteString(buf, "ns")
|
| cmpbin.WriteUint(buf, 2)
|
| cmpbin.WriteString(buf, "hi")
|
| - buf.WriteByte(byte(PTInt))
|
| + buf.WriteByte(byte(ds.PTInt))
|
| cmpbin.WriteInt(buf, -2)
|
| _, err := ReadKey(buf, WithContext, "", "")
|
| So(err, ShouldErrLike, "zero/negative")
|
| @@ -229,20 +312,20 @@ func TestSerializationReadMisc(t *testing.T) {
|
| })
|
|
|
| Convey("ReadGeoPoint", func() {
|
| - gp := GeoPoint{}
|
| + buf := mkBuf(nil)
|
| Convey("trunc 1", func() {
|
| - err := gp.Read(buf)
|
| + _, err := ReadGeoPoint(buf)
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("trunc 2", func() {
|
| cmpbin.WriteFloat64(buf, 100)
|
| - err := gp.Read(buf)
|
| + _, err := ReadGeoPoint(buf)
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("invalid", func() {
|
| cmpbin.WriteFloat64(buf, 100)
|
| cmpbin.WriteFloat64(buf, 1000)
|
| - err := gp.Read(buf)
|
| + _, err := ReadGeoPoint(buf)
|
| So(err, ShouldErrLike, "invalid GeoPoint")
|
| })
|
| })
|
| @@ -252,79 +335,121 @@ func TestSerializationReadMisc(t *testing.T) {
|
| pst, err := time.LoadLocation("America/Los_Angeles")
|
| So(err, ShouldBeNil)
|
| So(func() {
|
| - WriteTime(buf, time.Now().In(pst))
|
| + WriteTime(mkBuf(nil), time.Now().In(pst))
|
| }, ShouldPanic)
|
| })
|
| })
|
|
|
| Convey("ReadTime", func() {
|
| Convey("trunc 1", func() {
|
| - _, err := ReadTime(buf)
|
| + _, err := ReadTime(mkBuf(nil))
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| })
|
|
|
| Convey("ReadProperty", func() {
|
| - p := Property{}
|
| + buf := mkBuf(nil)
|
| Convey("trunc 1", func() {
|
| - err := p.Read(buf, WithContext, "", "")
|
| + p, err := ReadProperty(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| - So(p.Type(), ShouldEqual, PTNull)
|
| + So(p.Type(), ShouldEqual, ds.PTNull)
|
| So(p.Value(), ShouldBeNil)
|
| })
|
| Convey("trunc (PTBytes)", func() {
|
| - buf.WriteByte(byte(PTBytes))
|
| - err := p.Read(buf, WithContext, "", "")
|
| + buf.WriteByte(byte(ds.PTBytes))
|
| + _, err := ReadProperty(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("trunc (PTBlobKey)", func() {
|
| - buf.WriteByte(byte(PTBlobKey))
|
| - err := p.Read(buf, WithContext, "", "")
|
| + buf.WriteByte(byte(ds.PTBlobKey))
|
| + _, err := ReadProperty(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("invalid type", func() {
|
| - buf.WriteByte(byte(PTUnknown + 1))
|
| - err := p.Read(buf, WithContext, "", "")
|
| + buf.WriteByte(byte(ds.PTUnknown + 1))
|
| + _, err := ReadProperty(buf, WithContext, "", "")
|
| So(err, ShouldErrLike, "unknown type!")
|
| })
|
| })
|
|
|
| Convey("ReadPropertyMap", func() {
|
| - pm := PropertyMap{}
|
| + buf := mkBuf(nil)
|
| Convey("trunc 1", func() {
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("too many rows", func() {
|
| cmpbin.WriteUint(buf, 1000000)
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldErrLike, "huge number of rows")
|
| })
|
| Convey("trunc 2", func() {
|
| cmpbin.WriteUint(buf, 10)
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("trunc 3", func() {
|
| cmpbin.WriteUint(buf, 10)
|
| cmpbin.WriteString(buf, "ohai")
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| Convey("too many values", func() {
|
| cmpbin.WriteUint(buf, 10)
|
| cmpbin.WriteString(buf, "ohai")
|
| cmpbin.WriteUint(buf, 100000)
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldErrLike, "huge number of properties")
|
| })
|
| Convey("trunc 4", func() {
|
| cmpbin.WriteUint(buf, 10)
|
| cmpbin.WriteString(buf, "ohai")
|
| cmpbin.WriteUint(buf, 10)
|
| - err := pm.Read(buf, WithContext, "", "")
|
| + _, err := ReadPropertyMap(buf, WithContext, "", "")
|
| So(err, ShouldEqual, io.EOF)
|
| })
|
| })
|
| +
|
| + Convey("IndexDefinition", func() {
|
| + id := ds.IndexDefinition{Kind: "kind"}
|
| + data := ToBytes(id)
|
| + So(string(data), ShouldStartWith, string(ds.IndexBuiltinQueryPrefix()))
|
| + newID, err := ReadIndexDefinition(mkBuf(data))
|
| + So(err, ShouldBeNil)
|
| + So(newID, ShouldResemble, id)
|
| +
|
| + id.SortBy = append(id.SortBy, ds.IndexColumn{Property: "prop"})
|
| + data = ToBytes(id)
|
| + So(string(data), ShouldStartWith, string(ds.IndexBuiltinQueryPrefix()))
|
| + newID, err = ReadIndexDefinition(mkBuf(data))
|
| + So(err, ShouldBeNil)
|
| + So(newID, ShouldResemble, id)
|
| +
|
| + id.SortBy = append(id.SortBy, ds.IndexColumn{Property: "other", Direction: ds.DESCENDING})
|
| + id.Ancestor = true
|
| + data = ToBytes(id)
|
| + So(string(data), ShouldStartWith, string(ds.IndexComplexQueryPrefix()))
|
| + newID, err = ReadIndexDefinition(mkBuf(data))
|
| + So(err, ShouldBeNil)
|
| + So(newID, ShouldResemble, id)
|
| +
|
| + // invalid
|
| + id.SortBy = append(id.SortBy, ds.IndexColumn{Property: "", Direction: ds.DESCENDING})
|
| + data = ToBytes(id)
|
| + So(string(data), ShouldStartWith, string(ds.IndexComplexQueryPrefix()))
|
| + newID, err = ReadIndexDefinition(mkBuf(data))
|
| + So(err, ShouldBeNil)
|
| + So(newID, ShouldResemble, id)
|
| +
|
| + Convey("too many", func() {
|
| + id := ds.IndexDefinition{Kind: "wat"}
|
| + for i := 0; i < MaxIndexColumns+1; i++ {
|
| + id.SortBy = append(id.SortBy, ds.IndexColumn{Property: "Hi", Direction: ds.ASCENDING})
|
| + }
|
| + data := ToBytes(id)
|
| + newID, err = ReadIndexDefinition(mkBuf(data))
|
| + So(err, ShouldErrLike, "over 64 sort orders")
|
| + })
|
| + })
|
| })
|
| }
|
|
|