aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/google.golang.org/appengine/datastore/datastore_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/appengine/datastore/datastore_test.go')
-rw-r--r--vendor/google.golang.org/appengine/datastore/datastore_test.go1750
1 files changed, 0 insertions, 1750 deletions
diff --git a/vendor/google.golang.org/appengine/datastore/datastore_test.go b/vendor/google.golang.org/appengine/datastore/datastore_test.go
deleted file mode 100644
index 683cd15..0000000
--- a/vendor/google.golang.org/appengine/datastore/datastore_test.go
+++ /dev/null
@@ -1,1750 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-// Use of this source code is governed by the Apache 2.0
-// license that can be found in the LICENSE file.
-
-package datastore
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "reflect"
- "sort"
- "strings"
- "testing"
- "time"
-
- "google.golang.org/appengine"
- "google.golang.org/appengine/internal/aetesting"
- pb "google.golang.org/appengine/internal/datastore"
-)
-
-const testAppID = "testApp"
-
-type (
- myBlob []byte
- myByte byte
- myString string
-)
-
-func makeMyByteSlice(n int) []myByte {
- b := make([]myByte, n)
- for i := range b {
- b[i] = myByte(i)
- }
- return b
-}
-
-func makeInt8Slice(n int) []int8 {
- b := make([]int8, n)
- for i := range b {
- b[i] = int8(i)
- }
- return b
-}
-
-func makeUint8Slice(n int) []uint8 {
- b := make([]uint8, n)
- for i := range b {
- b[i] = uint8(i)
- }
- return b
-}
-
-func newKey(stringID string, parent *Key) *Key {
- return &Key{
- kind: "kind",
- stringID: stringID,
- intID: 0,
- parent: parent,
- appID: testAppID,
- }
-}
-
-var (
- testKey0 = newKey("name0", nil)
- testKey1a = newKey("name1", nil)
- testKey1b = newKey("name1", nil)
- testKey2a = newKey("name2", testKey0)
- testKey2b = newKey("name2", testKey0)
- testGeoPt0 = appengine.GeoPoint{Lat: 1.2, Lng: 3.4}
- testGeoPt1 = appengine.GeoPoint{Lat: 5, Lng: 10}
- testBadGeoPt = appengine.GeoPoint{Lat: 1000, Lng: 34}
-
- now = time.Unix(1e9, 0).UTC()
-)
-
-type B0 struct {
- B []byte
-}
-
-type B1 struct {
- B []int8
-}
-
-type B2 struct {
- B myBlob
-}
-
-type B3 struct {
- B []myByte
-}
-
-type B4 struct {
- B [][]byte
-}
-
-type B5 struct {
- B ByteString
-}
-
-type C0 struct {
- I int
- C chan int
-}
-
-type C1 struct {
- I int
- C *chan int
-}
-
-type C2 struct {
- I int
- C []chan int
-}
-
-type C3 struct {
- C string
-}
-
-type E struct{}
-
-type G0 struct {
- G appengine.GeoPoint
-}
-
-type G1 struct {
- G []appengine.GeoPoint
-}
-
-type K0 struct {
- K *Key
-}
-
-type K1 struct {
- K []*Key
-}
-
-type S struct {
- St string
-}
-
-type NoOmit struct {
- A string
- B int `datastore:"Bb"`
- C bool `datastore:",noindex"`
-}
-
-type OmitAll struct {
- A string `datastore:",omitempty"`
- B int `datastore:"Bb,omitempty"`
- C bool `datastore:",omitempty,noindex"`
- D time.Time `datastore:",omitempty"`
- F []int `datastore:",omitempty"`
-}
-
-type Omit struct {
- A string `datastore:",omitempty"`
- B int `datastore:"Bb,omitempty"`
- C bool `datastore:",omitempty,noindex"`
- D time.Time `datastore:",omitempty"`
- F []int `datastore:",omitempty"`
- S `datastore:",omitempty"`
-}
-
-type NoOmits struct {
- No []NoOmit `datastore:",omitempty"`
- S `datastore:",omitempty"`
- Ss S `datastore:",omitempty"`
-}
-
-type N0 struct {
- X0
- Nonymous X0
- Ignore string `datastore:"-"`
- Other string
-}
-
-type N1 struct {
- X0
- Nonymous []X0
- Ignore string `datastore:"-"`
- Other string
-}
-
-type N2 struct {
- N1 `datastore:"red"`
- Green N1 `datastore:"green"`
- Blue N1
- White N1 `datastore:"-"`
-}
-
-type O0 struct {
- I int64
-}
-
-type O1 struct {
- I int32
-}
-
-type U0 struct {
- U uint
-}
-
-type U1 struct {
- U string
-}
-
-type T struct {
- T time.Time
-}
-
-type X0 struct {
- S string
- I int
- i int
-}
-
-type X1 struct {
- S myString
- I int32
- J int64
-}
-
-type X2 struct {
- Z string
- i int
-}
-
-type X3 struct {
- S bool
- I int
-}
-
-type Y0 struct {
- B bool
- F []float64
- G []float64
-}
-
-type Y1 struct {
- B bool
- F float64
-}
-
-type Y2 struct {
- B bool
- F []int64
-}
-
-type Tagged struct {
- A int `datastore:"a,noindex"`
- B []int `datastore:"b"`
- C int `datastore:",noindex"`
- D int `datastore:""`
- E int
- // The "flatten" option is parsed but ignored for now.
- F int `datastore:",noindex,flatten"`
- G int `datastore:",flatten"`
- I int `datastore:"-"`
- J int `datastore:",noindex" json:"j"`
-
- Y0 `datastore:"-"`
- Z chan int `datastore:"-,"`
-}
-
-type InvalidTagged1 struct {
- I int `datastore:"\t"`
-}
-
-type InvalidTagged2 struct {
- I int
- J int `datastore:"I"`
-}
-
-type Inner1 struct {
- W int32
- X string
-}
-
-type Inner2 struct {
- Y float64
-}
-
-type Inner3 struct {
- Z bool
-}
-
-type Outer struct {
- A int16
- I []Inner1
- J Inner2
- Inner3
-}
-
-type OuterEquivalent struct {
- A int16
- IDotW []int32 `datastore:"I.W"`
- IDotX []string `datastore:"I.X"`
- JDotY float64 `datastore:"J.Y"`
- Z bool
-}
-
-type Dotted struct {
- A DottedA `datastore:"A0.A1.A2"`
-}
-
-type DottedA struct {
- B DottedB `datastore:"B3"`
-}
-
-type DottedB struct {
- C int `datastore:"C4.C5"`
-}
-
-type SliceOfSlices struct {
- I int
- S []struct {
- J int
- F []float64
- }
-}
-
-type Recursive struct {
- I int
- R []Recursive
-}
-
-type MutuallyRecursive0 struct {
- I int
- R []MutuallyRecursive1
-}
-
-type MutuallyRecursive1 struct {
- I int
- R []MutuallyRecursive0
-}
-
-type Doubler struct {
- S string
- I int64
- B bool
-}
-
-type Repeat struct {
- Key string
- Value []byte
-}
-
-type Repeated struct {
- Repeats []Repeat
-}
-
-func (d *Doubler) Load(props []Property) error {
- return LoadStruct(d, props)
-}
-
-type EmbeddedTime struct {
- time.Time
-}
-
-type SpecialTime struct {
- MyTime EmbeddedTime
-}
-
-func (d *Doubler) Save() ([]Property, error) {
- // Save the default Property slice to an in-memory buffer (a PropertyList).
- props, err := SaveStruct(d)
- if err != nil {
- return nil, err
- }
- var list PropertyList
- if err := list.Load(props); err != nil {
- return nil, err
- }
-
- // Edit that PropertyList, and send it on.
- for i := range list {
- switch v := list[i].Value.(type) {
- case string:
- // + means string concatenation.
- list[i].Value = v + v
- case int64:
- // + means integer addition.
- list[i].Value = v + v
- }
- }
- return list.Save()
-}
-
-var _ PropertyLoadSaver = (*Doubler)(nil)
-
-type Deriver struct {
- S, Derived, Ignored string
-}
-
-func (e *Deriver) Load(props []Property) error {
- for _, p := range props {
- if p.Name != "S" {
- continue
- }
- e.S = p.Value.(string)
- e.Derived = "derived+" + e.S
- }
- return nil
-}
-
-func (e *Deriver) Save() ([]Property, error) {
- return []Property{
- {
- Name: "S",
- Value: e.S,
- },
- }, nil
-}
-
-var _ PropertyLoadSaver = (*Deriver)(nil)
-
-type BadMultiPropEntity struct{}
-
-func (e *BadMultiPropEntity) Load(props []Property) error {
- return errors.New("unimplemented")
-}
-
-func (e *BadMultiPropEntity) Save() ([]Property, error) {
- // Write multiple properties with the same name "I", but Multiple is false.
- var props []Property
- for i := 0; i < 3; i++ {
- props = append(props, Property{
- Name: "I",
- Value: int64(i),
- })
- }
- return props, nil
-}
-
-var _ PropertyLoadSaver = (*BadMultiPropEntity)(nil)
-
-type BK struct {
- Key appengine.BlobKey
-}
-
-type testCase struct {
- desc string
- src interface{}
- want interface{}
- putErr string
- getErr string
-}
-
-var testCases = []testCase{
- {
- "chan save fails",
- &C0{I: -1},
- &E{},
- "unsupported struct field",
- "",
- },
- {
- "*chan save fails",
- &C1{I: -1},
- &E{},
- "unsupported struct field",
- "",
- },
- {
- "[]chan save fails",
- &C2{I: -1, C: make([]chan int, 8)},
- &E{},
- "unsupported struct field",
- "",
- },
- {
- "chan load fails",
- &C3{C: "not a chan"},
- &C0{},
- "",
- "type mismatch",
- },
- {
- "*chan load fails",
- &C3{C: "not a *chan"},
- &C1{},
- "",
- "type mismatch",
- },
- {
- "[]chan load fails",
- &C3{C: "not a []chan"},
- &C2{},
- "",
- "type mismatch",
- },
- {
- "empty struct",
- &E{},
- &E{},
- "",
- "",
- },
- {
- "geopoint",
- &G0{G: testGeoPt0},
- &G0{G: testGeoPt0},
- "",
- "",
- },
- {
- "geopoint invalid",
- &G0{G: testBadGeoPt},
- &G0{},
- "invalid GeoPoint value",
- "",
- },
- {
- "geopoint as props",
- &G0{G: testGeoPt0},
- &PropertyList{
- Property{Name: "G", Value: testGeoPt0, NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "geopoint slice",
- &G1{G: []appengine.GeoPoint{testGeoPt0, testGeoPt1}},
- &G1{G: []appengine.GeoPoint{testGeoPt0, testGeoPt1}},
- "",
- "",
- },
- {
- "omit empty, all",
- &OmitAll{},
- new(PropertyList),
- "",
- "",
- },
- {
- "omit empty",
- &Omit{},
- &PropertyList{
- Property{Name: "St", Value: "", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "omit empty, fields populated",
- &Omit{
- A: "a",
- B: 10,
- C: true,
- D: now,
- F: []int{11},
- },
- &PropertyList{
- Property{Name: "A", Value: "a", NoIndex: false, Multiple: false},
- Property{Name: "Bb", Value: int64(10), NoIndex: false, Multiple: false},
- Property{Name: "C", Value: true, NoIndex: true, Multiple: false},
- Property{Name: "D", Value: now, NoIndex: false, Multiple: false},
- Property{Name: "F", Value: int64(11), NoIndex: false, Multiple: true},
- Property{Name: "St", Value: "", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "omit empty, fields populated",
- &Omit{
- A: "a",
- B: 10,
- C: true,
- D: now,
- F: []int{11},
- S: S{St: "string"},
- },
- &PropertyList{
- Property{Name: "A", Value: "a", NoIndex: false, Multiple: false},
- Property{Name: "Bb", Value: int64(10), NoIndex: false, Multiple: false},
- Property{Name: "C", Value: true, NoIndex: true, Multiple: false},
- Property{Name: "D", Value: now, NoIndex: false, Multiple: false},
- Property{Name: "F", Value: int64(11), NoIndex: false, Multiple: true},
- Property{Name: "St", Value: "string", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "omit empty does not propagate",
- &NoOmits{
- No: []NoOmit{
- NoOmit{},
- },
- S: S{},
- Ss: S{},
- },
- &PropertyList{
- Property{Name: "No.A", Value: "", NoIndex: false, Multiple: true},
- Property{Name: "No.Bb", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "No.C", Value: false, NoIndex: true, Multiple: true},
- Property{Name: "Ss.St", Value: "", NoIndex: false, Multiple: false},
- Property{Name: "St", Value: "", NoIndex: false, Multiple: false}},
- "",
- "",
- },
- {
- "key",
- &K0{K: testKey1a},
- &K0{K: testKey1b},
- "",
- "",
- },
- {
- "key with parent",
- &K0{K: testKey2a},
- &K0{K: testKey2b},
- "",
- "",
- },
- {
- "nil key",
- &K0{},
- &K0{},
- "",
- "",
- },
- {
- "all nil keys in slice",
- &K1{[]*Key{nil, nil}},
- &K1{[]*Key{nil, nil}},
- "",
- "",
- },
- {
- "some nil keys in slice",
- &K1{[]*Key{testKey1a, nil, testKey2a}},
- &K1{[]*Key{testKey1b, nil, testKey2b}},
- "",
- "",
- },
- {
- "overflow",
- &O0{I: 1 << 48},
- &O1{},
- "",
- "overflow",
- },
- {
- "time",
- &T{T: time.Unix(1e9, 0)},
- &T{T: time.Unix(1e9, 0)},
- "",
- "",
- },
- {
- "time as props",
- &T{T: time.Unix(1e9, 0)},
- &PropertyList{
- Property{Name: "T", Value: time.Unix(1e9, 0).UTC(), NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "uint save",
- &U0{U: 1},
- &U0{},
- "unsupported struct field",
- "",
- },
- {
- "uint load",
- &U1{U: "not a uint"},
- &U0{},
- "",
- "type mismatch",
- },
- {
- "zero",
- &X0{},
- &X0{},
- "",
- "",
- },
- {
- "basic",
- &X0{S: "one", I: 2, i: 3},
- &X0{S: "one", I: 2},
- "",
- "",
- },
- {
- "save string/int load myString/int32",
- &X0{S: "one", I: 2, i: 3},
- &X1{S: "one", I: 2},
- "",
- "",
- },
- {
- "missing fields",
- &X0{S: "one", I: 2, i: 3},
- &X2{},
- "",
- "no such struct field",
- },
- {
- "save string load bool",
- &X0{S: "one", I: 2, i: 3},
- &X3{I: 2},
- "",
- "type mismatch",
- },
- {
- "basic slice",
- &Y0{B: true, F: []float64{7, 8, 9}},
- &Y0{B: true, F: []float64{7, 8, 9}},
- "",
- "",
- },
- {
- "save []float64 load float64",
- &Y0{B: true, F: []float64{7, 8, 9}},
- &Y1{B: true},
- "",
- "requires a slice",
- },
- {
- "save []float64 load []int64",
- &Y0{B: true, F: []float64{7, 8, 9}},
- &Y2{B: true},
- "",
- "type mismatch",
- },
- {
- "single slice is too long",
- &Y0{F: make([]float64, maxIndexedProperties+1)},
- &Y0{},
- "too many indexed properties",
- "",
- },
- {
- "two slices are too long",
- &Y0{F: make([]float64, maxIndexedProperties), G: make([]float64, maxIndexedProperties)},
- &Y0{},
- "too many indexed properties",
- "",
- },
- {
- "one slice and one scalar are too long",
- &Y0{F: make([]float64, maxIndexedProperties), B: true},
- &Y0{},
- "too many indexed properties",
- "",
- },
- {
- "slice of slices of bytes",
- &Repeated{
- Repeats: []Repeat{
- {
- Key: "key 1",
- Value: []byte("value 1"),
- },
- {
- Key: "key 2",
- Value: []byte("value 2"),
- },
- },
- },
- &Repeated{
- Repeats: []Repeat{
- {
- Key: "key 1",
- Value: []byte("value 1"),
- },
- {
- Key: "key 2",
- Value: []byte("value 2"),
- },
- },
- },
- "",
- "",
- },
- {
- "long blob",
- &B0{B: makeUint8Slice(maxIndexedProperties + 1)},
- &B0{B: makeUint8Slice(maxIndexedProperties + 1)},
- "",
- "",
- },
- {
- "long []int8 is too long",
- &B1{B: makeInt8Slice(maxIndexedProperties + 1)},
- &B1{},
- "too many indexed properties",
- "",
- },
- {
- "short []int8",
- &B1{B: makeInt8Slice(3)},
- &B1{B: makeInt8Slice(3)},
- "",
- "",
- },
- {
- "long myBlob",
- &B2{B: makeUint8Slice(maxIndexedProperties + 1)},
- &B2{B: makeUint8Slice(maxIndexedProperties + 1)},
- "",
- "",
- },
- {
- "short myBlob",
- &B2{B: makeUint8Slice(3)},
- &B2{B: makeUint8Slice(3)},
- "",
- "",
- },
- {
- "long []myByte",
- &B3{B: makeMyByteSlice(maxIndexedProperties + 1)},
- &B3{B: makeMyByteSlice(maxIndexedProperties + 1)},
- "",
- "",
- },
- {
- "short []myByte",
- &B3{B: makeMyByteSlice(3)},
- &B3{B: makeMyByteSlice(3)},
- "",
- "",
- },
- {
- "slice of blobs",
- &B4{B: [][]byte{
- makeUint8Slice(3),
- makeUint8Slice(4),
- makeUint8Slice(5),
- }},
- &B4{B: [][]byte{
- makeUint8Slice(3),
- makeUint8Slice(4),
- makeUint8Slice(5),
- }},
- "",
- "",
- },
- {
- "short ByteString",
- &B5{B: ByteString(makeUint8Slice(3))},
- &B5{B: ByteString(makeUint8Slice(3))},
- "",
- "",
- },
- {
- "short ByteString as props",
- &B5{B: ByteString(makeUint8Slice(3))},
- &PropertyList{
- Property{Name: "B", Value: ByteString(makeUint8Slice(3)), NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "short ByteString into string",
- &B5{B: ByteString("legacy")},
- &struct{ B string }{"legacy"},
- "",
- "",
- },
- {
- "[]byte must be noindex",
- &PropertyList{
- Property{Name: "B", Value: makeUint8Slice(3), NoIndex: false},
- },
- nil,
- "cannot index a []byte valued Property",
- "",
- },
- {
- "save tagged load props",
- &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, F: 6, G: 7, I: 8, J: 9},
- &PropertyList{
- // 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.
- Property{Name: "C", Value: int64(3), NoIndex: true, Multiple: false},
- Property{Name: "D", Value: int64(4), NoIndex: false, Multiple: false},
- Property{Name: "E", Value: int64(5), NoIndex: false, Multiple: false},
- Property{Name: "F", Value: int64(6), NoIndex: true, Multiple: false},
- Property{Name: "G", Value: int64(7), NoIndex: false, Multiple: false},
- Property{Name: "J", Value: int64(9), NoIndex: true, Multiple: false},
- Property{Name: "a", Value: int64(1), NoIndex: true, Multiple: false},
- Property{Name: "b", Value: int64(21), NoIndex: false, Multiple: true},
- Property{Name: "b", Value: int64(22), NoIndex: false, Multiple: true},
- Property{Name: "b", Value: int64(23), NoIndex: false, Multiple: true},
- },
- "",
- "",
- },
- {
- "save tagged load tagged",
- &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6, J: 7},
- &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, J: 7},
- "",
- "",
- },
- {
- "save props load tagged",
- &PropertyList{
- Property{Name: "A", Value: int64(11), NoIndex: true, Multiple: false},
- Property{Name: "a", Value: int64(12), NoIndex: true, Multiple: false},
- },
- &Tagged{A: 12},
- "",
- `cannot load field "A"`,
- },
- {
- "invalid tagged1",
- &InvalidTagged1{I: 1},
- &InvalidTagged1{},
- "struct tag has invalid property name",
- "",
- },
- {
- "invalid tagged2",
- &InvalidTagged2{I: 1, J: 2},
- &InvalidTagged2{},
- "struct tag has repeated property name",
- "",
- },
- {
- "doubler",
- &Doubler{S: "s", I: 1, B: true},
- &Doubler{S: "ss", I: 2, B: true},
- "",
- "",
- },
- {
- "save struct load props",
- &X0{S: "s", I: 1},
- &PropertyList{
- Property{Name: "I", Value: int64(1), NoIndex: false, Multiple: false},
- Property{Name: "S", Value: "s", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "save props load struct",
- &PropertyList{
- Property{Name: "S", Value: "s", NoIndex: false, Multiple: false},
- Property{Name: "I", Value: int64(1), NoIndex: false, Multiple: false},
- },
- &X0{S: "s", I: 1},
- "",
- "",
- },
- {
- "nil-value props",
- &PropertyList{
- Property{Name: "I", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "B", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "S", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "F", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "K", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "T", Value: nil, NoIndex: false, Multiple: false},
- Property{Name: "J", Value: nil, NoIndex: false, Multiple: true},
- Property{Name: "J", Value: int64(7), NoIndex: false, Multiple: true},
- Property{Name: "J", Value: nil, NoIndex: false, Multiple: true},
- },
- &struct {
- I int64
- B bool
- S string
- F float64
- K *Key
- T time.Time
- J []int64
- }{
- J: []int64{0, 7, 0},
- },
- "",
- "",
- },
- {
- "save outer load props",
- &Outer{
- A: 1,
- I: []Inner1{
- {10, "ten"},
- {20, "twenty"},
- {30, "thirty"},
- },
- J: Inner2{
- Y: 3.14,
- },
- Inner3: Inner3{
- Z: true,
- },
- },
- &PropertyList{
- Property{Name: "A", Value: int64(1), NoIndex: false, Multiple: false},
- Property{Name: "I.W", Value: int64(10), NoIndex: false, Multiple: true},
- Property{Name: "I.W", Value: int64(20), NoIndex: false, Multiple: true},
- Property{Name: "I.W", Value: int64(30), NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "ten", NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "twenty", NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "thirty", NoIndex: false, Multiple: true},
- Property{Name: "J.Y", Value: float64(3.14), NoIndex: false, Multiple: false},
- Property{Name: "Z", Value: true, NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "save props load outer-equivalent",
- &PropertyList{
- Property{Name: "A", Value: int64(1), NoIndex: false, Multiple: false},
- Property{Name: "I.W", Value: int64(10), NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "ten", NoIndex: false, Multiple: true},
- Property{Name: "I.W", Value: int64(20), NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "twenty", NoIndex: false, Multiple: true},
- Property{Name: "I.W", Value: int64(30), NoIndex: false, Multiple: true},
- Property{Name: "I.X", Value: "thirty", NoIndex: false, Multiple: true},
- Property{Name: "J.Y", Value: float64(3.14), NoIndex: false, Multiple: false},
- Property{Name: "Z", Value: true, NoIndex: false, Multiple: false},
- },
- &OuterEquivalent{
- A: 1,
- IDotW: []int32{10, 20, 30},
- IDotX: []string{"ten", "twenty", "thirty"},
- JDotY: 3.14,
- Z: true,
- },
- "",
- "",
- },
- {
- "save outer-equivalent load outer",
- &OuterEquivalent{
- A: 1,
- IDotW: []int32{10, 20, 30},
- IDotX: []string{"ten", "twenty", "thirty"},
- JDotY: 3.14,
- Z: true,
- },
- &Outer{
- A: 1,
- I: []Inner1{
- {10, "ten"},
- {20, "twenty"},
- {30, "thirty"},
- },
- J: Inner2{
- Y: 3.14,
- },
- Inner3: Inner3{
- Z: true,
- },
- },
- "",
- "",
- },
- {
- "dotted names save",
- &Dotted{A: DottedA{B: DottedB{C: 88}}},
- &PropertyList{
- Property{Name: "A0.A1.A2.B3.C4.C5", Value: int64(88), NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "dotted names load",
- &PropertyList{
- Property{Name: "A0.A1.A2.B3.C4.C5", Value: int64(99), NoIndex: false, Multiple: false},
- },
- &Dotted{A: DottedA{B: DottedB{C: 99}}},
- "",
- "",
- },
- {
- "save struct load deriver",
- &X0{S: "s", I: 1},
- &Deriver{S: "s", Derived: "derived+s"},
- "",
- "",
- },
- {
- "save deriver load struct",
- &Deriver{S: "s", Derived: "derived+s", Ignored: "ignored"},
- &X0{S: "s"},
- "",
- "",
- },
- {
- "bad multi-prop entity",
- &BadMultiPropEntity{},
- &BadMultiPropEntity{},
- "Multiple is false",
- "",
- },
- // Regression: CL 25062824 broke handling of appengine.BlobKey fields.
- {
- "appengine.BlobKey",
- &BK{Key: "blah"},
- &BK{Key: "blah"},
- "",
- "",
- },
- {
- "zero time.Time",
- &T{T: time.Time{}},
- &T{T: time.Time{}},
- "",
- "",
- },
- {
- "time.Time near Unix zero time",
- &T{T: time.Unix(0, 4e3)},
- &T{T: time.Unix(0, 4e3)},
- "",
- "",
- },
- {
- "time.Time, far in the future",
- &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)},
- &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)},
- "",
- "",
- },
- {
- "time.Time, very far in the past",
- &T{T: time.Date(-300000, 1, 1, 0, 0, 0, 0, time.UTC)},
- &T{},
- "time value out of range",
- "",
- },
- {
- "time.Time, very far in the future",
- &T{T: time.Date(294248, 1, 1, 0, 0, 0, 0, time.UTC)},
- &T{},
- "time value out of range",
- "",
- },
- {
- "structs",
- &N0{
- X0: X0{S: "one", I: 2, i: 3},
- Nonymous: X0{S: "four", I: 5, i: 6},
- Ignore: "ignore",
- Other: "other",
- },
- &N0{
- X0: X0{S: "one", I: 2},
- Nonymous: X0{S: "four", I: 5},
- Other: "other",
- },
- "",
- "",
- },
- {
- "slice of structs",
- &N1{
- X0: X0{S: "one", I: 2, i: 3},
- Nonymous: []X0{
- {S: "four", I: 5, i: 6},
- {S: "seven", I: 8, i: 9},
- {S: "ten", I: 11, i: 12},
- {S: "thirteen", I: 14, i: 15},
- },
- Ignore: "ignore",
- Other: "other",
- },
- &N1{
- X0: X0{S: "one", I: 2},
- Nonymous: []X0{
- {S: "four", I: 5},
- {S: "seven", I: 8},
- {S: "ten", I: 11},
- {S: "thirteen", I: 14},
- },
- Other: "other",
- },
- "",
- "",
- },
- {
- "structs with slices of structs",
- &N2{
- N1: N1{
- X0: X0{S: "rouge"},
- Nonymous: []X0{
- {S: "rosso0"},
- {S: "rosso1"},
- },
- },
- Green: N1{
- X0: X0{S: "vert"},
- Nonymous: []X0{
- {S: "verde0"},
- {S: "verde1"},
- {S: "verde2"},
- },
- },
- Blue: N1{
- X0: X0{S: "bleu"},
- Nonymous: []X0{
- {S: "blu0"},
- {S: "blu1"},
- {S: "blu2"},
- {S: "blu3"},
- },
- },
- },
- &N2{
- N1: N1{
- X0: X0{S: "rouge"},
- Nonymous: []X0{
- {S: "rosso0"},
- {S: "rosso1"},
- },
- },
- Green: N1{
- X0: X0{S: "vert"},
- Nonymous: []X0{
- {S: "verde0"},
- {S: "verde1"},
- {S: "verde2"},
- },
- },
- Blue: N1{
- X0: X0{S: "bleu"},
- Nonymous: []X0{
- {S: "blu0"},
- {S: "blu1"},
- {S: "blu2"},
- {S: "blu3"},
- },
- },
- },
- "",
- "",
- },
- {
- "save structs load props",
- &N2{
- N1: N1{
- X0: X0{S: "rouge"},
- Nonymous: []X0{
- {S: "rosso0"},
- {S: "rosso1"},
- },
- },
- Green: N1{
- X0: X0{S: "vert"},
- Nonymous: []X0{
- {S: "verde0"},
- {S: "verde1"},
- {S: "verde2"},
- },
- },
- Blue: N1{
- X0: X0{S: "bleu"},
- Nonymous: []X0{
- {S: "blu0"},
- {S: "blu1"},
- {S: "blu2"},
- {S: "blu3"},
- },
- },
- },
- &PropertyList{
- Property{Name: "Blue.I", Value: int64(0), NoIndex: false, Multiple: false},
- Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blu0", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blu1", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blu2", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blu3", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Other", Value: "", NoIndex: false, Multiple: false},
- Property{Name: "Blue.S", Value: "bleu", NoIndex: false, Multiple: false},
- Property{Name: "green.I", Value: int64(0), NoIndex: false, Multiple: false},
- Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.S", Value: "verde0", NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.S", Value: "verde1", NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.S", Value: "verde2", NoIndex: false, Multiple: true},
- Property{Name: "green.Other", Value: "", NoIndex: false, Multiple: false},
- Property{Name: "green.S", Value: "vert", NoIndex: false, Multiple: false},
- Property{Name: "red.I", Value: int64(0), NoIndex: false, Multiple: false},
- Property{Name: "red.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "red.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true},
- Property{Name: "red.Nonymous.S", Value: "rosso0", NoIndex: false, Multiple: true},
- Property{Name: "red.Nonymous.S", Value: "rosso1", NoIndex: false, Multiple: true},
- Property{Name: "red.Other", Value: "", NoIndex: false, Multiple: false},
- Property{Name: "red.S", Value: "rouge", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "save props load structs with ragged fields",
- &PropertyList{
- Property{Name: "red.S", Value: "rot", NoIndex: false, Multiple: false},
- Property{Name: "green.Nonymous.I", Value: int64(10), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.I", Value: int64(11), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.I", Value: int64(12), NoIndex: false, Multiple: true},
- Property{Name: "green.Nonymous.I", Value: int64(13), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blau0", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.I", Value: int64(20), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blau1", NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.I", Value: int64(21), NoIndex: false, Multiple: true},
- Property{Name: "Blue.Nonymous.S", Value: "blau2", NoIndex: false, Multiple: true},
- },
- &N2{
- N1: N1{
- X0: X0{S: "rot"},
- },
- Green: N1{
- Nonymous: []X0{
- {I: 10},
- {I: 11},
- {I: 12},
- {I: 13},
- },
- },
- Blue: N1{
- Nonymous: []X0{
- {S: "blau0", I: 20},
- {S: "blau1", I: 21},
- {S: "blau2"},
- },
- },
- },
- "",
- "",
- },
- {
- "save structs with noindex tags",
- &struct {
- A struct {
- X string `datastore:",noindex"`
- Y string
- } `datastore:",noindex"`
- B struct {
- X string `datastore:",noindex"`
- Y string
- }
- }{},
- &PropertyList{
- Property{Name: "A.X", Value: "", NoIndex: true, Multiple: false},
- Property{Name: "A.Y", Value: "", NoIndex: true, Multiple: false},
- Property{Name: "B.X", Value: "", NoIndex: true, Multiple: false},
- Property{Name: "B.Y", Value: "", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "embedded struct with name override",
- &struct {
- Inner1 `datastore:"foo"`
- }{},
- &PropertyList{
- Property{Name: "foo.W", Value: int64(0), NoIndex: false, Multiple: false},
- Property{Name: "foo.X", Value: "", NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "slice of slices",
- &SliceOfSlices{},
- nil,
- "flattening nested structs leads to a slice of slices",
- "",
- },
- {
- "recursive struct",
- &Recursive{},
- nil,
- "recursive struct",
- "",
- },
- {
- "mutually recursive struct",
- &MutuallyRecursive0{},
- nil,
- "recursive struct",
- "",
- },
- {
- "non-exported struct fields",
- &struct {
- i, J int64
- }{i: 1, J: 2},
- &PropertyList{
- Property{Name: "J", Value: int64(2), NoIndex: false, Multiple: false},
- },
- "",
- "",
- },
- {
- "json.RawMessage",
- &struct {
- J json.RawMessage
- }{
- J: json.RawMessage("rawr"),
- },
- &PropertyList{
- Property{Name: "J", Value: []byte("rawr"), NoIndex: true, Multiple: false},
- },
- "",
- "",
- },
- {
- "json.RawMessage to myBlob",
- &struct {
- B json.RawMessage
- }{
- B: json.RawMessage("rawr"),
- },
- &B2{B: myBlob("rawr")},
- "",
- "",
- },
- {
- "embedded time field",
- &SpecialTime{MyTime: EmbeddedTime{now}},
- &SpecialTime{MyTime: EmbeddedTime{now}},
- "",
- "",
- },
- {
- "embedded time load",
- &PropertyList{
- Property{Name: "MyTime.", Value: now, NoIndex: false, Multiple: false},
- },
- &SpecialTime{MyTime: EmbeddedTime{now}},
- "",
- "",
- },
-}
-
-// checkErr returns the empty string if either both want and err are zero,
-// or if want is a non-empty substring of err's string representation.
-func checkErr(want string, err error) string {
- if err != nil {
- got := err.Error()
- if want == "" || strings.Index(got, want) == -1 {
- return got
- }
- } else if want != "" {
- return fmt.Sprintf("want error %q", want)
- }
- return ""
-}
-
-func TestRoundTrip(t *testing.T) {
- for _, tc := range testCases {
- p, err := saveEntity(testAppID, testKey0, tc.src)
- if s := checkErr(tc.putErr, err); s != "" {
- t.Errorf("%s: save: %s", tc.desc, s)
- continue
- }
- if p == nil {
- continue
- }
- var got interface{}
- if _, ok := tc.want.(*PropertyList); ok {
- got = new(PropertyList)
- } else {
- got = reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
- }
- err = loadEntity(got, p)
- if s := checkErr(tc.getErr, err); s != "" {
- t.Errorf("%s: load: %s", tc.desc, s)
- continue
- }
- if pl, ok := got.(*PropertyList); ok {
- // Sort by name to make sure we have a deterministic order.
- sort.Stable(byName(*pl))
- }
- equal := false
- if gotT, ok := got.(*T); ok {
- // Round tripping a time.Time can result in a different time.Location: Local instead of UTC.
- // We therefore test equality explicitly, instead of relying on reflect.DeepEqual.
- equal = gotT.T.Equal(tc.want.(*T).T)
- } else {
- equal = reflect.DeepEqual(got, tc.want)
- }
- if !equal {
- t.Errorf("%s: compare: got %v want %v", tc.desc, got, tc.want)
- continue
- }
- }
-}
-
-type byName PropertyList
-
-func (s byName) Len() int { return len(s) }
-func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
-func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func TestQueryConstruction(t *testing.T) {
- tests := []struct {
- q, exp *Query
- err string
- }{
- {
- q: NewQuery("Foo"),
- exp: &Query{
- kind: "Foo",
- limit: -1,
- },
- },
- {
- // Regular filtered query with standard spacing.
- q: NewQuery("Foo").Filter("foo >", 7),
- exp: &Query{
- kind: "Foo",
- filter: []filter{
- {
- FieldName: "foo",
- Op: greaterThan,
- Value: 7,
- },
- },
- limit: -1,
- },
- },
- {
- // Filtered query with no spacing.
- q: NewQuery("Foo").Filter("foo=", 6),
- exp: &Query{
- kind: "Foo",
- filter: []filter{
- {
- FieldName: "foo",
- Op: equal,
- Value: 6,
- },
- },
- limit: -1,
- },
- },
- {
- // Filtered query with funky spacing.
- q: NewQuery("Foo").Filter(" foo< ", 8),
- exp: &Query{
- kind: "Foo",
- filter: []filter{
- {
- FieldName: "foo",
- Op: lessThan,
- Value: 8,
- },
- },
- limit: -1,
- },
- },
- {
- // Filtered query with multicharacter op.
- q: NewQuery("Foo").Filter("foo >=", 9),
- exp: &Query{
- kind: "Foo",
- filter: []filter{
- {
- FieldName: "foo",
- Op: greaterEq,
- Value: 9,
- },
- },
- limit: -1,
- },
- },
- {
- // Query with ordering.
- q: NewQuery("Foo").Order("bar"),
- exp: &Query{
- kind: "Foo",
- order: []order{
- {
- FieldName: "bar",
- Direction: ascending,
- },
- },
- limit: -1,
- },
- },
- {
- // Query with reverse ordering, and funky spacing.
- q: NewQuery("Foo").Order(" - bar"),
- exp: &Query{
- kind: "Foo",
- order: []order{
- {
- FieldName: "bar",
- Direction: descending,
- },
- },
- limit: -1,
- },
- },
- {
- // Query with an empty ordering.
- q: NewQuery("Foo").Order(""),
- err: "empty order",
- },
- {
- // Query with a + ordering.
- q: NewQuery("Foo").Order("+bar"),
- err: "invalid order",
- },
- }
- for i, test := range tests {
- if test.q.err != nil {
- got := test.q.err.Error()
- if !strings.Contains(got, test.err) {
- t.Errorf("%d: error mismatch: got %q want something containing %q", i, got, test.err)
- }
- continue
- }
- if !reflect.DeepEqual(test.q, test.exp) {
- t.Errorf("%d: mismatch: got %v want %v", i, test.q, test.exp)
- }
- }
-}
-
-func TestStringMeaning(t *testing.T) {
- var xx [4]interface{}
- xx[0] = &struct {
- X string
- }{"xx0"}
- xx[1] = &struct {
- X string `datastore:",noindex"`
- }{"xx1"}
- xx[2] = &struct {
- X []byte
- }{[]byte("xx2")}
- xx[3] = &struct {
- X []byte `datastore:",noindex"`
- }{[]byte("xx3")}
-
- indexed := [4]bool{
- true,
- false,
- false, // A []byte is always no-index.
- false,
- }
- want := [4]pb.Property_Meaning{
- pb.Property_NO_MEANING,
- pb.Property_TEXT,
- pb.Property_BLOB,
- pb.Property_BLOB,
- }
-
- for i, x := range xx {
- props, err := SaveStruct(x)
- if err != nil {
- t.Errorf("i=%d: SaveStruct: %v", i, err)
- continue
- }
- e, err := propertiesToProto("appID", testKey0, props)
- if err != nil {
- t.Errorf("i=%d: propertiesToProto: %v", i, err)
- continue
- }
- var p *pb.Property
- switch {
- case indexed[i] && len(e.Property) == 1:
- p = e.Property[0]
- case !indexed[i] && len(e.RawProperty) == 1:
- p = e.RawProperty[0]
- default:
- t.Errorf("i=%d: EntityProto did not have expected property slice", i)
- continue
- }
- if got := p.GetMeaning(); got != want[i] {
- t.Errorf("i=%d: meaning: got %v, want %v", i, got, want[i])
- continue
- }
- }
-}
-
-func TestNamespaceResetting(t *testing.T) {
- // These environment variables are necessary because *Query.Run will
- // call internal.FullyQualifiedAppID which checks these variables or falls
- // back to the Metadata service that is not available in tests.
- environ := []struct {
- key, value string
- }{
- {"GAE_LONG_APP_ID", "my-app-id"},
- {"GAE_PARTITION", "1"},
- }
- for _, v := range environ {
- old := os.Getenv(v.key)
- os.Setenv(v.key, v.value)
- v.value = old
- }
- defer func() { // Restore old environment after the test completes.
- for _, v := range environ {
- if v.value == "" {
- os.Unsetenv(v.key)
- continue
- }
- os.Setenv(v.key, v.value)
- }
- }()
-
- namec := make(chan *string, 1)
- c0 := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(req *pb.Query, res *pb.QueryResult) error {
- namec <- req.NameSpace
- return fmt.Errorf("RPC error")
- })
-
- // Check that wrapping c0 in a namespace twice works correctly.
- c1, err := appengine.Namespace(c0, "A")
- if err != nil {
- t.Fatalf("appengine.Namespace: %v", err)
- }
- c2, err := appengine.Namespace(c1, "") // should act as the original context
- if err != nil {
- t.Fatalf("appengine.Namespace: %v", err)
- }
-
- q := NewQuery("SomeKind")
-
- q.Run(c0)
- if ns := <-namec; ns != nil {
- t.Errorf(`RunQuery with c0: ns = %q, want nil`, *ns)
- }
-
- q.Run(c1)
- if ns := <-namec; ns == nil {
- t.Error(`RunQuery with c1: ns = nil, want "A"`)
- } else if *ns != "A" {
- t.Errorf(`RunQuery with c1: ns = %q, want "A"`, *ns)
- }
-
- q.Run(c2)
- if ns := <-namec; ns != nil {
- t.Errorf(`RunQuery with c2: ns = %q, want nil`, *ns)
- }
-}