| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package datastore | |
| 6 | |
| 7 import ( | |
| 8 "bytes" | |
| 9 "io" | |
| 10 "testing" | |
| 11 "time" | |
| 12 | |
| 13 "github.com/luci/gae/service/blobstore" | |
| 14 "github.com/luci/luci-go/common/cmpbin" | |
| 15 . "github.com/smartystreets/goconvey/convey" | |
| 16 ) | |
| 17 | |
| 18 func init() { | |
| 19 WritePropertyMapDeterministic = true | |
| 20 } | |
| 21 | |
| 22 type dspmapTC struct { | |
| 23 name string | |
| 24 props PropertyMap | |
| 25 } | |
| 26 | |
| 27 func TestPropertyMapSerialization(t *testing.T) { | |
| 28 t.Parallel() | |
| 29 | |
| 30 tests := []dspmapTC{ | |
| 31 { | |
| 32 "basic", | |
| 33 PropertyMap{ | |
| 34 "R": {mp(false), mp(2.1), mpNI(3)}, | |
| 35 "S": {mp("hello"), mp("world")}, | |
| 36 }, | |
| 37 }, | |
| 38 { | |
| 39 "keys", | |
| 40 PropertyMap{ | |
| 41 "DS": {mp(mkKey("appy", "ns", "Foo", 7)),
mp(mkKey("other", "", "Yot", "wheeep"))}, | |
| 42 "blobstore": {mp(blobstore.Key("sup")), mp(blobs
tore.Key("nerds"))}, | |
| 43 }, | |
| 44 }, | |
| 45 { | |
| 46 "geo", | |
| 47 PropertyMap{ | |
| 48 "G": {mp(GeoPoint{Lat: 1, Lng: 2})}, | |
| 49 }, | |
| 50 }, | |
| 51 { | |
| 52 "data", | |
| 53 PropertyMap{ | |
| 54 "S": {mp("sup"), mp("fool"), mp("nerd")
}, | |
| 55 "D.Foo.Nerd": {mp([]byte("sup")), mp([]byte("foo
l"))}, | |
| 56 "B": {mp(ByteString("sup")), mp(ByteStr
ing("fool"))}, | |
| 57 }, | |
| 58 }, | |
| 59 { | |
| 60 "time", | |
| 61 PropertyMap{ | |
| 62 "T": { | |
| 63 mp(time.Now().UTC()), | |
| 64 mp(time.Now().Add(time.Second).UTC())}, | |
| 65 }, | |
| 66 }, | |
| 67 { | |
| 68 "empty vals", | |
| 69 PropertyMap{ | |
| 70 "T": {mp(true), mp(true)}, | |
| 71 "F": {mp(false), mp(false)}, | |
| 72 "N": {mp(nil), mp(nil)}, | |
| 73 "E": {}, | |
| 74 }, | |
| 75 }, | |
| 76 } | |
| 77 | |
| 78 Convey("PropertyMap serialization", t, func() { | |
| 79 Convey("round trip", func() { | |
| 80 for _, tc := range tests { | |
| 81 tc := tc | |
| 82 Convey(tc.name, func() { | |
| 83 buf := &bytes.Buffer{} | |
| 84 tc.props.Write(buf, WithContext) | |
| 85 dec := PropertyMap{} | |
| 86 err := dec.Read(buf, WithContext, "", ""
) | |
| 87 So(err, ShouldBeNil) | |
| 88 So(dec, ShouldResemble, tc.props) | |
| 89 }) | |
| 90 } | |
| 91 }) | |
| 92 }) | |
| 93 } | |
| 94 | |
| 95 func TestSerializationReadMisc(t *testing.T) { | |
| 96 t.Parallel() | |
| 97 | |
| 98 Convey("Misc Serialization tests", t, func() { | |
| 99 buf := &bytes.Buffer{} | |
| 100 Convey("ReadKey", func() { | |
| 101 Convey("good cases", func() { | |
| 102 Convey("w/ ctx decodes normally w/ ctx", func()
{ | |
| 103 k := mkKey("aid", "ns", "knd", "yo", "ot
her", 10) | |
| 104 WriteKey(buf, WithContext, k) | |
| 105 dk, err := ReadKey(buf, WithContext, "",
"") | |
| 106 So(err, ShouldBeNil) | |
| 107 So(dk, ShouldEqualKey, k) | |
| 108 }) | |
| 109 Convey("w/ ctx decodes normally w/o ctx", func()
{ | |
| 110 k := mkKey("aid", "ns", "knd", "yo", "ot
her", 10) | |
| 111 WriteKey(buf, WithContext, k) | |
| 112 dk, err := ReadKey(buf, WithoutContext,
"spam", "nerd") | |
| 113 So(err, ShouldBeNil) | |
| 114 So(dk, ShouldEqualKey, mkKey("spam", "ne
rd", "knd", "yo", "other", 10)) | |
| 115 }) | |
| 116 Convey("w/o ctx decodes normally w/ ctx", func()
{ | |
| 117 k := mkKey("aid", "ns", "knd", "yo", "ot
her", 10) | |
| 118 WriteKey(buf, WithoutContext, k) | |
| 119 dk, err := ReadKey(buf, WithContext, "sp
am", "nerd") | |
| 120 So(err, ShouldBeNil) | |
| 121 So(dk, ShouldEqualKey, mkKey("", "", "kn
d", "yo", "other", 10)) | |
| 122 }) | |
| 123 Convey("w/o ctx decodes normally w/o ctx", func(
) { | |
| 124 k := mkKey("aid", "ns", "knd", "yo", "ot
her", 10) | |
| 125 WriteKey(buf, WithoutContext, k) | |
| 126 dk, err := ReadKey(buf, WithoutContext,
"spam", "nerd") | |
| 127 So(err, ShouldBeNil) | |
| 128 So(dk, ShouldEqualKey, mkKey("spam", "ne
rd", "knd", "yo", "other", 10)) | |
| 129 }) | |
| 130 Convey("IntIDs always sort before StringIDs", fu
nc() { | |
| 131 // -1 writes as almost all 1's in the fi
rst byte under cmpbin, even | |
| 132 // though it's technically not a valid k
ey. | |
| 133 k := mkKey("aid", "ns", "knd", -1) | |
| 134 WriteKey(buf, WithoutContext, k) | |
| 135 | |
| 136 k = mkKey("aid", "ns", "knd", "hat") | |
| 137 buf2 := &bytes.Buffer{} | |
| 138 WriteKey(buf2, WithoutContext, k) | |
| 139 | |
| 140 So(bytes.Compare(buf.Bytes(), buf2.Bytes
()), ShouldBeLessThan, 0) | |
| 141 }) | |
| 142 }) | |
| 143 | |
| 144 Convey("err cases", func() { | |
| 145 Convey("nil", func() { | |
| 146 _, err := ReadKey(buf, WithContext, "",
"") | |
| 147 So(err, ShouldEqual, io.EOF) | |
| 148 }) | |
| 149 Convey("str", func() { | |
| 150 buf.WriteString("sup") | |
| 151 _, err := ReadKey(buf, WithContext, "",
"") | |
| 152 So(err, ShouldErrLike, "expected actualC
tx") | |
| 153 }) | |
| 154 Convey("truncated 1", func() { | |
| 155 buf.WriteByte(1) // actualCtx == 1 | |
| 156 _, err := ReadKey(buf, WithContext, "",
"") | |
| 157 So(err, ShouldEqual, io.EOF) | |
| 158 }) | |
| 159 Convey("truncated 2", func() { | |
| 160 buf.WriteByte(1) // actualCtx == 1 | |
| 161 cmpbin.WriteString(buf, "aid") | |
| 162 _, err := ReadKey(buf, WithContext, "",
"") | |
| 163 So(err, ShouldEqual, io.EOF) | |
| 164 }) | |
| 165 Convey("truncated 3", func() { | |
| 166 buf.WriteByte(1) // actualCtx == 1 | |
| 167 cmpbin.WriteString(buf, "aid") | |
| 168 cmpbin.WriteString(buf, "ns") | |
| 169 _, err := ReadKey(buf, WithContext, "",
"") | |
| 170 So(err, ShouldEqual, io.EOF) | |
| 171 }) | |
| 172 Convey("huge key", func() { | |
| 173 buf.WriteByte(1) // actualCtx == 1 | |
| 174 cmpbin.WriteString(buf, "aid") | |
| 175 cmpbin.WriteString(buf, "ns") | |
| 176 cmpbin.WriteUint(buf, 1000) | |
| 177 _, err := ReadKey(buf, WithContext, "",
"") | |
| 178 So(err, ShouldErrLike, "huge key") | |
| 179 }) | |
| 180 Convey("insufficient tokens", func() { | |
| 181 buf.WriteByte(1) // actualCtx == 1 | |
| 182 cmpbin.WriteString(buf, "aid") | |
| 183 cmpbin.WriteString(buf, "ns") | |
| 184 cmpbin.WriteUint(buf, 2) | |
| 185 _, err := ReadKey(buf, WithContext, "",
"") | |
| 186 So(err, ShouldEqual, io.EOF) | |
| 187 }) | |
| 188 Convey("partial token 1", func() { | |
| 189 buf.WriteByte(1) // actualCtx == 1 | |
| 190 cmpbin.WriteString(buf, "aid") | |
| 191 cmpbin.WriteString(buf, "ns") | |
| 192 cmpbin.WriteUint(buf, 2) | |
| 193 cmpbin.WriteString(buf, "hi") | |
| 194 _, err := ReadKey(buf, WithContext, "",
"") | |
| 195 So(err, ShouldEqual, io.EOF) | |
| 196 }) | |
| 197 Convey("partial token 2", func() { | |
| 198 buf.WriteByte(1) // actualCtx == 1 | |
| 199 cmpbin.WriteString(buf, "aid") | |
| 200 cmpbin.WriteString(buf, "ns") | |
| 201 cmpbin.WriteUint(buf, 2) | |
| 202 cmpbin.WriteString(buf, "hi") | |
| 203 buf.WriteByte(byte(PTString)) | |
| 204 _, err := ReadKey(buf, WithContext, "",
"") | |
| 205 So(err, ShouldEqual, io.EOF) | |
| 206 }) | |
| 207 Convey("bad token (invalid type)", func() { | |
| 208 buf.WriteByte(1) // actualCtx == 1 | |
| 209 cmpbin.WriteString(buf, "aid") | |
| 210 cmpbin.WriteString(buf, "ns") | |
| 211 cmpbin.WriteUint(buf, 2) | |
| 212 cmpbin.WriteString(buf, "hi") | |
| 213 buf.WriteByte(byte(PTBlobKey)) | |
| 214 _, err := ReadKey(buf, WithContext, "",
"") | |
| 215 So(err, ShouldErrLike, "invalid type PTB
lobKey") | |
| 216 }) | |
| 217 Convey("bad token (invalid IntID)", func() { | |
| 218 buf.WriteByte(1) // actualCtx == 1 | |
| 219 cmpbin.WriteString(buf, "aid") | |
| 220 cmpbin.WriteString(buf, "ns") | |
| 221 cmpbin.WriteUint(buf, 2) | |
| 222 cmpbin.WriteString(buf, "hi") | |
| 223 buf.WriteByte(byte(PTInt)) | |
| 224 cmpbin.WriteInt(buf, -2) | |
| 225 _, err := ReadKey(buf, WithContext, "",
"") | |
| 226 So(err, ShouldErrLike, "zero/negative") | |
| 227 }) | |
| 228 }) | |
| 229 }) | |
| 230 | |
| 231 Convey("ReadGeoPoint", func() { | |
| 232 gp := GeoPoint{} | |
| 233 Convey("trunc 1", func() { | |
| 234 err := gp.Read(buf) | |
| 235 So(err, ShouldEqual, io.EOF) | |
| 236 }) | |
| 237 Convey("trunc 2", func() { | |
| 238 cmpbin.WriteFloat64(buf, 100) | |
| 239 err := gp.Read(buf) | |
| 240 So(err, ShouldEqual, io.EOF) | |
| 241 }) | |
| 242 Convey("invalid", func() { | |
| 243 cmpbin.WriteFloat64(buf, 100) | |
| 244 cmpbin.WriteFloat64(buf, 1000) | |
| 245 err := gp.Read(buf) | |
| 246 So(err, ShouldErrLike, "invalid GeoPoint") | |
| 247 }) | |
| 248 }) | |
| 249 | |
| 250 Convey("WriteTime", func() { | |
| 251 Convey("in non-UTC!", func() { | |
| 252 pst, err := time.LoadLocation("America/Los_Angel
es") | |
| 253 So(err, ShouldBeNil) | |
| 254 So(func() { | |
| 255 WriteTime(buf, time.Now().In(pst)) | |
| 256 }, ShouldPanic) | |
| 257 }) | |
| 258 }) | |
| 259 | |
| 260 Convey("ReadTime", func() { | |
| 261 Convey("trunc 1", func() { | |
| 262 _, err := ReadTime(buf) | |
| 263 So(err, ShouldEqual, io.EOF) | |
| 264 }) | |
| 265 }) | |
| 266 | |
| 267 Convey("ReadProperty", func() { | |
| 268 p := Property{} | |
| 269 Convey("trunc 1", func() { | |
| 270 err := p.Read(buf, WithContext, "", "") | |
| 271 So(err, ShouldEqual, io.EOF) | |
| 272 So(p.Type(), ShouldEqual, PTNull) | |
| 273 So(p.Value(), ShouldBeNil) | |
| 274 }) | |
| 275 Convey("trunc (PTBytes)", func() { | |
| 276 buf.WriteByte(byte(PTBytes)) | |
| 277 err := p.Read(buf, WithContext, "", "") | |
| 278 So(err, ShouldEqual, io.EOF) | |
| 279 }) | |
| 280 Convey("trunc (PTBlobKey)", func() { | |
| 281 buf.WriteByte(byte(PTBlobKey)) | |
| 282 err := p.Read(buf, WithContext, "", "") | |
| 283 So(err, ShouldEqual, io.EOF) | |
| 284 }) | |
| 285 Convey("invalid type", func() { | |
| 286 buf.WriteByte(byte(PTUnknown + 1)) | |
| 287 err := p.Read(buf, WithContext, "", "") | |
| 288 So(err, ShouldErrLike, "unknown type!") | |
| 289 }) | |
| 290 }) | |
| 291 | |
| 292 Convey("ReadPropertyMap", func() { | |
| 293 pm := PropertyMap{} | |
| 294 Convey("trunc 1", func() { | |
| 295 err := pm.Read(buf, WithContext, "", "") | |
| 296 So(err, ShouldEqual, io.EOF) | |
| 297 }) | |
| 298 Convey("too many rows", func() { | |
| 299 cmpbin.WriteUint(buf, 1000000) | |
| 300 err := pm.Read(buf, WithContext, "", "") | |
| 301 So(err, ShouldErrLike, "huge number of rows") | |
| 302 }) | |
| 303 Convey("trunc 2", func() { | |
| 304 cmpbin.WriteUint(buf, 10) | |
| 305 err := pm.Read(buf, WithContext, "", "") | |
| 306 So(err, ShouldEqual, io.EOF) | |
| 307 }) | |
| 308 Convey("trunc 3", func() { | |
| 309 cmpbin.WriteUint(buf, 10) | |
| 310 cmpbin.WriteString(buf, "ohai") | |
| 311 err := pm.Read(buf, WithContext, "", "") | |
| 312 So(err, ShouldEqual, io.EOF) | |
| 313 }) | |
| 314 Convey("too many values", func() { | |
| 315 cmpbin.WriteUint(buf, 10) | |
| 316 cmpbin.WriteString(buf, "ohai") | |
| 317 cmpbin.WriteUint(buf, 100000) | |
| 318 err := pm.Read(buf, WithContext, "", "") | |
| 319 So(err, ShouldErrLike, "huge number of propertie
s") | |
| 320 }) | |
| 321 Convey("trunc 4", func() { | |
| 322 cmpbin.WriteUint(buf, 10) | |
| 323 cmpbin.WriteString(buf, "ohai") | |
| 324 cmpbin.WriteUint(buf, 10) | |
| 325 err := pm.Read(buf, WithContext, "", "") | |
| 326 So(err, ShouldEqual, io.EOF) | |
| 327 }) | |
| 328 }) | |
| 329 }) | |
| 330 } | |
| OLD | NEW |