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.
 
 
 

1340 lines
36 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. "errors"
  18. "flag"
  19. "fmt"
  20. "log"
  21. "math"
  22. "os"
  23. "path/filepath"
  24. "runtime"
  25. "sort"
  26. "testing"
  27. "time"
  28. "cloud.google.com/go/internal/pretty"
  29. "cloud.google.com/go/internal/testutil"
  30. "cloud.google.com/go/internal/uid"
  31. "github.com/google/go-cmp/cmp"
  32. "github.com/google/go-cmp/cmp/cmpopts"
  33. "google.golang.org/api/option"
  34. "google.golang.org/genproto/googleapis/type/latlng"
  35. "google.golang.org/grpc"
  36. "google.golang.org/grpc/codes"
  37. "google.golang.org/grpc/metadata"
  38. )
  39. func TestMain(m *testing.M) {
  40. initIntegrationTest()
  41. status := m.Run()
  42. cleanupIntegrationTest()
  43. os.Exit(status)
  44. }
  45. const (
  46. envProjID = "GCLOUD_TESTS_GOLANG_FIRESTORE_PROJECT_ID"
  47. envPrivateKey = "GCLOUD_TESTS_GOLANG_FIRESTORE_KEY"
  48. )
  49. var (
  50. iClient *Client
  51. iColl *CollectionRef
  52. collectionIDs = uid.NewSpace("go-integration-test", nil)
  53. )
  54. func initIntegrationTest() {
  55. flag.Parse() // needed for testing.Short()
  56. if testing.Short() {
  57. return
  58. }
  59. ctx := context.Background()
  60. testProjectID := os.Getenv(envProjID)
  61. if testProjectID == "" {
  62. log.Println("Integration tests skipped. See CONTRIBUTING.md for details")
  63. return
  64. }
  65. ts := testutil.TokenSourceEnv(ctx, envPrivateKey,
  66. "https://www.googleapis.com/auth/cloud-platform",
  67. "https://www.googleapis.com/auth/datastore")
  68. if ts == nil {
  69. log.Fatal("The project key must be set. See CONTRIBUTING.md for details")
  70. }
  71. ti := &testInterceptor{dbPath: "projects/" + testProjectID + "/databases/(default)"}
  72. c, err := NewClient(ctx, testProjectID,
  73. option.WithTokenSource(ts),
  74. option.WithGRPCDialOption(grpc.WithUnaryInterceptor(ti.interceptUnary)),
  75. option.WithGRPCDialOption(grpc.WithStreamInterceptor(ti.interceptStream)),
  76. )
  77. if err != nil {
  78. log.Fatalf("NewClient: %v", err)
  79. }
  80. iClient = c
  81. iColl = c.Collection(collectionIDs.New())
  82. refDoc := iColl.NewDoc()
  83. integrationTestMap["ref"] = refDoc
  84. wantIntegrationTestMap["ref"] = refDoc
  85. integrationTestStruct.Ref = refDoc
  86. }
  87. type testInterceptor struct {
  88. dbPath string
  89. }
  90. func (ti *testInterceptor) interceptUnary(ctx context.Context, method string, req, res interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  91. ti.checkMetadata(ctx, method)
  92. return invoker(ctx, method, req, res, cc, opts...)
  93. }
  94. func (ti *testInterceptor) interceptStream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
  95. ti.checkMetadata(ctx, method)
  96. return streamer(ctx, desc, cc, method, opts...)
  97. }
  98. func (ti *testInterceptor) checkMetadata(ctx context.Context, method string) {
  99. md, ok := metadata.FromOutgoingContext(ctx)
  100. if !ok {
  101. log.Fatalf("method %s: bad metadata", method)
  102. }
  103. for _, h := range []string{"google-cloud-resource-prefix", "x-goog-api-client"} {
  104. v, ok := md[h]
  105. if !ok {
  106. log.Fatalf("method %s, header %s missing", method, h)
  107. }
  108. if len(v) != 1 {
  109. log.Fatalf("method %s, header %s: bad value %v", method, h, v)
  110. }
  111. }
  112. v := md["google-cloud-resource-prefix"][0]
  113. if v != ti.dbPath {
  114. log.Fatalf("method %s: bad resource prefix header: %q", method, v)
  115. }
  116. }
  117. func cleanupIntegrationTest() {
  118. if iClient == nil {
  119. return
  120. }
  121. // TODO(jba): delete everything in integrationColl.
  122. iClient.Close()
  123. }
  124. // integrationClient should be called by integration tests to get a valid client. It will never
  125. // return nil. If integrationClient returns, an integration test can proceed without
  126. // further checks.
  127. func integrationClient(t *testing.T) *Client {
  128. if testing.Short() {
  129. t.Skip("Integration tests skipped in short mode")
  130. }
  131. if iClient == nil {
  132. t.SkipNow() // log message printed in initIntegrationTest
  133. }
  134. return iClient
  135. }
  136. func integrationColl(t *testing.T) *CollectionRef {
  137. _ = integrationClient(t)
  138. return iColl
  139. }
  140. type integrationTestStructType struct {
  141. Int int
  142. Str string
  143. Bool bool
  144. Float float32
  145. Null interface{}
  146. Bytes []byte
  147. Time time.Time
  148. Geo, NilGeo *latlng.LatLng
  149. Ref *DocumentRef
  150. }
  151. var (
  152. integrationTime = time.Date(2017, 3, 20, 1, 2, 3, 456789, time.UTC)
  153. // Firestore times are accurate only to microseconds.
  154. wantIntegrationTime = time.Date(2017, 3, 20, 1, 2, 3, 456000, time.UTC)
  155. integrationGeo = &latlng.LatLng{Latitude: 30, Longitude: 70}
  156. // Use this when writing a doc.
  157. integrationTestMap = map[string]interface{}{
  158. "int": 1,
  159. "str": "two",
  160. "bool": true,
  161. "float": 3.14,
  162. "null": nil,
  163. "bytes": []byte("bytes"),
  164. "*": map[string]interface{}{"`": 4},
  165. "time": integrationTime,
  166. "geo": integrationGeo,
  167. "ref": nil, // populated by initIntegrationTest
  168. }
  169. // The returned data is slightly different.
  170. wantIntegrationTestMap = map[string]interface{}{
  171. "int": int64(1),
  172. "str": "two",
  173. "bool": true,
  174. "float": 3.14,
  175. "null": nil,
  176. "bytes": []byte("bytes"),
  177. "*": map[string]interface{}{"`": int64(4)},
  178. "time": wantIntegrationTime,
  179. "geo": integrationGeo,
  180. "ref": nil, // populated by initIntegrationTest
  181. }
  182. integrationTestStruct = integrationTestStructType{
  183. Int: 1,
  184. Str: "two",
  185. Bool: true,
  186. Float: 3.14,
  187. Null: nil,
  188. Bytes: []byte("bytes"),
  189. Time: integrationTime,
  190. Geo: integrationGeo,
  191. NilGeo: nil,
  192. Ref: nil, // populated by initIntegrationTest
  193. }
  194. )
  195. func TestIntegration_Create(t *testing.T) {
  196. ctx := context.Background()
  197. doc := integrationColl(t).NewDoc()
  198. start := time.Now()
  199. h := testHelper{t}
  200. wr := h.mustCreate(doc, integrationTestMap)
  201. end := time.Now()
  202. checkTimeBetween(t, wr.UpdateTime, start, end)
  203. _, err := doc.Create(ctx, integrationTestMap)
  204. codeEq(t, "Create on a present doc", codes.AlreadyExists, err)
  205. // OK to create an empty document.
  206. _, err = integrationColl(t).NewDoc().Create(ctx, map[string]interface{}{})
  207. codeEq(t, "Create empty doc", codes.OK, err)
  208. }
  209. func TestIntegration_Get(t *testing.T) {
  210. ctx := context.Background()
  211. doc := integrationColl(t).NewDoc()
  212. h := testHelper{t}
  213. h.mustCreate(doc, integrationTestMap)
  214. ds := h.mustGet(doc)
  215. if ds.CreateTime != ds.UpdateTime {
  216. t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime)
  217. }
  218. got := ds.Data()
  219. if want := wantIntegrationTestMap; !testEqual(got, want) {
  220. t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
  221. }
  222. doc = integrationColl(t).NewDoc()
  223. empty := map[string]interface{}{}
  224. h.mustCreate(doc, empty)
  225. ds = h.mustGet(doc)
  226. if ds.CreateTime != ds.UpdateTime {
  227. t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime)
  228. }
  229. if got, want := ds.Data(), empty; !testEqual(got, want) {
  230. t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
  231. }
  232. ds, err := integrationColl(t).NewDoc().Get(ctx)
  233. codeEq(t, "Get on a missing doc", codes.NotFound, err)
  234. if ds == nil || ds.Exists() {
  235. t.Fatal("got nil or existing doc snapshot, want !ds.Exists")
  236. }
  237. if ds.ReadTime.IsZero() {
  238. t.Error("got zero read time")
  239. }
  240. }
  241. func TestIntegration_GetAll(t *testing.T) {
  242. type getAll struct{ N int }
  243. h := testHelper{t}
  244. coll := integrationColl(t)
  245. ctx := context.Background()
  246. var docRefs []*DocumentRef
  247. for i := 0; i < 5; i++ {
  248. doc := coll.NewDoc()
  249. docRefs = append(docRefs, doc)
  250. if i != 3 {
  251. h.mustCreate(doc, getAll{N: i})
  252. }
  253. }
  254. docSnapshots, err := iClient.GetAll(ctx, docRefs)
  255. if err != nil {
  256. t.Fatal(err)
  257. }
  258. if got, want := len(docSnapshots), len(docRefs); got != want {
  259. t.Fatalf("got %d snapshots, want %d", got, want)
  260. }
  261. for i, ds := range docSnapshots {
  262. if i == 3 {
  263. if ds == nil || ds.Exists() {
  264. t.Fatal("got nil or existing doc snapshot, want !ds.Exists")
  265. }
  266. err := ds.DataTo(nil)
  267. codeEq(t, "DataTo on a missing doc", codes.NotFound, err)
  268. } else {
  269. var got getAll
  270. if err := ds.DataTo(&got); err != nil {
  271. t.Fatal(err)
  272. }
  273. want := getAll{N: i}
  274. if got != want {
  275. t.Errorf("%d: got %+v, want %+v", i, got, want)
  276. }
  277. }
  278. if ds.ReadTime.IsZero() {
  279. t.Errorf("%d: got zero read time", i)
  280. }
  281. }
  282. }
  283. func TestIntegration_Add(t *testing.T) {
  284. start := time.Now()
  285. _, wr, err := integrationColl(t).Add(context.Background(), integrationTestMap)
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. end := time.Now()
  290. checkTimeBetween(t, wr.UpdateTime, start, end)
  291. }
  292. func TestIntegration_Set(t *testing.T) {
  293. coll := integrationColl(t)
  294. h := testHelper{t}
  295. ctx := context.Background()
  296. // Set Should be able to create a new doc.
  297. doc := coll.NewDoc()
  298. wr1 := h.mustSet(doc, integrationTestMap)
  299. // Calling Set on the doc completely replaces the contents.
  300. // The update time should increase.
  301. newData := map[string]interface{}{
  302. "str": "change",
  303. "x": "1",
  304. }
  305. wr2 := h.mustSet(doc, newData)
  306. if !wr1.UpdateTime.Before(wr2.UpdateTime) {
  307. t.Errorf("update time did not increase: old=%s, new=%s", wr1.UpdateTime, wr2.UpdateTime)
  308. }
  309. ds := h.mustGet(doc)
  310. if got := ds.Data(); !testEqual(got, newData) {
  311. t.Errorf("got %v, want %v", got, newData)
  312. }
  313. newData = map[string]interface{}{
  314. "str": "1",
  315. "x": "2",
  316. "y": "3",
  317. }
  318. // SetOptions:
  319. // Only fields mentioned in the Merge option will be changed.
  320. // In this case, "str" will not be changed to "1".
  321. wr3, err := doc.Set(ctx, newData, Merge([]string{"x"}, []string{"y"}))
  322. if err != nil {
  323. t.Fatal(err)
  324. }
  325. ds = h.mustGet(doc)
  326. want := map[string]interface{}{
  327. "str": "change",
  328. "x": "2",
  329. "y": "3",
  330. }
  331. if got := ds.Data(); !testEqual(got, want) {
  332. t.Errorf("got %v, want %v", got, want)
  333. }
  334. if !wr2.UpdateTime.Before(wr3.UpdateTime) {
  335. t.Errorf("update time did not increase: old=%s, new=%s", wr2.UpdateTime, wr3.UpdateTime)
  336. }
  337. // Another way to change only x and y is to pass a map with only
  338. // those keys, and use MergeAll.
  339. wr4, err := doc.Set(ctx, map[string]interface{}{"x": "4", "y": "5"}, MergeAll)
  340. if err != nil {
  341. t.Fatal(err)
  342. }
  343. ds = h.mustGet(doc)
  344. want = map[string]interface{}{
  345. "str": "change",
  346. "x": "4",
  347. "y": "5",
  348. }
  349. if got := ds.Data(); !testEqual(got, want) {
  350. t.Errorf("got %v, want %v", got, want)
  351. }
  352. if !wr3.UpdateTime.Before(wr4.UpdateTime) {
  353. t.Errorf("update time did not increase: old=%s, new=%s", wr3.UpdateTime, wr4.UpdateTime)
  354. }
  355. // use firestore.Delete to delete a field.
  356. // TODO(deklerk) We should be able to use mustSet, but then we get a test error. We should investigate this.
  357. _, err = doc.Set(ctx, map[string]interface{}{"str": Delete}, MergeAll)
  358. if err != nil {
  359. t.Fatal(err)
  360. }
  361. ds = h.mustGet(doc)
  362. want = map[string]interface{}{
  363. "x": "4",
  364. "y": "5",
  365. }
  366. if got := ds.Data(); !testEqual(got, want) {
  367. t.Errorf("got %v, want %v", got, want)
  368. }
  369. // Writing an empty doc with MergeAll should create the doc.
  370. doc2 := coll.NewDoc()
  371. want = map[string]interface{}{}
  372. h.mustSet(doc2, want, MergeAll)
  373. ds = h.mustGet(doc2)
  374. if got := ds.Data(); !testEqual(got, want) {
  375. t.Errorf("got %v, want %v", got, want)
  376. }
  377. }
  378. func TestIntegration_Delete(t *testing.T) {
  379. ctx := context.Background()
  380. doc := integrationColl(t).NewDoc()
  381. h := testHelper{t}
  382. h.mustCreate(doc, integrationTestMap)
  383. h.mustDelete(doc)
  384. // Confirm that doc doesn't exist.
  385. if _, err := doc.Get(ctx); grpc.Code(err) != codes.NotFound {
  386. t.Fatalf("got error <%v>, want NotFound", err)
  387. }
  388. er := func(_ *WriteResult, err error) error { return err }
  389. codeEq(t, "Delete on a missing doc", codes.OK,
  390. er(doc.Delete(ctx)))
  391. // TODO(jba): confirm that the server should return InvalidArgument instead of
  392. // FailedPrecondition.
  393. wr := h.mustCreate(doc, integrationTestMap)
  394. codeEq(t, "Delete with wrong LastUpdateTime", codes.FailedPrecondition,
  395. er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
  396. codeEq(t, "Delete with right LastUpdateTime", codes.OK,
  397. er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime))))
  398. }
  399. func TestIntegration_Update(t *testing.T) {
  400. ctx := context.Background()
  401. doc := integrationColl(t).NewDoc()
  402. h := testHelper{t}
  403. h.mustCreate(doc, integrationTestMap)
  404. fpus := []Update{
  405. {Path: "bool", Value: false},
  406. {Path: "time", Value: 17},
  407. {FieldPath: []string{"*", "`"}, Value: 18},
  408. {Path: "null", Value: Delete},
  409. {Path: "noSuchField", Value: Delete}, // deleting a non-existent field is a no-op
  410. }
  411. wr := h.mustUpdate(doc, fpus)
  412. ds := h.mustGet(doc)
  413. got := ds.Data()
  414. want := copyMap(wantIntegrationTestMap)
  415. want["bool"] = false
  416. want["time"] = int64(17)
  417. want["*"] = map[string]interface{}{"`": int64(18)}
  418. delete(want, "null")
  419. if !testEqual(got, want) {
  420. t.Errorf("got\n%#v\nwant\n%#v", got, want)
  421. }
  422. er := func(_ *WriteResult, err error) error { return err }
  423. codeEq(t, "Update on missing doc", codes.NotFound,
  424. er(integrationColl(t).NewDoc().Update(ctx, fpus)))
  425. codeEq(t, "Update with wrong LastUpdateTime", codes.FailedPrecondition,
  426. er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
  427. codeEq(t, "Update with right LastUpdateTime", codes.OK,
  428. er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime))))
  429. }
  430. func TestIntegration_Collections(t *testing.T) {
  431. ctx := context.Background()
  432. c := integrationClient(t)
  433. h := testHelper{t}
  434. got, err := c.Collections(ctx).GetAll()
  435. if err != nil {
  436. t.Fatal(err)
  437. }
  438. // There should be at least one collection.
  439. if len(got) == 0 {
  440. t.Error("got 0 top-level collections, want at least one")
  441. }
  442. doc := integrationColl(t).NewDoc()
  443. got, err = doc.Collections(ctx).GetAll()
  444. if err != nil {
  445. t.Fatal(err)
  446. }
  447. if len(got) != 0 {
  448. t.Errorf("got %d collections, want 0", len(got))
  449. }
  450. var want []*CollectionRef
  451. for i := 0; i < 3; i++ {
  452. id := collectionIDs.New()
  453. cr := doc.Collection(id)
  454. want = append(want, cr)
  455. h.mustCreate(cr.NewDoc(), integrationTestMap)
  456. }
  457. got, err = doc.Collections(ctx).GetAll()
  458. if err != nil {
  459. t.Fatal(err)
  460. }
  461. if !testEqual(got, want) {
  462. t.Errorf("got\n%#v\nwant\n%#v", got, want)
  463. }
  464. }
  465. func TestIntegration_ServerTimestamp(t *testing.T) {
  466. type S struct {
  467. A int
  468. B time.Time
  469. C time.Time `firestore:"C.C,serverTimestamp"`
  470. D map[string]interface{}
  471. E time.Time `firestore:",omitempty,serverTimestamp"`
  472. }
  473. data := S{
  474. A: 1,
  475. B: aTime,
  476. // C is unset, so will get the server timestamp.
  477. D: map[string]interface{}{"x": ServerTimestamp},
  478. // E is unset, so will get the server timestamp.
  479. }
  480. h := testHelper{t}
  481. doc := integrationColl(t).NewDoc()
  482. // Bound times of the RPC, with some slack for clock skew.
  483. start := time.Now()
  484. h.mustCreate(doc, data)
  485. end := time.Now()
  486. ds := h.mustGet(doc)
  487. var got S
  488. if err := ds.DataTo(&got); err != nil {
  489. t.Fatal(err)
  490. }
  491. if !testEqual(got.B, aTime) {
  492. t.Errorf("B: got %s, want %s", got.B, aTime)
  493. }
  494. checkTimeBetween(t, got.C, start, end)
  495. if g, w := got.D["x"], got.C; !testEqual(g, w) {
  496. t.Errorf(`D["x"] = %s, want equal to C (%s)`, g, w)
  497. }
  498. if g, w := got.E, got.C; !testEqual(g, w) {
  499. t.Errorf(`E = %s, want equal to C (%s)`, g, w)
  500. }
  501. }
  502. func TestIntegration_MergeServerTimestamp(t *testing.T) {
  503. doc := integrationColl(t).NewDoc()
  504. h := testHelper{t}
  505. // Create a doc with an ordinary field "a" and a ServerTimestamp field "b".
  506. h.mustSet(doc, map[string]interface{}{"a": 1, "b": ServerTimestamp})
  507. docSnap := h.mustGet(doc)
  508. data1 := docSnap.Data()
  509. // Merge with a document with a different value of "a". However,
  510. // specify only "b" in the list of merge fields.
  511. h.mustSet(doc, map[string]interface{}{"a": 2, "b": ServerTimestamp}, Merge([]string{"b"}))
  512. // The result should leave "a" unchanged, while "b" is updated.
  513. docSnap = h.mustGet(doc)
  514. data2 := docSnap.Data()
  515. if got, want := data2["a"], data1["a"]; got != want {
  516. t.Errorf("got %v, want %v", got, want)
  517. }
  518. t1 := data1["b"].(time.Time)
  519. t2 := data2["b"].(time.Time)
  520. if !t1.Before(t2) {
  521. t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2)
  522. }
  523. }
  524. func TestIntegration_MergeNestedServerTimestamp(t *testing.T) {
  525. doc := integrationColl(t).NewDoc()
  526. h := testHelper{t}
  527. // Create a doc with an ordinary field "a" a ServerTimestamp field "b",
  528. // and a second ServerTimestamp field "c.d".
  529. h.mustSet(doc, map[string]interface{}{
  530. "a": 1,
  531. "b": ServerTimestamp,
  532. "c": map[string]interface{}{"d": ServerTimestamp},
  533. })
  534. data1 := h.mustGet(doc).Data()
  535. // Merge with a document with a different value of "a". However,
  536. // specify only "c.d" in the list of merge fields.
  537. h.mustSet(doc, map[string]interface{}{
  538. "a": 2,
  539. "b": ServerTimestamp,
  540. "c": map[string]interface{}{"d": ServerTimestamp},
  541. }, Merge([]string{"c", "d"}))
  542. // The result should leave "a" and "b" unchanged, while "c.d" is updated.
  543. data2 := h.mustGet(doc).Data()
  544. if got, want := data2["a"], data1["a"]; got != want {
  545. t.Errorf("a: got %v, want %v", got, want)
  546. }
  547. want := data1["b"].(time.Time)
  548. got := data2["b"].(time.Time)
  549. if !got.Equal(want) {
  550. t.Errorf("b: got %s, want %s", got, want)
  551. }
  552. t1 := data1["c"].(map[string]interface{})["d"].(time.Time)
  553. t2 := data2["c"].(map[string]interface{})["d"].(time.Time)
  554. if !t1.Before(t2) {
  555. t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2)
  556. }
  557. }
  558. func TestIntegration_WriteBatch(t *testing.T) {
  559. ctx := context.Background()
  560. b := integrationClient(t).Batch()
  561. h := testHelper{t}
  562. doc1 := iColl.NewDoc()
  563. doc2 := iColl.NewDoc()
  564. b.Create(doc1, integrationTestMap)
  565. b.Set(doc2, integrationTestMap)
  566. b.Update(doc1, []Update{{Path: "bool", Value: false}})
  567. b.Update(doc1, []Update{{Path: "str", Value: Delete}})
  568. wrs, err := b.Commit(ctx)
  569. if err != nil {
  570. t.Fatal(err)
  571. }
  572. if got, want := len(wrs), 4; got != want {
  573. t.Fatalf("got %d WriteResults, want %d", got, want)
  574. }
  575. got1 := h.mustGet(doc1).Data()
  576. want := copyMap(wantIntegrationTestMap)
  577. want["bool"] = false
  578. delete(want, "str")
  579. if !testEqual(got1, want) {
  580. t.Errorf("got\n%#v\nwant\n%#v", got1, want)
  581. }
  582. got2 := h.mustGet(doc2).Data()
  583. if !testEqual(got2, wantIntegrationTestMap) {
  584. t.Errorf("got\n%#v\nwant\n%#v", got2, wantIntegrationTestMap)
  585. }
  586. // TODO(jba): test two updates to the same document when it is supported.
  587. // TODO(jba): test verify when it is supported.
  588. }
  589. func TestIntegration_Query(t *testing.T) {
  590. ctx := context.Background()
  591. coll := integrationColl(t)
  592. h := testHelper{t}
  593. var wants []map[string]interface{}
  594. for i := 0; i < 3; i++ {
  595. doc := coll.NewDoc()
  596. // To support running this test in parallel with the others, use a field name
  597. // that we don't use anywhere else.
  598. h.mustCreate(doc, map[string]interface{}{"q": i, "x": 1})
  599. wants = append(wants, map[string]interface{}{"q": int64(i)})
  600. }
  601. q := coll.Select("q").OrderBy("q", Asc)
  602. for i, test := range []struct {
  603. q Query
  604. want []map[string]interface{}
  605. }{
  606. {q, wants},
  607. {q.Where("q", ">", 1), wants[2:]},
  608. {q.WherePath([]string{"q"}, ">", 1), wants[2:]},
  609. {q.Offset(1).Limit(1), wants[1:2]},
  610. {q.StartAt(1), wants[1:]},
  611. {q.StartAfter(1), wants[2:]},
  612. {q.EndAt(1), wants[:2]},
  613. {q.EndBefore(1), wants[:1]},
  614. } {
  615. gotDocs, err := test.q.Documents(ctx).GetAll()
  616. if err != nil {
  617. t.Errorf("#%d: %+v: %v", i, test.q, err)
  618. continue
  619. }
  620. if len(gotDocs) != len(test.want) {
  621. t.Errorf("#%d: %+v: got %d docs, want %d", i, test.q, len(gotDocs), len(test.want))
  622. continue
  623. }
  624. for j, g := range gotDocs {
  625. if got, want := g.Data(), test.want[j]; !testEqual(got, want) {
  626. t.Errorf("#%d: %+v, #%d: got\n%+v\nwant\n%+v", i, test.q, j, got, want)
  627. }
  628. }
  629. }
  630. _, err := coll.Select("q").Where("x", "==", 1).OrderBy("q", Asc).Documents(ctx).GetAll()
  631. codeEq(t, "Where and OrderBy on different fields without an index", codes.FailedPrecondition, err)
  632. // Using the collection itself as the query should return the full documents.
  633. allDocs, err := coll.Documents(ctx).GetAll()
  634. if err != nil {
  635. t.Fatal(err)
  636. }
  637. seen := map[int64]bool{} // "q" values we see
  638. for _, d := range allDocs {
  639. data := d.Data()
  640. q, ok := data["q"]
  641. if !ok {
  642. // A document from another test.
  643. continue
  644. }
  645. if seen[q.(int64)] {
  646. t.Errorf("%v: duplicate doc", data)
  647. }
  648. seen[q.(int64)] = true
  649. if data["x"] != int64(1) {
  650. t.Errorf("%v: wrong or missing 'x'", data)
  651. }
  652. if len(data) != 2 {
  653. t.Errorf("%v: want two keys", data)
  654. }
  655. }
  656. if got, want := len(seen), len(wants); got != want {
  657. t.Errorf("got %d docs with 'q', want %d", len(seen), len(wants))
  658. }
  659. }
  660. // Test unary filters.
  661. func TestIntegration_QueryUnary(t *testing.T) {
  662. ctx := context.Background()
  663. coll := integrationColl(t)
  664. h := testHelper{t}
  665. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": "a"})
  666. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": nil})
  667. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": math.NaN()})
  668. wantNull := map[string]interface{}{"q": nil}
  669. wantNaN := map[string]interface{}{"q": math.NaN()}
  670. base := coll.Select("q").Where("x", "==", 2)
  671. for _, test := range []struct {
  672. q Query
  673. want map[string]interface{}
  674. }{
  675. {base.Where("q", "==", nil), wantNull},
  676. {base.Where("q", "==", math.NaN()), wantNaN},
  677. } {
  678. got, err := test.q.Documents(ctx).GetAll()
  679. if err != nil {
  680. t.Fatal(err)
  681. }
  682. if len(got) != 1 {
  683. t.Errorf("got %d responses, want 1", len(got))
  684. continue
  685. }
  686. if g, w := got[0].Data(), test.want; !testEqual(g, w) {
  687. t.Errorf("%v: got %v, want %v", test.q, g, w)
  688. }
  689. }
  690. }
  691. // Test the special DocumentID field in queries.
  692. func TestIntegration_QueryName(t *testing.T) {
  693. ctx := context.Background()
  694. h := testHelper{t}
  695. checkIDs := func(q Query, wantIDs []string) {
  696. gots, err := q.Documents(ctx).GetAll()
  697. if err != nil {
  698. t.Fatal(err)
  699. }
  700. if len(gots) != len(wantIDs) {
  701. t.Fatalf("got %d, want %d", len(gots), len(wantIDs))
  702. }
  703. for i, g := range gots {
  704. if got, want := g.Ref.ID, wantIDs[i]; got != want {
  705. t.Errorf("#%d: got %s, want %s", i, got, want)
  706. }
  707. }
  708. }
  709. coll := integrationColl(t)
  710. var wantIDs []string
  711. for i := 0; i < 3; i++ {
  712. doc := coll.NewDoc()
  713. h.mustCreate(doc, map[string]interface{}{"nm": 1})
  714. wantIDs = append(wantIDs, doc.ID)
  715. }
  716. sort.Strings(wantIDs)
  717. q := coll.Where("nm", "==", 1).OrderBy(DocumentID, Asc)
  718. checkIDs(q, wantIDs)
  719. // Empty Select.
  720. q = coll.Select().Where("nm", "==", 1).OrderBy(DocumentID, Asc)
  721. checkIDs(q, wantIDs)
  722. // Test cursors with __name__.
  723. checkIDs(q.StartAt(wantIDs[1]), wantIDs[1:])
  724. checkIDs(q.EndAt(wantIDs[1]), wantIDs[:2])
  725. }
  726. func TestIntegration_QueryNested(t *testing.T) {
  727. ctx := context.Background()
  728. h := testHelper{t}
  729. coll1 := integrationColl(t)
  730. doc1 := coll1.NewDoc()
  731. coll2 := doc1.Collection(collectionIDs.New())
  732. doc2 := coll2.NewDoc()
  733. wantData := map[string]interface{}{"x": int64(1)}
  734. h.mustCreate(doc2, wantData)
  735. q := coll2.Select("x")
  736. got, err := q.Documents(ctx).GetAll()
  737. if err != nil {
  738. t.Fatal(err)
  739. }
  740. if len(got) != 1 {
  741. t.Fatalf("got %d docs, want 1", len(got))
  742. }
  743. if gotData := got[0].Data(); !testEqual(gotData, wantData) {
  744. t.Errorf("got\n%+v\nwant\n%+v", gotData, wantData)
  745. }
  746. }
  747. func TestIntegration_RunTransaction(t *testing.T) {
  748. ctx := context.Background()
  749. h := testHelper{t}
  750. type Player struct {
  751. Name string
  752. Score int
  753. Star bool `firestore:"*"`
  754. }
  755. pat := Player{Name: "Pat", Score: 3, Star: false}
  756. client := integrationClient(t)
  757. patDoc := iColl.Doc("pat")
  758. var anError error
  759. incPat := func(_ context.Context, tx *Transaction) error {
  760. doc, err := tx.Get(patDoc)
  761. if err != nil {
  762. return err
  763. }
  764. score, err := doc.DataAt("Score")
  765. if err != nil {
  766. return err
  767. }
  768. // Since the Star field is called "*", we must use DataAtPath to get it.
  769. star, err := doc.DataAtPath([]string{"*"})
  770. if err != nil {
  771. return err
  772. }
  773. err = tx.Update(patDoc, []Update{{Path: "Score", Value: int(score.(int64) + 7)}})
  774. if err != nil {
  775. return err
  776. }
  777. // Since the Star field is called "*", we must use Update to change it.
  778. err = tx.Update(patDoc,
  779. []Update{{FieldPath: []string{"*"}, Value: !star.(bool)}})
  780. if err != nil {
  781. return err
  782. }
  783. return anError
  784. }
  785. h.mustCreate(patDoc, pat)
  786. err := client.RunTransaction(ctx, incPat)
  787. if err != nil {
  788. t.Fatal(err)
  789. }
  790. ds := h.mustGet(patDoc)
  791. var got Player
  792. if err := ds.DataTo(&got); err != nil {
  793. t.Fatal(err)
  794. }
  795. want := Player{Name: "Pat", Score: 10, Star: true}
  796. if got != want {
  797. t.Errorf("got %+v, want %+v", got, want)
  798. }
  799. // Function returns error, so transaction is rolled back and no writes happen.
  800. anError = errors.New("bad")
  801. err = client.RunTransaction(ctx, incPat)
  802. if err != anError {
  803. t.Fatalf("got %v, want %v", err, anError)
  804. }
  805. if err := ds.DataTo(&got); err != nil {
  806. t.Fatal(err)
  807. }
  808. // want is same as before.
  809. if got != want {
  810. t.Errorf("got %+v, want %+v", got, want)
  811. }
  812. }
  813. func TestIntegration_TransactionGetAll(t *testing.T) {
  814. ctx := context.Background()
  815. h := testHelper{t}
  816. type Player struct {
  817. Name string
  818. Score int
  819. }
  820. lee := Player{Name: "Lee", Score: 3}
  821. sam := Player{Name: "Sam", Score: 1}
  822. client := integrationClient(t)
  823. leeDoc := iColl.Doc("lee")
  824. samDoc := iColl.Doc("sam")
  825. h.mustCreate(leeDoc, lee)
  826. h.mustCreate(samDoc, sam)
  827. err := client.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error {
  828. docs, err := tx.GetAll([]*DocumentRef{samDoc, leeDoc})
  829. if err != nil {
  830. return err
  831. }
  832. for i, want := range []Player{sam, lee} {
  833. var got Player
  834. if err := docs[i].DataTo(&got); err != nil {
  835. return err
  836. }
  837. if !testutil.Equal(got, want) {
  838. return fmt.Errorf("got %+v, want %+v", got, want)
  839. }
  840. }
  841. return nil
  842. })
  843. if err != nil {
  844. t.Fatal(err)
  845. }
  846. }
  847. func TestIntegration_WatchDocument(t *testing.T) {
  848. coll := integrationColl(t)
  849. ctx := context.Background()
  850. h := testHelper{t}
  851. doc := coll.NewDoc()
  852. it := doc.Snapshots(ctx)
  853. defer it.Stop()
  854. next := func() *DocumentSnapshot {
  855. snap, err := it.Next()
  856. if err != nil {
  857. t.Fatal(err)
  858. }
  859. return snap
  860. }
  861. snap := next()
  862. if snap.Exists() {
  863. t.Fatal("snapshot exists; it should not")
  864. }
  865. want := map[string]interface{}{"a": int64(1), "b": "two"}
  866. h.mustCreate(doc, want)
  867. snap = next()
  868. if got := snap.Data(); !testutil.Equal(got, want) {
  869. t.Fatalf("got %v, want %v", got, want)
  870. }
  871. h.mustUpdate(doc, []Update{{Path: "a", Value: int64(2)}})
  872. want["a"] = int64(2)
  873. snap = next()
  874. if got := snap.Data(); !testutil.Equal(got, want) {
  875. t.Fatalf("got %v, want %v", got, want)
  876. }
  877. h.mustDelete(doc)
  878. snap = next()
  879. if snap.Exists() {
  880. t.Fatal("snapshot exists; it should not")
  881. }
  882. h.mustCreate(doc, want)
  883. snap = next()
  884. if got := snap.Data(); !testutil.Equal(got, want) {
  885. t.Fatalf("got %v, want %v", got, want)
  886. }
  887. }
  888. func TestIntegration_ArrayUnion_Create(t *testing.T) {
  889. path := "somepath"
  890. data := map[string]interface{}{
  891. path: ArrayUnion("a", "b"),
  892. }
  893. doc := integrationColl(t).NewDoc()
  894. h := testHelper{t}
  895. h.mustCreate(doc, data)
  896. ds := h.mustGet(doc)
  897. var gotMap map[string][]string
  898. if err := ds.DataTo(&gotMap); err != nil {
  899. t.Fatal(err)
  900. }
  901. if _, ok := gotMap[path]; !ok {
  902. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  903. }
  904. want := []string{"a", "b"}
  905. for i, v := range gotMap[path] {
  906. if v != want[i] {
  907. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  908. }
  909. }
  910. }
  911. func TestIntegration_ArrayUnion_Update(t *testing.T) {
  912. doc := integrationColl(t).NewDoc()
  913. h := testHelper{t}
  914. path := "somepath"
  915. h.mustCreate(doc, map[string]interface{}{
  916. path: []string{"a", "b"},
  917. })
  918. fpus := []Update{
  919. {
  920. Path: path,
  921. Value: ArrayUnion("this should be added"),
  922. },
  923. }
  924. h.mustUpdate(doc, fpus)
  925. ds := h.mustGet(doc)
  926. var gotMap map[string][]string
  927. err := ds.DataTo(&gotMap)
  928. if err != nil {
  929. t.Fatal(err)
  930. }
  931. if _, ok := gotMap[path]; !ok {
  932. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  933. }
  934. want := []string{"a", "b", "this should be added"}
  935. for i, v := range gotMap[path] {
  936. if v != want[i] {
  937. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  938. }
  939. }
  940. }
  941. func TestIntegration_ArrayUnion_Set(t *testing.T) {
  942. coll := integrationColl(t)
  943. h := testHelper{t}
  944. path := "somepath"
  945. doc := coll.NewDoc()
  946. newData := map[string]interface{}{
  947. path: ArrayUnion("a", "b"),
  948. }
  949. h.mustSet(doc, newData)
  950. ds := h.mustGet(doc)
  951. var gotMap map[string][]string
  952. if err := ds.DataTo(&gotMap); err != nil {
  953. t.Fatal(err)
  954. }
  955. if _, ok := gotMap[path]; !ok {
  956. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  957. }
  958. want := []string{"a", "b"}
  959. for i, v := range gotMap[path] {
  960. if v != want[i] {
  961. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  962. }
  963. }
  964. }
  965. func TestIntegration_ArrayRemove_Create(t *testing.T) {
  966. doc := integrationColl(t).NewDoc()
  967. h := testHelper{t}
  968. path := "somepath"
  969. h.mustCreate(doc, map[string]interface{}{
  970. path: ArrayRemove("a", "b"),
  971. })
  972. ds := h.mustGet(doc)
  973. var gotMap map[string][]string
  974. err := ds.DataTo(&gotMap)
  975. if err != nil {
  976. t.Fatal(err)
  977. }
  978. if _, ok := gotMap[path]; !ok {
  979. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  980. }
  981. // A create with arrayRemove results in an empty array.
  982. want := []string(nil)
  983. if !testEqual(gotMap[path], want) {
  984. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  985. }
  986. }
  987. func TestIntegration_ArrayRemove_Update(t *testing.T) {
  988. doc := integrationColl(t).NewDoc()
  989. h := testHelper{t}
  990. path := "somepath"
  991. h.mustCreate(doc, map[string]interface{}{
  992. path: []string{"a", "this should be removed", "c"},
  993. })
  994. fpus := []Update{
  995. {
  996. Path: path,
  997. Value: ArrayRemove("this should be removed"),
  998. },
  999. }
  1000. h.mustUpdate(doc, fpus)
  1001. ds := h.mustGet(doc)
  1002. var gotMap map[string][]string
  1003. err := ds.DataTo(&gotMap)
  1004. if err != nil {
  1005. t.Fatal(err)
  1006. }
  1007. if _, ok := gotMap[path]; !ok {
  1008. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  1009. }
  1010. want := []string{"a", "c"}
  1011. for i, v := range gotMap[path] {
  1012. if v != want[i] {
  1013. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  1014. }
  1015. }
  1016. }
  1017. func TestIntegration_ArrayRemove_Set(t *testing.T) {
  1018. coll := integrationColl(t)
  1019. h := testHelper{t}
  1020. path := "somepath"
  1021. doc := coll.NewDoc()
  1022. newData := map[string]interface{}{
  1023. path: ArrayRemove("a", "b"),
  1024. }
  1025. h.mustSet(doc, newData)
  1026. ds := h.mustGet(doc)
  1027. var gotMap map[string][]string
  1028. if err := ds.DataTo(&gotMap); err != nil {
  1029. t.Fatal(err)
  1030. }
  1031. if _, ok := gotMap[path]; !ok {
  1032. t.Fatalf("expected a %v key in data, got %v", path, gotMap)
  1033. }
  1034. want := []string(nil)
  1035. if !testEqual(gotMap[path], want) {
  1036. t.Fatalf("got\n%#v\nwant\n%#v", gotMap[path], want)
  1037. }
  1038. }
  1039. type imap map[string]interface{}
  1040. func TestIntegration_WatchQuery(t *testing.T) {
  1041. ctx := context.Background()
  1042. coll := integrationColl(t)
  1043. h := testHelper{t}
  1044. q := coll.Where("e", ">", 1).OrderBy("e", Asc)
  1045. it := q.Snapshots(ctx)
  1046. defer it.Stop()
  1047. next := func() ([]*DocumentSnapshot, []DocumentChange) {
  1048. qsnap, err := it.Next()
  1049. if err != nil {
  1050. t.Fatal(err)
  1051. }
  1052. if qsnap.ReadTime.IsZero() {
  1053. t.Fatal("zero time")
  1054. }
  1055. ds, err := qsnap.Documents.GetAll()
  1056. if err != nil {
  1057. t.Fatal(err)
  1058. }
  1059. if qsnap.Size != len(ds) {
  1060. t.Fatalf("Size=%d but we have %d docs", qsnap.Size, len(ds))
  1061. }
  1062. return ds, qsnap.Changes
  1063. }
  1064. copts := append([]cmp.Option{cmpopts.IgnoreFields(DocumentSnapshot{}, "ReadTime")}, cmpOpts...)
  1065. check := func(msg string, wantd []*DocumentSnapshot, wantc []DocumentChange) {
  1066. gotd, gotc := next()
  1067. if diff := testutil.Diff(gotd, wantd, copts...); diff != "" {
  1068. t.Errorf("%s: %s", msg, diff)
  1069. }
  1070. if diff := testutil.Diff(gotc, wantc, copts...); diff != "" {
  1071. t.Errorf("%s: %s", msg, diff)
  1072. }
  1073. }
  1074. check("initial", nil, nil)
  1075. doc1 := coll.NewDoc()
  1076. h.mustCreate(doc1, imap{"e": int64(2), "b": "two"})
  1077. wds := h.mustGet(doc1)
  1078. check("one",
  1079. []*DocumentSnapshot{wds},
  1080. []DocumentChange{{Kind: DocumentAdded, Doc: wds, OldIndex: -1, NewIndex: 0}})
  1081. // Add a doc that does not match. We won't see a snapshot for this.
  1082. doc2 := coll.NewDoc()
  1083. h.mustCreate(doc2, imap{"e": int64(1)})
  1084. // Update the first doc. We should see the change. We won't see doc2.
  1085. h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(3)}})
  1086. wds = h.mustGet(doc1)
  1087. check("update",
  1088. []*DocumentSnapshot{wds},
  1089. []DocumentChange{{Kind: DocumentModified, Doc: wds, OldIndex: 0, NewIndex: 0}})
  1090. // Now update doc so that it is not in the query. We should see a snapshot with no docs.
  1091. h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(0)}})
  1092. check("update2", nil, []DocumentChange{{Kind: DocumentRemoved, Doc: wds, OldIndex: 0, NewIndex: -1}})
  1093. // Add two docs out of order. We should see them in order.
  1094. doc3 := coll.NewDoc()
  1095. doc4 := coll.NewDoc()
  1096. want3 := imap{"e": int64(5)}
  1097. want4 := imap{"e": int64(4)}
  1098. h.mustCreate(doc3, want3)
  1099. h.mustCreate(doc4, want4)
  1100. wds4 := h.mustGet(doc4)
  1101. wds3 := h.mustGet(doc3)
  1102. check("two#1",
  1103. []*DocumentSnapshot{wds3},
  1104. []DocumentChange{{Kind: DocumentAdded, Doc: wds3, OldIndex: -1, NewIndex: 0}})
  1105. check("two#2",
  1106. []*DocumentSnapshot{wds4, wds3},
  1107. []DocumentChange{{Kind: DocumentAdded, Doc: wds4, OldIndex: -1, NewIndex: 0}})
  1108. // Delete a doc.
  1109. h.mustDelete(doc4)
  1110. check("after del", []*DocumentSnapshot{wds3}, []DocumentChange{{Kind: DocumentRemoved, Doc: wds4, OldIndex: 0, NewIndex: -1}})
  1111. }
  1112. func TestIntegration_WatchQueryCancel(t *testing.T) {
  1113. ctx := context.Background()
  1114. coll := integrationColl(t)
  1115. q := coll.Where("e", ">", 1).OrderBy("e", Asc)
  1116. ctx, cancel := context.WithCancel(ctx)
  1117. it := q.Snapshots(ctx)
  1118. defer it.Stop()
  1119. // First call opens the stream.
  1120. _, err := it.Next()
  1121. if err != nil {
  1122. t.Fatal(err)
  1123. }
  1124. cancel()
  1125. _, err = it.Next()
  1126. codeEq(t, "after cancel", codes.Canceled, err)
  1127. }
  1128. func TestIntegration_MissingDocs(t *testing.T) {
  1129. ctx := context.Background()
  1130. h := testHelper{t}
  1131. client := integrationClient(t)
  1132. coll := client.Collection(collectionIDs.New())
  1133. dr1 := coll.NewDoc()
  1134. dr2 := coll.NewDoc()
  1135. dr3 := dr2.Collection("sub").NewDoc()
  1136. h.mustCreate(dr1, integrationTestMap)
  1137. defer h.mustDelete(dr1)
  1138. h.mustCreate(dr3, integrationTestMap)
  1139. defer h.mustDelete(dr3)
  1140. // dr1 is a document in coll. dr2 was never created, but there are documents in
  1141. // its sub-collections. It is "missing".
  1142. // The Collection.DocumentRefs method includes missing document refs.
  1143. want := []string{dr1.Path, dr2.Path}
  1144. drs, err := coll.DocumentRefs(ctx).GetAll()
  1145. if err != nil {
  1146. t.Fatal(err)
  1147. }
  1148. var got []string
  1149. for _, dr := range drs {
  1150. got = append(got, dr.Path)
  1151. }
  1152. sort.Strings(want)
  1153. sort.Strings(got)
  1154. if !testutil.Equal(got, want) {
  1155. t.Errorf("got %v, want %v", got, want)
  1156. }
  1157. }
  1158. func codeEq(t *testing.T, msg string, code codes.Code, err error) {
  1159. if grpc.Code(err) != code {
  1160. t.Fatalf("%s:\ngot <%v>\nwant code %s", msg, err, code)
  1161. }
  1162. }
  1163. func loc() string {
  1164. _, file, line, ok := runtime.Caller(2)
  1165. if !ok {
  1166. return "???"
  1167. }
  1168. return fmt.Sprintf("%s:%d", filepath.Base(file), line)
  1169. }
  1170. func copyMap(m map[string]interface{}) map[string]interface{} {
  1171. c := map[string]interface{}{}
  1172. for k, v := range m {
  1173. c[k] = v
  1174. }
  1175. return c
  1176. }
  1177. func checkTimeBetween(t *testing.T, got, low, high time.Time) {
  1178. // Allow slack for clock skew.
  1179. const slack = 4 * time.Second
  1180. low = low.Add(-slack)
  1181. high = high.Add(slack)
  1182. if got.Before(low) || got.After(high) {
  1183. t.Fatalf("got %s, not in [%s, %s]", got, low, high)
  1184. }
  1185. }
  1186. type testHelper struct {
  1187. t *testing.T
  1188. }
  1189. func (h testHelper) mustCreate(doc *DocumentRef, data interface{}) *WriteResult {
  1190. wr, err := doc.Create(context.Background(), data)
  1191. if err != nil {
  1192. h.t.Fatalf("%s: creating: %v", loc(), err)
  1193. }
  1194. return wr
  1195. }
  1196. func (h testHelper) mustUpdate(doc *DocumentRef, updates []Update) *WriteResult {
  1197. wr, err := doc.Update(context.Background(), updates)
  1198. if err != nil {
  1199. h.t.Fatalf("%s: updating: %v", loc(), err)
  1200. }
  1201. return wr
  1202. }
  1203. func (h testHelper) mustGet(doc *DocumentRef) *DocumentSnapshot {
  1204. d, err := doc.Get(context.Background())
  1205. if err != nil {
  1206. h.t.Fatalf("%s: getting: %v", loc(), err)
  1207. }
  1208. return d
  1209. }
  1210. func (h testHelper) mustDelete(doc *DocumentRef) *WriteResult {
  1211. wr, err := doc.Delete(context.Background())
  1212. if err != nil {
  1213. h.t.Fatalf("%s: updating: %v", loc(), err)
  1214. }
  1215. return wr
  1216. }
  1217. func (h testHelper) mustSet(doc *DocumentRef, data interface{}, opts ...SetOption) *WriteResult {
  1218. wr, err := doc.Set(context.Background(), data, opts...)
  1219. if err != nil {
  1220. h.t.Fatalf("%s: updating: %v", loc(), err)
  1221. }
  1222. return wr
  1223. }