|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // 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 (
- "reflect"
- "sort"
- "strings"
- "testing"
- "time"
-
- tspb "github.com/golang/protobuf/ptypes/timestamp"
- pb "google.golang.org/genproto/googleapis/firestore/v1"
- )
-
- func TestToProtoDocument(t *testing.T) {
- type s struct{ I int }
-
- for _, test := range []struct {
- in interface{}
- want *pb.Document
- wantErr bool
- }{
- {nil, nil, true},
- {[]int{1}, nil, true},
- {map[string]int{"a": 1},
- &pb.Document{Fields: map[string]*pb.Value{"a": intval(1)}},
- false},
- {s{2}, &pb.Document{Fields: map[string]*pb.Value{"I": intval(2)}}, false},
- {&s{3}, &pb.Document{Fields: map[string]*pb.Value{"I": intval(3)}}, false},
- } {
- got, _, gotErr := toProtoDocument(test.in)
- if (gotErr != nil) != test.wantErr {
- t.Errorf("%v: got error %v, want %t", test.in, gotErr, test.wantErr)
- }
- if gotErr != nil {
- continue
- }
- if !testEqual(got, test.want) {
- t.Errorf("%v: got %v, want %v", test.in, got, test.want)
- }
- }
- }
-
- func TestNewDocumentSnapshot(t *testing.T) {
- c := &Client{
- projectID: "projID",
- databaseID: "(database)",
- }
- docRef := c.Doc("C/a")
- in := &pb.Document{
- CreateTime: &tspb.Timestamp{Seconds: 10},
- UpdateTime: &tspb.Timestamp{Seconds: 20},
- Fields: map[string]*pb.Value{"a": intval(1)},
- }
- want := &DocumentSnapshot{
- Ref: docRef,
- CreateTime: time.Unix(10, 0).UTC(),
- UpdateTime: time.Unix(20, 0).UTC(),
- ReadTime: aTime,
- proto: in,
- c: c,
- }
- got, err := newDocumentSnapshot(docRef, in, c, aTimestamp)
- if err != nil {
- t.Fatal(err)
- }
- if !testEqual(got, want) {
- t.Errorf("got %+v\nwant %+v", got, want)
- }
- }
-
- func TestData(t *testing.T) {
- doc := &DocumentSnapshot{
- proto: &pb.Document{
- Fields: map[string]*pb.Value{"a": intval(1), "b": strval("x")},
- },
- }
- got := doc.Data()
- want := map[string]interface{}{"a": int64(1), "b": "x"}
- if !testEqual(got, want) {
- t.Errorf("got %#v\nwant %#v", got, want)
- }
- var got2 map[string]interface{}
- if err := doc.DataTo(&got2); err != nil {
- t.Fatal(err)
- }
- if !testEqual(got2, want) {
- t.Errorf("got %#v\nwant %#v", got2, want)
- }
-
- type s struct {
- A int
- B string
- }
- var got3 s
- if err := doc.DataTo(&got3); err != nil {
- t.Fatal(err)
- }
- want2 := s{A: 1, B: "x"}
- if !testEqual(got3, want2) {
- t.Errorf("got %#v\nwant %#v", got3, want2)
- }
- }
-
- var testDoc = &DocumentSnapshot{
- proto: &pb.Document{
- Fields: map[string]*pb.Value{
- "a": intval(1),
- "b": mapval(map[string]*pb.Value{
- "`": intval(2),
- "~": mapval(map[string]*pb.Value{
- "x": intval(3),
- }),
- }),
- },
- },
- }
-
- func TestDataAt(t *testing.T) {
- for _, test := range []struct {
- fieldPath string
- want interface{}
- }{
- {"a", int64(1)},
- {"b.`", int64(2)},
- } {
- got, err := testDoc.DataAt(test.fieldPath)
- if err != nil {
- t.Errorf("%q: %v", test.fieldPath, err)
- continue
- }
- if !testEqual(got, test.want) {
- t.Errorf("%q: got %v, want %v", test.fieldPath, got, test.want)
- }
- }
-
- for _, bad := range []string{
- "c.~.x", // bad field path
- "a.b", // "a" isn't a map
- "z.b", // bad non-final key
- "b.z", // bad final key
- } {
- _, err := testDoc.DataAt(bad)
- if err == nil {
- t.Errorf("%q: got nil, want error", bad)
- }
- }
- }
-
- func TestDataAtPath(t *testing.T) {
- for _, test := range []struct {
- fieldPath FieldPath
- want interface{}
- }{
- {[]string{"a"}, int64(1)},
- {[]string{"b", "`"}, int64(2)},
- {[]string{"b", "~"}, map[string]interface{}{"x": int64(3)}},
- {[]string{"b", "~", "x"}, int64(3)},
- } {
- got, err := testDoc.DataAtPath(test.fieldPath)
- if err != nil {
- t.Errorf("%v: %v", test.fieldPath, err)
- continue
- }
- if !testEqual(got, test.want) {
- t.Errorf("%v: got %v, want %v", test.fieldPath, got, test.want)
- }
- }
-
- for _, bad := range []FieldPath{
- []string{"c", "", "x"}, // bad field path
- []string{"a", "b"}, // "a" isn't a map
- []string{"z", "~"}, // bad non-final key
- []string{"b", "z"}, // bad final key
- } {
- _, err := testDoc.DataAtPath(bad)
- if err == nil {
- t.Errorf("%v: got nil, want error", bad)
- }
- }
- }
-
- func TestExtractTransforms(t *testing.T) {
- type S struct {
- A time.Time `firestore:",serverTimestamp"`
- B time.Time `firestore:",serverTimestamp"`
- C *time.Time `firestore:",serverTimestamp"`
- D *time.Time `firestore:"d.d,serverTimestamp"`
- E *time.Time `firestore:",serverTimestamp"`
- F time.Time
- G int
- }
-
- m := map[string]interface{}{
- "ar": map[string]interface{}{"k2": ArrayRemove("e", "f", "g")},
- "au": map[string]interface{}{"k1": ArrayUnion("a", "b", "c")},
- "x": 1,
- "y": &S{
- // A is a zero time: included
- B: aTime, // not a zero time: excluded
- //C is nil: included
- D: &time.Time{}, // pointer to a zero time: included
- E: &aTime, // pointer to a non-zero time: excluded
- //F is a zero time, but does not have the right tag: excluded
- G: 15, // not a time.Time
- },
- "z": map[string]interface{}{"w": ServerTimestamp},
- }
- got, err := extractTransforms(reflect.ValueOf(m), nil)
- if err != nil {
- t.Fatal(err)
- }
- want := []*pb.DocumentTransform_FieldTransform{
- {
- FieldPath: "ar.k2",
- TransformType: &pb.DocumentTransform_FieldTransform_RemoveAllFromArray{
- RemoveAllFromArray: &pb.ArrayValue{Values: []*pb.Value{
- {ValueType: &pb.Value_StringValue{"e"}},
- {ValueType: &pb.Value_StringValue{"f"}},
- {ValueType: &pb.Value_StringValue{"g"}},
- }},
- },
- },
- {
- FieldPath: "au.k1",
- TransformType: &pb.DocumentTransform_FieldTransform_AppendMissingElements{
- AppendMissingElements: &pb.ArrayValue{Values: []*pb.Value{
- {ValueType: &pb.Value_StringValue{"a"}},
- {ValueType: &pb.Value_StringValue{"b"}},
- {ValueType: &pb.Value_StringValue{"c"}},
- }},
- },
- },
- {
- FieldPath: "y.A",
- TransformType: &pb.DocumentTransform_FieldTransform_SetToServerValue{
- SetToServerValue: pb.DocumentTransform_FieldTransform_REQUEST_TIME,
- },
- },
- {
- FieldPath: "y.C",
- TransformType: &pb.DocumentTransform_FieldTransform_SetToServerValue{
- SetToServerValue: pb.DocumentTransform_FieldTransform_REQUEST_TIME,
- },
- },
- {
- FieldPath: "y.`d.d`",
- TransformType: &pb.DocumentTransform_FieldTransform_SetToServerValue{
- SetToServerValue: pb.DocumentTransform_FieldTransform_REQUEST_TIME,
- },
- },
- {
- FieldPath: "z.w",
- TransformType: &pb.DocumentTransform_FieldTransform_SetToServerValue{
- SetToServerValue: pb.DocumentTransform_FieldTransform_REQUEST_TIME,
- },
- },
- }
- if len(got) != len(want) {
- t.Fatalf("Expected output array of size %d, got %d: %v", len(want), len(got), got)
- }
- sort.Sort(byDocumentTransformFieldPath(got))
- for i, vw := range want {
- vg := got[i]
- if !testEqual(vw, vg) {
- t.Fatalf("index %d: got %#v, want %#v", i, vg, vw)
- }
- }
- }
-
- type byDocumentTransformFieldPath []*pb.DocumentTransform_FieldTransform
-
- func (b byDocumentTransformFieldPath) Len() int { return len(b) }
- func (b byDocumentTransformFieldPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
- func (b byDocumentTransformFieldPath) Less(i, j int) bool {
- return strings.Compare(b[i].FieldPath, b[j].FieldPath) < 1
- }
-
- func TestExtractTransformPathsErrors(t *testing.T) {
- type S struct {
- A int `firestore:",serverTimestamp"`
- }
- _, err := extractTransforms(reflect.ValueOf(S{}), nil)
- if err == nil {
- t.Error("got nil, want error")
- }
- }
|