213 lines
6.0 KiB
Go
213 lines
6.0 KiB
Go
|
// Copyright 2017 Google Inc. All Rights Reserved.
|
||
|
//
|
||
|
// 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 (
|
||
|
"testing"
|
||
|
|
||
|
tspb "github.com/golang/protobuf/ptypes/timestamp"
|
||
|
"golang.org/x/net/context"
|
||
|
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
|
||
|
"google.golang.org/grpc/codes"
|
||
|
"google.golang.org/grpc/status"
|
||
|
)
|
||
|
|
||
|
var testClient = &Client{
|
||
|
projectID: "projectID",
|
||
|
databaseID: "(default)",
|
||
|
}
|
||
|
|
||
|
func TestClientCollectionAndDoc(t *testing.T) {
|
||
|
coll1 := testClient.Collection("X")
|
||
|
db := "projects/projectID/databases/(default)"
|
||
|
wantc1 := &CollectionRef{
|
||
|
c: testClient,
|
||
|
parentPath: db,
|
||
|
Parent: nil,
|
||
|
ID: "X",
|
||
|
Path: "projects/projectID/databases/(default)/documents/X",
|
||
|
Query: Query{c: testClient, collectionID: "X", parentPath: db},
|
||
|
}
|
||
|
if !testEqual(coll1, wantc1) {
|
||
|
t.Fatalf("got\n%+v\nwant\n%+v", coll1, wantc1)
|
||
|
}
|
||
|
doc1 := testClient.Doc("X/a")
|
||
|
wantd1 := &DocumentRef{
|
||
|
Parent: coll1,
|
||
|
ID: "a",
|
||
|
Path: "projects/projectID/databases/(default)/documents/X/a",
|
||
|
}
|
||
|
|
||
|
if !testEqual(doc1, wantd1) {
|
||
|
t.Fatalf("got %+v, want %+v", doc1, wantd1)
|
||
|
}
|
||
|
coll2 := testClient.Collection("X/a/Y")
|
||
|
parentPath := "projects/projectID/databases/(default)/documents/X/a"
|
||
|
wantc2 := &CollectionRef{
|
||
|
c: testClient,
|
||
|
parentPath: parentPath,
|
||
|
Parent: doc1,
|
||
|
ID: "Y",
|
||
|
Path: "projects/projectID/databases/(default)/documents/X/a/Y",
|
||
|
Query: Query{c: testClient, collectionID: "Y", parentPath: parentPath},
|
||
|
}
|
||
|
if !testEqual(coll2, wantc2) {
|
||
|
t.Fatalf("\ngot %+v\nwant %+v", coll2, wantc2)
|
||
|
}
|
||
|
doc2 := testClient.Doc("X/a/Y/b")
|
||
|
wantd2 := &DocumentRef{
|
||
|
Parent: coll2,
|
||
|
ID: "b",
|
||
|
Path: "projects/projectID/databases/(default)/documents/X/a/Y/b",
|
||
|
}
|
||
|
if !testEqual(doc2, wantd2) {
|
||
|
t.Fatalf("got %+v, want %+v", doc2, wantd2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestClientCollDocErrors(t *testing.T) {
|
||
|
for _, badColl := range []string{"", "/", "/a/", "/a/b", "a/b/", "a//b"} {
|
||
|
coll := testClient.Collection(badColl)
|
||
|
if coll != nil {
|
||
|
t.Errorf("coll path %q: got %+v, want nil", badColl, coll)
|
||
|
}
|
||
|
}
|
||
|
for _, badDoc := range []string{"", "a", "/", "/a", "a/", "a/b/c", "a//b/c"} {
|
||
|
doc := testClient.Doc(badDoc)
|
||
|
if doc != nil {
|
||
|
t.Errorf("doc path %q: got %+v, want nil", badDoc, doc)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetAll(t *testing.T) {
|
||
|
c, srv := newMock(t)
|
||
|
defer c.Close()
|
||
|
const dbPath = "projects/projectID/databases/(default)"
|
||
|
req := &pb.BatchGetDocumentsRequest{
|
||
|
Database: dbPath,
|
||
|
Documents: []string{
|
||
|
dbPath + "/documents/C/a",
|
||
|
dbPath + "/documents/C/b",
|
||
|
dbPath + "/documents/C/c",
|
||
|
},
|
||
|
}
|
||
|
testGetAll(t, c, srv, dbPath, func(drs []*DocumentRef) ([]*DocumentSnapshot, error) {
|
||
|
return c.GetAll(context.Background(), drs)
|
||
|
}, req)
|
||
|
}
|
||
|
|
||
|
func testGetAll(t *testing.T, c *Client, srv *mockServer, dbPath string, getAll func([]*DocumentRef) ([]*DocumentSnapshot, error), req *pb.BatchGetDocumentsRequest) {
|
||
|
wantPBDocs := []*pb.Document{
|
||
|
{
|
||
|
Name: dbPath + "/documents/C/a",
|
||
|
CreateTime: aTimestamp,
|
||
|
UpdateTime: aTimestamp,
|
||
|
Fields: map[string]*pb.Value{"f": intval(2)},
|
||
|
},
|
||
|
nil,
|
||
|
{
|
||
|
Name: dbPath + "/documents/C/c",
|
||
|
CreateTime: aTimestamp,
|
||
|
UpdateTime: aTimestamp,
|
||
|
Fields: map[string]*pb.Value{"f": intval(1)},
|
||
|
},
|
||
|
}
|
||
|
wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2, aTimestamp3}
|
||
|
srv.addRPC(req,
|
||
|
[]interface{}{
|
||
|
// deliberately put these out of order
|
||
|
&pb.BatchGetDocumentsResponse{
|
||
|
Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[2]},
|
||
|
ReadTime: aTimestamp3,
|
||
|
},
|
||
|
&pb.BatchGetDocumentsResponse{
|
||
|
Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[0]},
|
||
|
ReadTime: aTimestamp,
|
||
|
},
|
||
|
&pb.BatchGetDocumentsResponse{
|
||
|
Result: &pb.BatchGetDocumentsResponse_Missing{dbPath + "/documents/C/b"},
|
||
|
ReadTime: aTimestamp2,
|
||
|
},
|
||
|
},
|
||
|
)
|
||
|
coll := c.Collection("C")
|
||
|
var docRefs []*DocumentRef
|
||
|
for _, name := range []string{"a", "b", "c"} {
|
||
|
docRefs = append(docRefs, coll.Doc(name))
|
||
|
}
|
||
|
docs, err := getAll(docRefs)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if got, want := len(docs), len(wantPBDocs); got != want {
|
||
|
t.Errorf("got %d docs, wanted %d", got, want)
|
||
|
}
|
||
|
for i, got := range docs {
|
||
|
want, err := newDocumentSnapshot(docRefs[i], wantPBDocs[i], c, wantReadTimes[i])
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if diff := testDiff(got, want); diff != "" {
|
||
|
t.Errorf("#%d: got=--, want==++\n%s", i, diff)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetAllErrors(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
const (
|
||
|
dbPath = "projects/projectID/databases/(default)"
|
||
|
docPath = dbPath + "/documents/C/a"
|
||
|
)
|
||
|
c, srv := newMock(t)
|
||
|
if _, err := c.GetAll(ctx, []*DocumentRef{nil}); err != errNilDocRef {
|
||
|
t.Errorf("got %v, want errNilDocRef", err)
|
||
|
}
|
||
|
|
||
|
// Internal server error.
|
||
|
srv.addRPC(
|
||
|
&pb.BatchGetDocumentsRequest{
|
||
|
Database: dbPath,
|
||
|
Documents: []string{docPath},
|
||
|
},
|
||
|
[]interface{}{status.Errorf(codes.Internal, "")},
|
||
|
)
|
||
|
_, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")})
|
||
|
codeEq(t, "GetAll #1", codes.Internal, err)
|
||
|
|
||
|
// Doc appears as both found and missing (server bug).
|
||
|
srv.reset()
|
||
|
srv.addRPC(
|
||
|
&pb.BatchGetDocumentsRequest{
|
||
|
Database: dbPath,
|
||
|
Documents: []string{docPath},
|
||
|
},
|
||
|
[]interface{}{
|
||
|
&pb.BatchGetDocumentsResponse{
|
||
|
Result: &pb.BatchGetDocumentsResponse_Found{&pb.Document{Name: docPath}},
|
||
|
ReadTime: aTimestamp,
|
||
|
},
|
||
|
&pb.BatchGetDocumentsResponse{
|
||
|
Result: &pb.BatchGetDocumentsResponse_Missing{docPath},
|
||
|
ReadTime: aTimestamp,
|
||
|
},
|
||
|
},
|
||
|
)
|
||
|
if _, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")}); err == nil {
|
||
|
t.Error("got nil, want error")
|
||
|
}
|
||
|
}
|