| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package cloud | 5 package cloud |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "crypto/rand" | 8 "crypto/rand" |
| 9 "encoding/hex" | 9 "encoding/hex" |
| 10 "fmt" | 10 "fmt" |
| 11 "os" | 11 "os" |
| 12 "testing" | 12 "testing" |
| 13 "time" | 13 "time" |
| 14 | 14 |
| 15 ds "github.com/luci/gae/service/datastore" | 15 ds "github.com/luci/gae/service/datastore" |
| 16 "github.com/luci/gae/service/info" | 16 "github.com/luci/gae/service/info" |
| 17 | 17 |
| 18 "cloud.google.com/go/datastore" | 18 "cloud.google.com/go/datastore" |
| 19 "github.com/luci/luci-go/common/errors" | 19 "github.com/luci/luci-go/common/errors" |
| 20 "golang.org/x/net/context" | 20 "golang.org/x/net/context" |
| 21 | 21 |
| 22 . "github.com/smartystreets/goconvey/convey" | 22 . "github.com/smartystreets/goconvey/convey" |
| 23 ) | 23 ) |
| 24 | 24 |
| 25 func mkProperties(index bool, vals ...interface{}) []ds.Property { | 25 func mkProperties(index bool, forceMulti bool, vals ...interface{}) ds.PropertyD
ata { |
| 26 indexSetting := ds.ShouldIndex | 26 indexSetting := ds.ShouldIndex |
| 27 if !index { | 27 if !index { |
| 28 indexSetting = ds.NoIndex | 28 indexSetting = ds.NoIndex |
| 29 } | 29 } |
| 30 | 30 |
| 31 » result := make([]ds.Property, len(vals)) | 31 » if len(vals) == 1 && !forceMulti { |
| 32 » » var prop ds.Property |
| 33 » » prop.SetValue(vals[0], indexSetting) |
| 34 » » return prop |
| 35 » } |
| 36 |
| 37 » result := make(ds.PropertySlice, len(vals)) |
| 32 for i, v := range vals { | 38 for i, v := range vals { |
| 33 result[i].SetValue(v, indexSetting) | 39 result[i].SetValue(v, indexSetting) |
| 34 } | 40 } |
| 35 return result | 41 return result |
| 36 } | 42 } |
| 37 | 43 |
| 38 func mkp(vals ...interface{}) []ds.Property { return mkProperties(true, vals..
.) } | 44 func mkp(vals ...interface{}) ds.PropertyData { return mkProperties(true, fals
e, vals...) } |
| 39 func mkpNI(vals ...interface{}) []ds.Property { return mkProperties(false, vals.
..) } | 45 func mkpNI(vals ...interface{}) ds.PropertyData { return mkProperties(false, fal
se, vals...) } |
| 40 | 46 |
| 41 // TestDatastore tests the cloud datastore implementation. | 47 // TestDatastore tests the cloud datastore implementation. |
| 42 // | 48 // |
| 43 // This test uses the gcloud datastore emulator. Like the Go datastore package, | 49 // This test uses the gcloud datastore emulator. Like the Go datastore package, |
| 44 // the emulator must use the gRPC interface. At the time of writing, the | 50 // the emulator must use the gRPC interface. At the time of writing, the |
| 45 // emulator included with the "gcloud" tool is an older emulator that does NOT | 51 // emulator included with the "gcloud" tool is an older emulator that does NOT |
| 46 // support gRPC. | 52 // support gRPC. |
| 47 // | 53 // |
| 48 // Download the emulator linked here: | 54 // Download the emulator linked here: |
| 49 // https://code.google.com/p/google-cloud-sdk/issues/detail?id=719#c3 | 55 // https://code.google.com/p/google-cloud-sdk/issues/detail?id=719#c3 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 67 return | 73 return |
| 68 } | 74 } |
| 69 | 75 |
| 70 Convey(fmt.Sprintf(`A cloud installation using datastore emulator %q`, e
mulatorHost), t, func() { | 76 Convey(fmt.Sprintf(`A cloud installation using datastore emulator %q`, e
mulatorHost), t, func() { |
| 71 c := context.Background() | 77 c := context.Background() |
| 72 client, err := datastore.NewClient(c, "luci-gae-test") | 78 client, err := datastore.NewClient(c, "luci-gae-test") |
| 73 So(err, ShouldBeNil) | 79 So(err, ShouldBeNil) |
| 74 defer client.Close() | 80 defer client.Close() |
| 75 | 81 |
| 76 testTime := ds.RoundTime(time.Date(2016, 1, 1, 0, 0, 0, 0, time.
UTC)) | 82 testTime := ds.RoundTime(time.Date(2016, 1, 1, 0, 0, 0, 0, time.
UTC)) |
| 83 _ = testTime |
| 77 | 84 |
| 78 c = Use(c, client) | 85 c = Use(c, client) |
| 79 | 86 |
| 80 Convey(`Supports namespaces`, func() { | 87 Convey(`Supports namespaces`, func() { |
| 81 namespaces := []string{"foo", "bar", "baz"} | 88 namespaces := []string{"foo", "bar", "baz"} |
| 82 | 89 |
| 83 // Clear all used entities from all namespaces. | 90 // Clear all used entities from all namespaces. |
| 84 for _, ns := range namespaces { | 91 for _, ns := range namespaces { |
| 85 nsCtx := info.Get(c).MustNamespace(ns) | 92 nsCtx := info.Get(c).MustNamespace(ns) |
| 86 di := ds.Get(nsCtx) | 93 di := ds.Get(nsCtx) |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 So(merr[1], ShouldEqual, ds.ErrNoSuchEntity) | 191 So(merr[1], ShouldEqual, ds.ErrNoSuchEntity) |
| 185 So(merr[2], ShouldBeNil) | 192 So(merr[2], ShouldBeNil) |
| 186 | 193 |
| 187 // put[1] will not be retrieved (delete) | 194 // put[1] will not be retrieved (delete) |
| 188 put[1] = get[1] | 195 put[1] = get[1] |
| 189 So(get, ShouldResemble, put) | 196 So(get, ShouldResemble, put) |
| 190 }) | 197 }) |
| 191 | 198 |
| 192 Convey(`Can put and get all supported entity fields.`, f
unc() { | 199 Convey(`Can put and get all supported entity fields.`, f
unc() { |
| 193 put := ds.PropertyMap{ | 200 put := ds.PropertyMap{ |
| 194 » » » » » "$id": mkpNI("foo"), | 201 » » » » » "$id": mkpNI("foo"), |
| 195 » » » » » "$kind": mkpNI("FooType"), | 202 » » » » » "$kind": mkpNI("FooType"), |
| 196 » » » » » "Number": mkp(1337), | 203 |
| 197 » » » » » "String": mkpNI("hello"), | 204 » » » » » "Number": mkp(1337), |
| 198 » » » » » "Bytes": mkp([]byte("world")), | 205 » » » » » "String": mkpNI("hello"), |
| 199 » » » » » "Time": mkp(testTime), | 206 » » » » » "Bytes": mkp([]byte("world")), |
| 200 » » » » » "Float": mkpNI(3.14), | 207 » » » » » "Time": mkp(testTime), |
| 201 » » » » » "Key": mkp(di.MakeKey("Parent", "Pare
ntID", "Child", 1337)), | 208 » » » » » "Float": mkpNI(3.14), |
| 209 » » » » » "Key": mkp(di.MakeKey("Parent", "P
arentID", "Child", 1337)), |
| 210 » » » » » "Null": mkp(nil), |
| 211 » » » » » "NullSlice": mkp(nil, nil), |
| 202 | 212 |
| 203 "ComplexSlice": mkp(1337, "string", []by
te("bytes"), testTime, float32(3.14), | 213 "ComplexSlice": mkp(1337, "string", []by
te("bytes"), testTime, float32(3.14), |
| 204 » » » » » » float64(2.71), true, di.MakeKey(
"SomeKey", "SomeID")), | 214 » » » » » » float64(2.71), true, nil, di.Mak
eKey("SomeKey", "SomeID")), |
| 215 |
| 216 » » » » » "Single": mkp("single"), |
| 217 » » » » » "SingleSlice": mkProperties(true, true,
"single"), // Force a single "multi" value. |
| 218 » » » » » "EmptySlice": ds.PropertySlice(nil), |
| 205 } | 219 } |
| 206 So(di.Put(put), ShouldBeNil) | 220 So(di.Put(put), ShouldBeNil) |
| 207 delete(put, "$key") | 221 delete(put, "$key") |
| 208 | 222 |
| 209 get := ds.PropertyMap{ | 223 get := ds.PropertyMap{ |
| 210 "$id": mkpNI("foo"), | 224 "$id": mkpNI("foo"), |
| 211 "$kind": mkpNI("FooType"), | 225 "$kind": mkpNI("FooType"), |
| 212 } | 226 } |
| 213 So(di.Get(get), ShouldBeNil) | 227 So(di.Get(get), ShouldBeNil) |
| 214 So(get, ShouldResemble, put) | 228 So(get, ShouldResemble, put) |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 pmap := ds.PropertyMap{"$kind": mkp("Tes
t"), "$id": mkp("quux")} | 299 pmap := ds.PropertyMap{"$kind": mkp("Tes
t"), "$id": mkp("quux")} |
| 286 err = di.RunInTransaction(func(c context
.Context) error { | 300 err = di.RunInTransaction(func(c context
.Context) error { |
| 287 return ds.Get(c).Get(pmap) | 301 return ds.Get(c).Get(pmap) |
| 288 }, nil) | 302 }, nil) |
| 289 So(err, ShouldEqual, ds.ErrNoSuchEntity) | 303 So(err, ShouldEqual, ds.ErrNoSuchEntity) |
| 290 }) | 304 }) |
| 291 }) | 305 }) |
| 292 }) | 306 }) |
| 293 }) | 307 }) |
| 294 } | 308 } |
| OLD | NEW |