diff options
Diffstat (limited to 'vendor/google.golang.org/appengine/datastore/datastore_test.go')
-rw-r--r-- | vendor/google.golang.org/appengine/datastore/datastore_test.go | 1750 |
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) - } -} |