Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

1147 рядки
31 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. "errors"
  17. "flag"
  18. "fmt"
  19. "log"
  20. "math"
  21. "os"
  22. "path/filepath"
  23. "runtime"
  24. "sort"
  25. "testing"
  26. "time"
  27. "cloud.google.com/go/internal/pretty"
  28. "cloud.google.com/go/internal/testutil"
  29. "cloud.google.com/go/internal/uid"
  30. "github.com/google/go-cmp/cmp"
  31. "github.com/google/go-cmp/cmp/cmpopts"
  32. "golang.org/x/net/context"
  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. ctx := context.Background()
  295. h := testHelper{t}
  296. // Set Should be able to create a new doc.
  297. doc := coll.NewDoc()
  298. wr1, err := doc.Set(ctx, integrationTestMap)
  299. if err != nil {
  300. t.Fatal(err)
  301. }
  302. // Calling Set on the doc completely replaces the contents.
  303. // The update time should increase.
  304. newData := map[string]interface{}{
  305. "str": "change",
  306. "x": "1",
  307. }
  308. wr2, err := doc.Set(ctx, newData)
  309. if err != nil {
  310. t.Fatal(err)
  311. }
  312. if !wr1.UpdateTime.Before(wr2.UpdateTime) {
  313. t.Errorf("update time did not increase: old=%s, new=%s", wr1.UpdateTime, wr2.UpdateTime)
  314. }
  315. ds := h.mustGet(doc)
  316. if got := ds.Data(); !testEqual(got, newData) {
  317. t.Errorf("got %v, want %v", got, newData)
  318. }
  319. newData = map[string]interface{}{
  320. "str": "1",
  321. "x": "2",
  322. "y": "3",
  323. }
  324. // SetOptions:
  325. // Only fields mentioned in the Merge option will be changed.
  326. // In this case, "str" will not be changed to "1".
  327. wr3, err := doc.Set(ctx, newData, Merge([]string{"x"}, []string{"y"}))
  328. if err != nil {
  329. t.Fatal(err)
  330. }
  331. ds = h.mustGet(doc)
  332. want := map[string]interface{}{
  333. "str": "change",
  334. "x": "2",
  335. "y": "3",
  336. }
  337. if got := ds.Data(); !testEqual(got, want) {
  338. t.Errorf("got %v, want %v", got, want)
  339. }
  340. if !wr2.UpdateTime.Before(wr3.UpdateTime) {
  341. t.Errorf("update time did not increase: old=%s, new=%s", wr2.UpdateTime, wr3.UpdateTime)
  342. }
  343. // Another way to change only x and y is to pass a map with only
  344. // those keys, and use MergeAll.
  345. wr4, err := doc.Set(ctx, map[string]interface{}{"x": "4", "y": "5"}, MergeAll)
  346. if err != nil {
  347. t.Fatal(err)
  348. }
  349. ds = h.mustGet(doc)
  350. want = map[string]interface{}{
  351. "str": "change",
  352. "x": "4",
  353. "y": "5",
  354. }
  355. if got := ds.Data(); !testEqual(got, want) {
  356. t.Errorf("got %v, want %v", got, want)
  357. }
  358. if !wr3.UpdateTime.Before(wr4.UpdateTime) {
  359. t.Errorf("update time did not increase: old=%s, new=%s", wr3.UpdateTime, wr4.UpdateTime)
  360. }
  361. // Writing an empty doc with MergeAll should create the doc.
  362. doc2 := coll.NewDoc()
  363. want = map[string]interface{}{}
  364. _, err = doc2.Set(ctx, want, MergeAll)
  365. if err != nil {
  366. t.Fatal(err)
  367. }
  368. ds = h.mustGet(doc2)
  369. if got := ds.Data(); !testEqual(got, want) {
  370. t.Errorf("got %v, want %v", got, want)
  371. }
  372. }
  373. func TestIntegration_Delete(t *testing.T) {
  374. ctx := context.Background()
  375. doc := integrationColl(t).NewDoc()
  376. h := testHelper{t}
  377. h.mustCreate(doc, integrationTestMap)
  378. wr := h.mustDelete(doc)
  379. // Confirm that doc doesn't exist.
  380. if _, err := doc.Get(ctx); grpc.Code(err) != codes.NotFound {
  381. t.Fatalf("got error <%v>, want NotFound", err)
  382. }
  383. er := func(_ *WriteResult, err error) error { return err }
  384. codeEq(t, "Delete on a missing doc", codes.OK,
  385. er(doc.Delete(ctx)))
  386. // TODO(jba): confirm that the server should return InvalidArgument instead of
  387. // FailedPrecondition.
  388. wr = h.mustCreate(doc, integrationTestMap)
  389. codeEq(t, "Delete with wrong LastUpdateTime", codes.FailedPrecondition,
  390. er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
  391. codeEq(t, "Delete with right LastUpdateTime", codes.OK,
  392. er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime))))
  393. }
  394. func TestIntegration_Update(t *testing.T) {
  395. ctx := context.Background()
  396. doc := integrationColl(t).NewDoc()
  397. h := testHelper{t}
  398. h.mustCreate(doc, integrationTestMap)
  399. fpus := []Update{
  400. {Path: "bool", Value: false},
  401. {Path: "time", Value: 17},
  402. {FieldPath: []string{"*", "`"}, Value: 18},
  403. {Path: "null", Value: Delete},
  404. {Path: "noSuchField", Value: Delete}, // deleting a non-existent field is a no-op
  405. }
  406. wr := h.mustUpdate(doc, fpus)
  407. ds := h.mustGet(doc)
  408. got := ds.Data()
  409. want := copyMap(wantIntegrationTestMap)
  410. want["bool"] = false
  411. want["time"] = int64(17)
  412. want["*"] = map[string]interface{}{"`": int64(18)}
  413. delete(want, "null")
  414. if !testEqual(got, want) {
  415. t.Errorf("got\n%#v\nwant\n%#v", got, want)
  416. }
  417. er := func(_ *WriteResult, err error) error { return err }
  418. codeEq(t, "Update on missing doc", codes.NotFound,
  419. er(integrationColl(t).NewDoc().Update(ctx, fpus)))
  420. codeEq(t, "Update with wrong LastUpdateTime", codes.FailedPrecondition,
  421. er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
  422. codeEq(t, "Update with right LastUpdateTime", codes.OK,
  423. er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime))))
  424. }
  425. func TestIntegration_Collections(t *testing.T) {
  426. ctx := context.Background()
  427. c := integrationClient(t)
  428. h := testHelper{t}
  429. got, err := c.Collections(ctx).GetAll()
  430. if err != nil {
  431. t.Fatal(err)
  432. }
  433. // There should be at least one collection.
  434. if len(got) == 0 {
  435. t.Error("got 0 top-level collections, want at least one")
  436. }
  437. doc := integrationColl(t).NewDoc()
  438. got, err = doc.Collections(ctx).GetAll()
  439. if err != nil {
  440. t.Fatal(err)
  441. }
  442. if len(got) != 0 {
  443. t.Errorf("got %d collections, want 0", len(got))
  444. }
  445. var want []*CollectionRef
  446. for i := 0; i < 3; i++ {
  447. id := collectionIDs.New()
  448. cr := doc.Collection(id)
  449. want = append(want, cr)
  450. h.mustCreate(cr.NewDoc(), integrationTestMap)
  451. }
  452. got, err = doc.Collections(ctx).GetAll()
  453. if err != nil {
  454. t.Fatal(err)
  455. }
  456. if !testEqual(got, want) {
  457. t.Errorf("got\n%#v\nwant\n%#v", got, want)
  458. }
  459. }
  460. func TestIntegration_ServerTimestamp(t *testing.T) {
  461. type S struct {
  462. A int
  463. B time.Time
  464. C time.Time `firestore:"C.C,serverTimestamp"`
  465. D map[string]interface{}
  466. E time.Time `firestore:",omitempty,serverTimestamp"`
  467. }
  468. data := S{
  469. A: 1,
  470. B: aTime,
  471. // C is unset, so will get the server timestamp.
  472. D: map[string]interface{}{"x": ServerTimestamp},
  473. // E is unset, so will get the server timestamp.
  474. }
  475. h := testHelper{t}
  476. doc := integrationColl(t).NewDoc()
  477. // Bound times of the RPC, with some slack for clock skew.
  478. start := time.Now()
  479. h.mustCreate(doc, data)
  480. end := time.Now()
  481. ds := h.mustGet(doc)
  482. var got S
  483. if err := ds.DataTo(&got); err != nil {
  484. t.Fatal(err)
  485. }
  486. if !testEqual(got.B, aTime) {
  487. t.Errorf("B: got %s, want %s", got.B, aTime)
  488. }
  489. checkTimeBetween(t, got.C, start, end)
  490. if g, w := got.D["x"], got.C; !testEqual(g, w) {
  491. t.Errorf(`D["x"] = %s, want equal to C (%s)`, g, w)
  492. }
  493. if g, w := got.E, got.C; !testEqual(g, w) {
  494. t.Errorf(`E = %s, want equal to C (%s)`, g, w)
  495. }
  496. }
  497. func TestIntegration_MergeServerTimestamp(t *testing.T) {
  498. ctx := context.Background()
  499. doc := integrationColl(t).NewDoc()
  500. h := testHelper{t}
  501. // Create a doc with an ordinary field "a" and a ServerTimestamp field "b".
  502. _, err := doc.Set(ctx, map[string]interface{}{
  503. "a": 1,
  504. "b": ServerTimestamp})
  505. if err != nil {
  506. t.Fatal(err)
  507. }
  508. docSnap := h.mustGet(doc)
  509. data1 := docSnap.Data()
  510. // Merge with a document with a different value of "a". However,
  511. // specify only "b" in the list of merge fields.
  512. _, err = doc.Set(ctx,
  513. map[string]interface{}{"a": 2, "b": ServerTimestamp},
  514. Merge([]string{"b"}))
  515. if err != nil {
  516. t.Fatal(err)
  517. }
  518. // The result should leave "a" unchanged, while "b" is updated.
  519. docSnap = h.mustGet(doc)
  520. data2 := docSnap.Data()
  521. if got, want := data2["a"], data1["a"]; got != want {
  522. t.Errorf("got %v, want %v", got, want)
  523. }
  524. t1 := data1["b"].(time.Time)
  525. t2 := data2["b"].(time.Time)
  526. if !t1.Before(t2) {
  527. t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2)
  528. }
  529. }
  530. func TestIntegration_MergeNestedServerTimestamp(t *testing.T) {
  531. ctx := context.Background()
  532. doc := integrationColl(t).NewDoc()
  533. h := testHelper{t}
  534. // Create a doc with an ordinary field "a" a ServerTimestamp field "b",
  535. // and a second ServerTimestamp field "c.d".
  536. _, err := doc.Set(ctx, map[string]interface{}{
  537. "a": 1,
  538. "b": ServerTimestamp,
  539. "c": map[string]interface{}{"d": ServerTimestamp},
  540. })
  541. if err != nil {
  542. t.Fatal(err)
  543. }
  544. data1 := h.mustGet(doc).Data()
  545. // Merge with a document with a different value of "a". However,
  546. // specify only "c.d" in the list of merge fields.
  547. _, err = doc.Set(ctx,
  548. map[string]interface{}{
  549. "a": 2,
  550. "b": ServerTimestamp,
  551. "c": map[string]interface{}{"d": ServerTimestamp},
  552. },
  553. Merge([]string{"c", "d"}))
  554. if err != nil {
  555. t.Fatal(err)
  556. }
  557. // The result should leave "a" and "b" unchanged, while "c.d" is updated.
  558. data2 := h.mustGet(doc).Data()
  559. if got, want := data2["a"], data1["a"]; got != want {
  560. t.Errorf("a: got %v, want %v", got, want)
  561. }
  562. want := data1["b"].(time.Time)
  563. got := data2["b"].(time.Time)
  564. if !got.Equal(want) {
  565. t.Errorf("b: got %s, want %s", got, want)
  566. }
  567. t1 := data1["c"].(map[string]interface{})["d"].(time.Time)
  568. t2 := data2["c"].(map[string]interface{})["d"].(time.Time)
  569. if !t1.Before(t2) {
  570. t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2)
  571. }
  572. }
  573. func TestIntegration_WriteBatch(t *testing.T) {
  574. ctx := context.Background()
  575. b := integrationClient(t).Batch()
  576. h := testHelper{t}
  577. doc1 := iColl.NewDoc()
  578. doc2 := iColl.NewDoc()
  579. b.Create(doc1, integrationTestMap)
  580. b.Set(doc2, integrationTestMap)
  581. b.Update(doc1, []Update{{Path: "bool", Value: false}})
  582. b.Update(doc1, []Update{{Path: "str", Value: Delete}})
  583. wrs, err := b.Commit(ctx)
  584. if err != nil {
  585. t.Fatal(err)
  586. }
  587. if got, want := len(wrs), 4; got != want {
  588. t.Fatalf("got %d WriteResults, want %d", got, want)
  589. }
  590. got1 := h.mustGet(doc1).Data()
  591. want := copyMap(wantIntegrationTestMap)
  592. want["bool"] = false
  593. delete(want, "str")
  594. if !testEqual(got1, want) {
  595. t.Errorf("got\n%#v\nwant\n%#v", got1, want)
  596. }
  597. got2 := h.mustGet(doc2).Data()
  598. if !testEqual(got2, wantIntegrationTestMap) {
  599. t.Errorf("got\n%#v\nwant\n%#v", got2, wantIntegrationTestMap)
  600. }
  601. // TODO(jba): test two updates to the same document when it is supported.
  602. // TODO(jba): test verify when it is supported.
  603. }
  604. func TestIntegration_Query(t *testing.T) {
  605. ctx := context.Background()
  606. coll := integrationColl(t)
  607. h := testHelper{t}
  608. var docs []*DocumentRef
  609. var wants []map[string]interface{}
  610. for i := 0; i < 3; i++ {
  611. doc := coll.NewDoc()
  612. docs = append(docs, doc)
  613. // To support running this test in parallel with the others, use a field name
  614. // that we don't use anywhere else.
  615. h.mustCreate(doc, map[string]interface{}{"q": i, "x": 1})
  616. wants = append(wants, map[string]interface{}{"q": int64(i)})
  617. }
  618. q := coll.Select("q").OrderBy("q", Asc)
  619. for i, test := range []struct {
  620. q Query
  621. want []map[string]interface{}
  622. }{
  623. {q, wants},
  624. {q.Where("q", ">", 1), wants[2:]},
  625. {q.WherePath([]string{"q"}, ">", 1), wants[2:]},
  626. {q.Offset(1).Limit(1), wants[1:2]},
  627. {q.StartAt(1), wants[1:]},
  628. {q.StartAfter(1), wants[2:]},
  629. {q.EndAt(1), wants[:2]},
  630. {q.EndBefore(1), wants[:1]},
  631. } {
  632. gotDocs, err := test.q.Documents(ctx).GetAll()
  633. if err != nil {
  634. t.Errorf("#%d: %+v: %v", i, test.q, err)
  635. continue
  636. }
  637. if len(gotDocs) != len(test.want) {
  638. t.Errorf("#%d: %+v: got %d docs, want %d", i, test.q, len(gotDocs), len(test.want))
  639. continue
  640. }
  641. for j, g := range gotDocs {
  642. if got, want := g.Data(), test.want[j]; !testEqual(got, want) {
  643. t.Errorf("#%d: %+v, #%d: got\n%+v\nwant\n%+v", i, test.q, j, got, want)
  644. }
  645. }
  646. }
  647. _, err := coll.Select("q").Where("x", "==", 1).OrderBy("q", Asc).Documents(ctx).GetAll()
  648. codeEq(t, "Where and OrderBy on different fields without an index", codes.FailedPrecondition, err)
  649. // Using the collection itself as the query should return the full documents.
  650. allDocs, err := coll.Documents(ctx).GetAll()
  651. if err != nil {
  652. t.Fatal(err)
  653. }
  654. seen := map[int64]bool{} // "q" values we see
  655. for _, d := range allDocs {
  656. data := d.Data()
  657. q, ok := data["q"]
  658. if !ok {
  659. // A document from another test.
  660. continue
  661. }
  662. if seen[q.(int64)] {
  663. t.Errorf("%v: duplicate doc", data)
  664. }
  665. seen[q.(int64)] = true
  666. if data["x"] != int64(1) {
  667. t.Errorf("%v: wrong or missing 'x'", data)
  668. }
  669. if len(data) != 2 {
  670. t.Errorf("%v: want two keys", data)
  671. }
  672. }
  673. if got, want := len(seen), len(wants); got != want {
  674. t.Errorf("got %d docs with 'q', want %d", len(seen), len(wants))
  675. }
  676. }
  677. // Test unary filters.
  678. func TestIntegration_QueryUnary(t *testing.T) {
  679. ctx := context.Background()
  680. coll := integrationColl(t)
  681. h := testHelper{t}
  682. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": "a"})
  683. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": nil})
  684. h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": math.NaN()})
  685. wantNull := map[string]interface{}{"q": nil}
  686. wantNaN := map[string]interface{}{"q": math.NaN()}
  687. base := coll.Select("q").Where("x", "==", 2)
  688. for _, test := range []struct {
  689. q Query
  690. want map[string]interface{}
  691. }{
  692. {base.Where("q", "==", nil), wantNull},
  693. {base.Where("q", "==", math.NaN()), wantNaN},
  694. } {
  695. got, err := test.q.Documents(ctx).GetAll()
  696. if err != nil {
  697. t.Fatal(err)
  698. }
  699. if len(got) != 1 {
  700. t.Errorf("got %d responses, want 1", len(got))
  701. continue
  702. }
  703. if g, w := got[0].Data(), test.want; !testEqual(g, w) {
  704. t.Errorf("%v: got %v, want %v", test.q, g, w)
  705. }
  706. }
  707. }
  708. // Test the special DocumentID field in queries.
  709. func TestIntegration_QueryName(t *testing.T) {
  710. ctx := context.Background()
  711. h := testHelper{t}
  712. checkIDs := func(q Query, wantIDs []string) {
  713. gots, err := q.Documents(ctx).GetAll()
  714. if err != nil {
  715. t.Fatal(err)
  716. }
  717. if len(gots) != len(wantIDs) {
  718. t.Fatalf("got %d, want %d", len(gots), len(wantIDs))
  719. }
  720. for i, g := range gots {
  721. if got, want := g.Ref.ID, wantIDs[i]; got != want {
  722. t.Errorf("#%d: got %s, want %s", i, got, want)
  723. }
  724. }
  725. }
  726. coll := integrationColl(t)
  727. var wantIDs []string
  728. for i := 0; i < 3; i++ {
  729. doc := coll.NewDoc()
  730. h.mustCreate(doc, map[string]interface{}{"nm": 1})
  731. wantIDs = append(wantIDs, doc.ID)
  732. }
  733. sort.Strings(wantIDs)
  734. q := coll.Where("nm", "==", 1).OrderBy(DocumentID, Asc)
  735. checkIDs(q, wantIDs)
  736. // Empty Select.
  737. q = coll.Select().Where("nm", "==", 1).OrderBy(DocumentID, Asc)
  738. checkIDs(q, wantIDs)
  739. // Test cursors with __name__.
  740. checkIDs(q.StartAt(wantIDs[1]), wantIDs[1:])
  741. checkIDs(q.EndAt(wantIDs[1]), wantIDs[:2])
  742. }
  743. func TestIntegration_QueryNested(t *testing.T) {
  744. ctx := context.Background()
  745. h := testHelper{t}
  746. coll1 := integrationColl(t)
  747. doc1 := coll1.NewDoc()
  748. coll2 := doc1.Collection(collectionIDs.New())
  749. doc2 := coll2.NewDoc()
  750. wantData := map[string]interface{}{"x": int64(1)}
  751. h.mustCreate(doc2, wantData)
  752. q := coll2.Select("x")
  753. got, err := q.Documents(ctx).GetAll()
  754. if err != nil {
  755. t.Fatal(err)
  756. }
  757. if len(got) != 1 {
  758. t.Fatalf("got %d docs, want 1", len(got))
  759. }
  760. if gotData := got[0].Data(); !testEqual(gotData, wantData) {
  761. t.Errorf("got\n%+v\nwant\n%+v", gotData, wantData)
  762. }
  763. }
  764. func TestIntegration_RunTransaction(t *testing.T) {
  765. ctx := context.Background()
  766. h := testHelper{t}
  767. type Player struct {
  768. Name string
  769. Score int
  770. Star bool `firestore:"*"`
  771. }
  772. pat := Player{Name: "Pat", Score: 3, Star: false}
  773. client := integrationClient(t)
  774. patDoc := iColl.Doc("pat")
  775. var anError error
  776. incPat := func(_ context.Context, tx *Transaction) error {
  777. doc, err := tx.Get(patDoc)
  778. if err != nil {
  779. return err
  780. }
  781. score, err := doc.DataAt("Score")
  782. if err != nil {
  783. return err
  784. }
  785. // Since the Star field is called "*", we must use DataAtPath to get it.
  786. star, err := doc.DataAtPath([]string{"*"})
  787. if err != nil {
  788. return err
  789. }
  790. err = tx.Update(patDoc, []Update{{Path: "Score", Value: int(score.(int64) + 7)}})
  791. if err != nil {
  792. return err
  793. }
  794. // Since the Star field is called "*", we must use Update to change it.
  795. err = tx.Update(patDoc,
  796. []Update{{FieldPath: []string{"*"}, Value: !star.(bool)}})
  797. if err != nil {
  798. return err
  799. }
  800. return anError
  801. }
  802. h.mustCreate(patDoc, pat)
  803. err := client.RunTransaction(ctx, incPat)
  804. if err != nil {
  805. t.Fatal(err)
  806. }
  807. ds := h.mustGet(patDoc)
  808. var got Player
  809. if err := ds.DataTo(&got); err != nil {
  810. t.Fatal(err)
  811. }
  812. want := Player{Name: "Pat", Score: 10, Star: true}
  813. if got != want {
  814. t.Errorf("got %+v, want %+v", got, want)
  815. }
  816. // Function returns error, so transaction is rolled back and no writes happen.
  817. anError = errors.New("bad")
  818. err = client.RunTransaction(ctx, incPat)
  819. if err != anError {
  820. t.Fatalf("got %v, want %v", err, anError)
  821. }
  822. if err := ds.DataTo(&got); err != nil {
  823. t.Fatal(err)
  824. }
  825. // want is same as before.
  826. if got != want {
  827. t.Errorf("got %+v, want %+v", got, want)
  828. }
  829. }
  830. func TestIntegration_TransactionGetAll(t *testing.T) {
  831. ctx := context.Background()
  832. h := testHelper{t}
  833. type Player struct {
  834. Name string
  835. Score int
  836. }
  837. lee := Player{Name: "Lee", Score: 3}
  838. sam := Player{Name: "Sam", Score: 1}
  839. client := integrationClient(t)
  840. leeDoc := iColl.Doc("lee")
  841. samDoc := iColl.Doc("sam")
  842. h.mustCreate(leeDoc, lee)
  843. h.mustCreate(samDoc, sam)
  844. err := client.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error {
  845. docs, err := tx.GetAll([]*DocumentRef{samDoc, leeDoc})
  846. if err != nil {
  847. return err
  848. }
  849. for i, want := range []Player{sam, lee} {
  850. var got Player
  851. if err := docs[i].DataTo(&got); err != nil {
  852. return err
  853. }
  854. if !testutil.Equal(got, want) {
  855. return fmt.Errorf("got %+v, want %+v", got, want)
  856. }
  857. }
  858. return nil
  859. })
  860. if err != nil {
  861. t.Fatal(err)
  862. }
  863. }
  864. func TestIntegration_WatchDocument(t *testing.T) {
  865. coll := integrationColl(t)
  866. ctx := context.Background()
  867. h := testHelper{t}
  868. doc := coll.NewDoc()
  869. it := doc.Snapshots(ctx)
  870. defer it.Stop()
  871. next := func() *DocumentSnapshot {
  872. snap, err := it.Next()
  873. if err != nil {
  874. t.Fatal(err)
  875. }
  876. return snap
  877. }
  878. snap := next()
  879. if snap.Exists() {
  880. t.Fatal("snapshot exists; it should not")
  881. }
  882. want := map[string]interface{}{"a": int64(1), "b": "two"}
  883. h.mustCreate(doc, want)
  884. snap = next()
  885. if got := snap.Data(); !testutil.Equal(got, want) {
  886. t.Fatalf("got %v, want %v", got, want)
  887. }
  888. h.mustUpdate(doc, []Update{{Path: "a", Value: int64(2)}})
  889. want["a"] = int64(2)
  890. snap = next()
  891. if got := snap.Data(); !testutil.Equal(got, want) {
  892. t.Fatalf("got %v, want %v", got, want)
  893. }
  894. h.mustDelete(doc)
  895. snap = next()
  896. if snap.Exists() {
  897. t.Fatal("snapshot exists; it should not")
  898. }
  899. h.mustCreate(doc, want)
  900. snap = next()
  901. if got := snap.Data(); !testutil.Equal(got, want) {
  902. t.Fatalf("got %v, want %v", got, want)
  903. }
  904. }
  905. type imap map[string]interface{}
  906. func TestIntegration_WatchQuery(t *testing.T) {
  907. ctx := context.Background()
  908. coll := integrationColl(t)
  909. h := testHelper{t}
  910. q := coll.Where("e", ">", 1).OrderBy("e", Asc)
  911. it := q.Snapshots(ctx)
  912. defer it.Stop()
  913. next := func() ([]*DocumentSnapshot, []DocumentChange) {
  914. diter, err := it.Next()
  915. if err != nil {
  916. t.Fatal(err)
  917. }
  918. if it.ReadTime.IsZero() {
  919. t.Fatal("zero time")
  920. }
  921. ds, err := diter.GetAll()
  922. if err != nil {
  923. t.Fatal(err)
  924. }
  925. if it.Size != len(ds) {
  926. t.Fatalf("Size=%d but we have %d docs", it.Size, len(ds))
  927. }
  928. return ds, it.Changes
  929. }
  930. copts := append([]cmp.Option{cmpopts.IgnoreFields(DocumentSnapshot{}, "ReadTime")}, cmpOpts...)
  931. check := func(msg string, wantd []*DocumentSnapshot, wantc []DocumentChange) {
  932. gotd, gotc := next()
  933. if diff := testutil.Diff(gotd, wantd, copts...); diff != "" {
  934. t.Errorf("%s: %s", msg, diff)
  935. }
  936. if diff := testutil.Diff(gotc, wantc, copts...); diff != "" {
  937. t.Errorf("%s: %s", msg, diff)
  938. }
  939. }
  940. check("initial", nil, nil)
  941. doc1 := coll.NewDoc()
  942. h.mustCreate(doc1, imap{"e": int64(2), "b": "two"})
  943. wds := h.mustGet(doc1)
  944. check("one",
  945. []*DocumentSnapshot{wds},
  946. []DocumentChange{{Kind: DocumentAdded, Doc: wds, OldIndex: -1, NewIndex: 0}})
  947. // Add a doc that does not match. We won't see a snapshot for this.
  948. doc2 := coll.NewDoc()
  949. h.mustCreate(doc2, imap{"e": int64(1)})
  950. // Update the first doc. We should see the change. We won't see doc2.
  951. h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(3)}})
  952. wds = h.mustGet(doc1)
  953. check("update",
  954. []*DocumentSnapshot{wds},
  955. []DocumentChange{{Kind: DocumentModified, Doc: wds, OldIndex: 0, NewIndex: 0}})
  956. // Now update doc so that it is not in the query. We should see a snapshot with no docs.
  957. h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(0)}})
  958. check("update2", nil, []DocumentChange{{Kind: DocumentRemoved, Doc: wds, OldIndex: 0, NewIndex: -1}})
  959. // Add two docs out of order. We should see them in order.
  960. doc3 := coll.NewDoc()
  961. doc4 := coll.NewDoc()
  962. want3 := imap{"e": int64(5)}
  963. want4 := imap{"e": int64(4)}
  964. h.mustCreate(doc3, want3)
  965. h.mustCreate(doc4, want4)
  966. wds4 := h.mustGet(doc4)
  967. wds3 := h.mustGet(doc3)
  968. check("two#1",
  969. []*DocumentSnapshot{wds3},
  970. []DocumentChange{{Kind: DocumentAdded, Doc: wds3, OldIndex: -1, NewIndex: 0}})
  971. check("two#2",
  972. []*DocumentSnapshot{wds4, wds3},
  973. []DocumentChange{{Kind: DocumentAdded, Doc: wds4, OldIndex: -1, NewIndex: 0}})
  974. // Delete a doc.
  975. h.mustDelete(doc4)
  976. check("after del", []*DocumentSnapshot{wds3}, []DocumentChange{{Kind: DocumentRemoved, Doc: wds4, OldIndex: 0, NewIndex: -1}})
  977. }
  978. func TestIntegration_WatchQueryCancel(t *testing.T) {
  979. ctx := context.Background()
  980. coll := integrationColl(t)
  981. q := coll.Where("e", ">", 1).OrderBy("e", Asc)
  982. ctx, cancel := context.WithCancel(ctx)
  983. it := q.Snapshots(ctx)
  984. defer it.Stop()
  985. // First call opens the stream.
  986. _, err := it.Next()
  987. if err != nil {
  988. t.Fatal(err)
  989. }
  990. cancel()
  991. _, err = it.Next()
  992. codeEq(t, "after cancel", codes.Canceled, err)
  993. }
  994. func codeEq(t *testing.T, msg string, code codes.Code, err error) {
  995. if grpc.Code(err) != code {
  996. t.Fatalf("%s:\ngot <%v>\nwant code %s", msg, err, code)
  997. }
  998. }
  999. func loc() string {
  1000. _, file, line, ok := runtime.Caller(2)
  1001. if !ok {
  1002. return "???"
  1003. }
  1004. return fmt.Sprintf("%s:%d", filepath.Base(file), line)
  1005. }
  1006. func copyMap(m map[string]interface{}) map[string]interface{} {
  1007. c := map[string]interface{}{}
  1008. for k, v := range m {
  1009. c[k] = v
  1010. }
  1011. return c
  1012. }
  1013. func checkTimeBetween(t *testing.T, got, low, high time.Time) {
  1014. // Allow slack for clock skew.
  1015. const slack = 4 * time.Second
  1016. low = low.Add(-slack)
  1017. high = high.Add(slack)
  1018. if got.Before(low) || got.After(high) {
  1019. t.Fatalf("got %s, not in [%s, %s]", got, low, high)
  1020. }
  1021. }
  1022. type testHelper struct {
  1023. t *testing.T
  1024. }
  1025. func (h testHelper) mustCreate(doc *DocumentRef, data interface{}) *WriteResult {
  1026. wr, err := doc.Create(context.Background(), data)
  1027. if err != nil {
  1028. h.t.Fatalf("%s: creating: %v", loc(), err)
  1029. }
  1030. return wr
  1031. }
  1032. func (h testHelper) mustUpdate(doc *DocumentRef, updates []Update) *WriteResult {
  1033. wr, err := doc.Update(context.Background(), updates)
  1034. if err != nil {
  1035. h.t.Fatalf("%s: updating: %v", loc(), err)
  1036. }
  1037. return wr
  1038. }
  1039. func (h testHelper) mustGet(doc *DocumentRef) *DocumentSnapshot {
  1040. d, err := doc.Get(context.Background())
  1041. if err != nil {
  1042. h.t.Fatalf("%s: getting: %v", loc(), err)
  1043. }
  1044. return d
  1045. }
  1046. func (h testHelper) mustDelete(doc *DocumentRef) *WriteResult {
  1047. wr, err := doc.Delete(context.Background())
  1048. if err != nil {
  1049. h.t.Fatalf("%s: updating: %v", loc(), err)
  1050. }
  1051. return wr
  1052. }