|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- // 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 (
- "context"
- "reflect"
- "sort"
- "testing"
- "time"
-
- pb "google.golang.org/genproto/googleapis/firestore/v1"
- "google.golang.org/genproto/googleapis/type/latlng"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- )
-
- var (
- writeResultForSet = &WriteResult{UpdateTime: aTime}
- commitResponseForSet = &pb.CommitResponse{
- WriteResults: []*pb.WriteResult{{UpdateTime: aTimestamp}},
- }
- )
-
- func TestDocGet(t *testing.T) {
- ctx := context.Background()
- c, srv := newMock(t)
- path := "projects/projectID/databases/(default)/documents/C/a"
- pdoc := &pb.Document{
- Name: path,
- CreateTime: aTimestamp,
- UpdateTime: aTimestamp,
- Fields: map[string]*pb.Value{"f": intval(1)},
- }
- srv.addRPC(&pb.BatchGetDocumentsRequest{
- Database: c.path(),
- Documents: []string{path},
- }, []interface{}{
- &pb.BatchGetDocumentsResponse{
- Result: &pb.BatchGetDocumentsResponse_Found{pdoc},
- ReadTime: aTimestamp2,
- },
- })
- ref := c.Collection("C").Doc("a")
- gotDoc, err := ref.Get(ctx)
- if err != nil {
- t.Fatal(err)
- }
- wantDoc := &DocumentSnapshot{
- Ref: ref,
- CreateTime: aTime,
- UpdateTime: aTime,
- ReadTime: aTime2,
- proto: pdoc,
- c: c,
- }
- if !testEqual(gotDoc, wantDoc) {
- t.Fatalf("\ngot %+v\nwant %+v", gotDoc, wantDoc)
- }
-
- path2 := "projects/projectID/databases/(default)/documents/C/b"
- srv.addRPC(
- &pb.BatchGetDocumentsRequest{
- Database: c.path(),
- Documents: []string{path2},
- }, []interface{}{
- &pb.BatchGetDocumentsResponse{
- Result: &pb.BatchGetDocumentsResponse_Missing{path2},
- ReadTime: aTimestamp3,
- },
- })
- _, err = c.Collection("C").Doc("b").Get(ctx)
- if grpc.Code(err) != codes.NotFound {
- t.Errorf("got %v, want NotFound", err)
- }
- }
-
- func TestDocSet(t *testing.T) {
- // Most tests for Set are in the conformance tests.
- ctx := context.Background()
- c, srv := newMock(t)
-
- doc := c.Collection("C").Doc("d")
- // Merge with a struct and FieldPaths.
- srv.addRPC(&pb.CommitRequest{
- Database: "projects/projectID/databases/(default)",
- Writes: []*pb.Write{
- {
- Operation: &pb.Write_Update{
- Update: &pb.Document{
- Name: "projects/projectID/databases/(default)/documents/C/d",
- Fields: map[string]*pb.Value{
- "*": mapval(map[string]*pb.Value{
- "~": boolval(true),
- }),
- },
- },
- },
- UpdateMask: &pb.DocumentMask{FieldPaths: []string{"`*`.`~`"}},
- },
- },
- }, commitResponseForSet)
- data := struct {
- A map[string]bool `firestore:"*"`
- }{A: map[string]bool{"~": true}}
- wr, err := doc.Set(ctx, data, Merge([]string{"*", "~"}))
- if err != nil {
- t.Fatal(err)
- }
- if !testEqual(wr, writeResultForSet) {
- t.Errorf("got %v, want %v", wr, writeResultForSet)
- }
-
- // MergeAll cannot be used with structs.
- _, err = doc.Set(ctx, data, MergeAll)
- if err == nil {
- t.Errorf("got nil, want error")
- }
- }
-
- func TestDocCreate(t *testing.T) {
- // Verify creation with structs. In particular, make sure zero values
- // are handled well.
- // Other tests for Create are handled by the conformance tests.
- ctx := context.Background()
- c, srv := newMock(t)
-
- type create struct {
- Time time.Time
- Bytes []byte
- Geo *latlng.LatLng
- }
- srv.addRPC(
- &pb.CommitRequest{
- Database: "projects/projectID/databases/(default)",
- Writes: []*pb.Write{
- {
- Operation: &pb.Write_Update{
- Update: &pb.Document{
- Name: "projects/projectID/databases/(default)/documents/C/d",
- Fields: map[string]*pb.Value{
- "Time": tsval(time.Time{}),
- "Bytes": bytesval(nil),
- "Geo": nullValue,
- },
- },
- },
- CurrentDocument: &pb.Precondition{
- ConditionType: &pb.Precondition_Exists{false},
- },
- },
- },
- },
- commitResponseForSet,
- )
- _, err := c.Collection("C").Doc("d").Create(ctx, &create{})
- if err != nil {
- t.Fatal(err)
- }
- }
-
- func TestDocDelete(t *testing.T) {
- ctx := context.Background()
- c, srv := newMock(t)
- srv.addRPC(
- &pb.CommitRequest{
- Database: "projects/projectID/databases/(default)",
- Writes: []*pb.Write{
- {Operation: &pb.Write_Delete{"projects/projectID/databases/(default)/documents/C/d"}},
- },
- },
- &pb.CommitResponse{
- WriteResults: []*pb.WriteResult{{}},
- })
- wr, err := c.Collection("C").Doc("d").Delete(ctx)
- if err != nil {
- t.Fatal(err)
- }
- if !testEqual(wr, &WriteResult{}) {
- t.Errorf("got %+v, want %+v", wr, writeResultForSet)
- }
- }
-
- var (
- testData = map[string]interface{}{"a": 1}
- testFields = map[string]*pb.Value{"a": intval(1)}
- )
-
- // Update is tested by the conformance tests.
-
- func TestFPVsFromData(t *testing.T) {
- type S struct{ X int }
-
- for _, test := range []struct {
- in interface{}
- want []fpv
- }{
- {
- in: nil,
- want: []fpv{{nil, nil}},
- },
- {
- in: map[string]interface{}{"a": nil},
- want: []fpv{{[]string{"a"}, nil}},
- },
- {
- in: map[string]interface{}{"a": 1},
- want: []fpv{{[]string{"a"}, 1}},
- },
- {
- in: map[string]interface{}{
- "a": 1,
- "b": map[string]interface{}{"c": 2},
- },
- want: []fpv{{[]string{"a"}, 1}, {[]string{"b", "c"}, 2}},
- },
- {
- in: map[string]interface{}{"s": &S{X: 3}},
- want: []fpv{{[]string{"s"}, &S{X: 3}}},
- },
- } {
- var got []fpv
- fpvsFromData(reflect.ValueOf(test.in), nil, &got)
- sort.Sort(byFieldPath(got))
- if !testEqual(got, test.want) {
- t.Errorf("%+v: got %v, want %v", test.in, got, test.want)
- }
- }
- }
-
- type byFieldPath []fpv
-
- func (b byFieldPath) Len() int { return len(b) }
- func (b byFieldPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
- func (b byFieldPath) Less(i, j int) bool { return b[i].fieldPath.less(b[j].fieldPath) }
-
- func commitRequestForSet() *pb.CommitRequest {
- return &pb.CommitRequest{
- Database: "projects/projectID/databases/(default)",
- Writes: []*pb.Write{
- {
- Operation: &pb.Write_Update{
- Update: &pb.Document{
- Name: "projects/projectID/databases/(default)/documents/C/d",
- Fields: testFields,
- },
- },
- },
- },
- }
- }
-
- func TestUpdateProcess(t *testing.T) {
- for _, test := range []struct {
- in Update
- want fpv
- wantErr bool
- }{
- {
- in: Update{Path: "a", Value: 1},
- want: fpv{fieldPath: []string{"a"}, value: 1},
- },
- {
- in: Update{Path: "c.d", Value: Delete},
- want: fpv{fieldPath: []string{"c", "d"}, value: Delete},
- },
- {
- in: Update{FieldPath: []string{"*", "~"}, Value: ServerTimestamp},
- want: fpv{fieldPath: []string{"*", "~"}, value: ServerTimestamp},
- },
- {
- in: Update{Path: "*"},
- wantErr: true, // bad rune in path
- },
- {
- in: Update{Path: "a", FieldPath: []string{"b"}},
- wantErr: true, // both Path and FieldPath
- },
- {
- in: Update{Value: 1},
- wantErr: true, // neither Path nor FieldPath
- },
- {
- in: Update{FieldPath: []string{"", "a"}},
- wantErr: true, // empty FieldPath component
- },
- } {
- got, err := test.in.process()
- if test.wantErr {
- if err == nil {
- t.Errorf("%+v: got nil, want error", test.in)
- }
- } else if err != nil {
- t.Errorf("%+v: got error %v, want nil", test.in, err)
- } else if !testEqual(got, test.want) {
- t.Errorf("%+v: got %+v, want %+v", test.in, got, test.want)
- }
- }
- }
|