|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- // Copyright 2017 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package firestore
-
- import (
- "fmt"
- "reflect"
- "testing"
- "time"
-
- ts "github.com/golang/protobuf/ptypes/timestamp"
- pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
-
- "google.golang.org/genproto/googleapis/type/latlng"
- )
-
- type testStruct1 struct {
- B bool
- I int
- U uint32
- F float64
- S string
- Y []byte
- T time.Time
- Ts *ts.Timestamp
- G *latlng.LatLng
- L []int
- M map[string]int
- P *int
- }
-
- var (
- p = new(int)
-
- testVal1 = testStruct1{
- B: true,
- I: 1,
- U: 2,
- F: 3.0,
- S: "four",
- Y: []byte{5},
- T: tm,
- Ts: ptm,
- G: ll,
- L: []int{6},
- M: map[string]int{"a": 7},
- P: p,
- }
-
- mapVal1 = mapval(map[string]*pb.Value{
- "B": boolval(true),
- "I": intval(1),
- "U": intval(2),
- "F": floatval(3),
- "S": {ValueType: &pb.Value_StringValue{"four"}},
- "Y": bytesval([]byte{5}),
- "T": tsval(tm),
- "Ts": {ValueType: &pb.Value_TimestampValue{ptm}},
- "G": geoval(ll),
- "L": arrayval(intval(6)),
- "M": mapval(map[string]*pb.Value{"a": intval(7)}),
- "P": intval(8),
- })
- )
-
- func TestToProtoValue(t *testing.T) {
- *p = 8
- for _, test := range []struct {
- in interface{}
- want *pb.Value
- }{
- {nil, nullValue},
- {[]int(nil), nullValue},
- {map[string]int(nil), nullValue},
- {(*testStruct1)(nil), nullValue},
- {(*ts.Timestamp)(nil), nullValue},
- {(*latlng.LatLng)(nil), nullValue},
- {(*DocumentRef)(nil), nullValue},
- {true, boolval(true)},
- {3, intval(3)},
- {uint32(3), intval(3)},
- {1.5, floatval(1.5)},
- {"str", strval("str")},
- {[]byte{1, 2}, bytesval([]byte{1, 2})},
- {tm, tsval(tm)},
- {ptm, &pb.Value{ValueType: &pb.Value_TimestampValue{ptm}}},
- {ll, geoval(ll)},
- {[]int{1, 2}, arrayval(intval(1), intval(2))},
- {&[]int{1, 2}, arrayval(intval(1), intval(2))},
- {[]int{}, arrayval()},
- {map[string]int{"a": 1, "b": 2},
- mapval(map[string]*pb.Value{"a": intval(1), "b": intval(2)})},
- {map[string]int{}, mapval(map[string]*pb.Value{})},
- {p, intval(8)},
- {&p, intval(8)},
- {map[string]interface{}{"a": 1, "p": p, "s": "str"},
- mapval(map[string]*pb.Value{"a": intval(1), "p": intval(8), "s": strval("str")})},
- {map[string]fmt.Stringer{"a": tm},
- mapval(map[string]*pb.Value{"a": tsval(tm)})},
- {testVal1, mapVal1},
- {
- &DocumentRef{
- ID: "d",
- Path: "projects/P/databases/D/documents/c/d",
- Parent: &CollectionRef{
- ID: "c",
- parentPath: "projects/P/databases/D",
- Path: "projects/P/databases/D/documents/c",
- Query: Query{collectionID: "c", parentPath: "projects/P/databases/D"},
- },
- },
- refval("projects/P/databases/D/documents/c/d"),
- },
- // ServerTimestamps are removed, possibly leaving nil.
- {map[string]interface{}{"a": ServerTimestamp}, nil},
- {
- map[string]interface{}{
- "a": map[string]interface{}{
- "b": map[string]interface{}{
- "c": ServerTimestamp,
- },
- },
- },
- nil,
- },
- {
- map[string]interface{}{
- "a": map[string]interface{}{
- "b": map[string]interface{}{
- "c": ServerTimestamp,
- "d": ServerTimestamp,
- },
- },
- },
- nil,
- },
- {
- map[string]interface{}{
- "a": map[string]interface{}{
- "b": map[string]interface{}{
- "c": ServerTimestamp,
- "d": ServerTimestamp,
- "e": 1,
- },
- },
- },
- mapval(map[string]*pb.Value{
- "a": mapval(map[string]*pb.Value{
- "b": mapval(map[string]*pb.Value{"e": intval(1)}),
- }),
- }),
- },
- } {
- got, _, err := toProtoValue(reflect.ValueOf(test.in))
- if err != nil {
- t.Errorf("%v (%T): %v", test.in, test.in, err)
- continue
- }
- if !testEqual(got, test.want) {
- t.Errorf("%+v (%T):\ngot\n%+v\nwant\n%+v", test.in, test.in, got, test.want)
- }
- }
- }
-
- type stringy struct{}
-
- func (stringy) String() string { return "stringy" }
-
- func TestToProtoValueErrors(t *testing.T) {
- for _, in := range []interface{}{
- uint64(0), // a bad fit for int64
- map[int]bool{}, // map key type is not string
- make(chan int), // can't handle type
- map[string]fmt.Stringer{"a": stringy{}}, // only empty interfaces
- ServerTimestamp, // ServerTimestamp can only be a field value
- []interface{}{ServerTimestamp},
- map[string]interface{}{"a": []interface{}{ServerTimestamp}},
- map[string]interface{}{"a": []interface{}{
- map[string]interface{}{"b": ServerTimestamp},
- }},
- Delete, // Delete should never appear
- []interface{}{Delete},
- map[string]interface{}{"a": Delete},
- map[string]interface{}{"a": []interface{}{Delete}},
- } {
- _, _, err := toProtoValue(reflect.ValueOf(in))
- if err == nil {
- t.Errorf("%v: got nil, want error", in)
- }
- }
- }
-
- type testStruct2 struct {
- Ignore int `firestore:"-"`
- Rename int `firestore:"a"`
- OmitEmpty int `firestore:",omitempty"`
- OmitEmptyTime time.Time `firestore:",omitempty"`
- }
-
- func TestToProtoValueTags(t *testing.T) {
- in := &testStruct2{
- Ignore: 1,
- Rename: 2,
- OmitEmpty: 3,
- OmitEmptyTime: aTime,
- }
- got, _, err := toProtoValue(reflect.ValueOf(in))
- if err != nil {
- t.Fatal(err)
- }
- want := mapval(map[string]*pb.Value{
- "a": intval(2),
- "OmitEmpty": intval(3),
- "OmitEmptyTime": tsval(aTime),
- })
- if !testEqual(got, want) {
- t.Errorf("got %+v, want %+v", got, want)
- }
-
- got, _, err = toProtoValue(reflect.ValueOf(testStruct2{}))
- if err != nil {
- t.Fatal(err)
- }
- want = mapval(map[string]*pb.Value{"a": intval(0)})
- if !testEqual(got, want) {
- t.Errorf("got\n%+v\nwant\n%+v", got, want)
- }
- }
-
- func TestToProtoValueEmbedded(t *testing.T) {
- // Embedded time.Time, LatLng, or Timestamp should behave like non-embedded.
- type embed struct {
- time.Time
- *latlng.LatLng
- *ts.Timestamp
- }
-
- got, _, err := toProtoValue(reflect.ValueOf(embed{tm, ll, ptm}))
- if err != nil {
- t.Fatal(err)
- }
- want := mapval(map[string]*pb.Value{
- "Time": tsval(tm),
- "LatLng": geoval(ll),
- "Timestamp": {ValueType: &pb.Value_TimestampValue{ptm}},
- })
- if !testEqual(got, want) {
- t.Errorf("got %+v, want %+v", got, want)
- }
- }
-
- func TestIsEmpty(t *testing.T) {
- for _, e := range []interface{}{int(0), float32(0), false, "", []int{}, []int(nil), (*int)(nil)} {
- if !isEmptyValue(reflect.ValueOf(e)) {
- t.Errorf("%v (%T): want true, got false", e, e)
- }
- }
- i := 3
- for _, n := range []interface{}{int(1), float32(1), true, "x", []int{1}, &i} {
- if isEmptyValue(reflect.ValueOf(n)) {
- t.Errorf("%v (%T): want false, got true", n, n)
- }
- }
- }
|