You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

227 lines
6.3 KiB

  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package firestore
  15. import (
  16. "context"
  17. "testing"
  18. tspb "github.com/golang/protobuf/ptypes/timestamp"
  19. pb "google.golang.org/genproto/googleapis/firestore/v1"
  20. "google.golang.org/grpc/codes"
  21. "google.golang.org/grpc/status"
  22. )
  23. var testClient = &Client{
  24. projectID: "projectID",
  25. databaseID: "(default)",
  26. }
  27. func TestClientCollectionAndDoc(t *testing.T) {
  28. coll1 := testClient.Collection("X")
  29. db := "projects/projectID/databases/(default)"
  30. wantc1 := &CollectionRef{
  31. c: testClient,
  32. parentPath: db + "/documents",
  33. selfPath: "X",
  34. Parent: nil,
  35. ID: "X",
  36. Path: "projects/projectID/databases/(default)/documents/X",
  37. Query: Query{
  38. c: testClient,
  39. collectionID: "X",
  40. path: "projects/projectID/databases/(default)/documents/X",
  41. parentPath: db + "/documents",
  42. },
  43. }
  44. if !testEqual(coll1, wantc1) {
  45. t.Fatalf("got\n%+v\nwant\n%+v", coll1, wantc1)
  46. }
  47. doc1 := testClient.Doc("X/a")
  48. wantd1 := &DocumentRef{
  49. Parent: coll1,
  50. ID: "a",
  51. Path: "projects/projectID/databases/(default)/documents/X/a",
  52. shortPath: "X/a",
  53. }
  54. if !testEqual(doc1, wantd1) {
  55. t.Fatalf("got %+v, want %+v", doc1, wantd1)
  56. }
  57. coll2 := testClient.Collection("X/a/Y")
  58. parentPath := "projects/projectID/databases/(default)/documents/X/a"
  59. wantc2 := &CollectionRef{
  60. c: testClient,
  61. parentPath: parentPath,
  62. selfPath: "X/a/Y",
  63. Parent: doc1,
  64. ID: "Y",
  65. Path: "projects/projectID/databases/(default)/documents/X/a/Y",
  66. Query: Query{
  67. c: testClient,
  68. collectionID: "Y",
  69. parentPath: parentPath,
  70. path: "projects/projectID/databases/(default)/documents/X/a/Y",
  71. },
  72. }
  73. if !testEqual(coll2, wantc2) {
  74. t.Fatalf("\ngot %+v\nwant %+v", coll2, wantc2)
  75. }
  76. doc2 := testClient.Doc("X/a/Y/b")
  77. wantd2 := &DocumentRef{
  78. Parent: coll2,
  79. ID: "b",
  80. Path: "projects/projectID/databases/(default)/documents/X/a/Y/b",
  81. shortPath: "X/a/Y/b",
  82. }
  83. if !testEqual(doc2, wantd2) {
  84. t.Fatalf("got %+v, want %+v", doc2, wantd2)
  85. }
  86. }
  87. func TestClientCollDocErrors(t *testing.T) {
  88. for _, badColl := range []string{"", "/", "/a/", "/a/b", "a/b/", "a//b"} {
  89. coll := testClient.Collection(badColl)
  90. if coll != nil {
  91. t.Errorf("coll path %q: got %+v, want nil", badColl, coll)
  92. }
  93. }
  94. for _, badDoc := range []string{"", "a", "/", "/a", "a/", "a/b/c", "a//b/c"} {
  95. doc := testClient.Doc(badDoc)
  96. if doc != nil {
  97. t.Errorf("doc path %q: got %+v, want nil", badDoc, doc)
  98. }
  99. }
  100. }
  101. func TestGetAll(t *testing.T) {
  102. c, srv := newMock(t)
  103. defer c.Close()
  104. const dbPath = "projects/projectID/databases/(default)"
  105. req := &pb.BatchGetDocumentsRequest{
  106. Database: dbPath,
  107. Documents: []string{
  108. dbPath + "/documents/C/a",
  109. dbPath + "/documents/C/b",
  110. dbPath + "/documents/C/c",
  111. },
  112. }
  113. testGetAll(t, c, srv, dbPath, func(drs []*DocumentRef) ([]*DocumentSnapshot, error) {
  114. return c.GetAll(context.Background(), drs)
  115. }, req)
  116. }
  117. func testGetAll(t *testing.T, c *Client, srv *mockServer, dbPath string, getAll func([]*DocumentRef) ([]*DocumentSnapshot, error), req *pb.BatchGetDocumentsRequest) {
  118. wantPBDocs := []*pb.Document{
  119. {
  120. Name: dbPath + "/documents/C/a",
  121. CreateTime: aTimestamp,
  122. UpdateTime: aTimestamp,
  123. Fields: map[string]*pb.Value{"f": intval(2)},
  124. },
  125. nil,
  126. {
  127. Name: dbPath + "/documents/C/c",
  128. CreateTime: aTimestamp,
  129. UpdateTime: aTimestamp,
  130. Fields: map[string]*pb.Value{"f": intval(1)},
  131. },
  132. }
  133. wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2, aTimestamp3}
  134. srv.addRPC(req,
  135. []interface{}{
  136. // deliberately put these out of order
  137. &pb.BatchGetDocumentsResponse{
  138. Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[2]},
  139. ReadTime: aTimestamp3,
  140. },
  141. &pb.BatchGetDocumentsResponse{
  142. Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[0]},
  143. ReadTime: aTimestamp,
  144. },
  145. &pb.BatchGetDocumentsResponse{
  146. Result: &pb.BatchGetDocumentsResponse_Missing{dbPath + "/documents/C/b"},
  147. ReadTime: aTimestamp2,
  148. },
  149. },
  150. )
  151. coll := c.Collection("C")
  152. var docRefs []*DocumentRef
  153. for _, name := range []string{"a", "b", "c"} {
  154. docRefs = append(docRefs, coll.Doc(name))
  155. }
  156. docs, err := getAll(docRefs)
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. if got, want := len(docs), len(wantPBDocs); got != want {
  161. t.Errorf("got %d docs, wanted %d", got, want)
  162. }
  163. for i, got := range docs {
  164. want, err := newDocumentSnapshot(docRefs[i], wantPBDocs[i], c, wantReadTimes[i])
  165. if err != nil {
  166. t.Fatal(err)
  167. }
  168. if diff := testDiff(got, want); diff != "" {
  169. t.Errorf("#%d: got=--, want==++\n%s", i, diff)
  170. }
  171. }
  172. }
  173. func TestGetAllErrors(t *testing.T) {
  174. ctx := context.Background()
  175. const (
  176. dbPath = "projects/projectID/databases/(default)"
  177. docPath = dbPath + "/documents/C/a"
  178. )
  179. c, srv := newMock(t)
  180. if _, err := c.GetAll(ctx, []*DocumentRef{nil}); err != errNilDocRef {
  181. t.Errorf("got %v, want errNilDocRef", err)
  182. }
  183. // Internal server error.
  184. srv.addRPC(
  185. &pb.BatchGetDocumentsRequest{
  186. Database: dbPath,
  187. Documents: []string{docPath},
  188. },
  189. []interface{}{status.Errorf(codes.Internal, "")},
  190. )
  191. _, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")})
  192. codeEq(t, "GetAll #1", codes.Internal, err)
  193. // Doc appears as both found and missing (server bug).
  194. srv.reset()
  195. srv.addRPC(
  196. &pb.BatchGetDocumentsRequest{
  197. Database: dbPath,
  198. Documents: []string{docPath},
  199. },
  200. []interface{}{
  201. &pb.BatchGetDocumentsResponse{
  202. Result: &pb.BatchGetDocumentsResponse_Found{&pb.Document{Name: docPath}},
  203. ReadTime: aTimestamp,
  204. },
  205. &pb.BatchGetDocumentsResponse{
  206. Result: &pb.BatchGetDocumentsResponse_Missing{docPath},
  207. ReadTime: aTimestamp,
  208. },
  209. },
  210. )
  211. if _, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")}); err == nil {
  212. t.Error("got nil, want error")
  213. }
  214. }