|
14 | 14 |
|
15 | 15 | package com.google.firebase.firestore;
|
16 | 16 |
|
| 17 | +import static com.google.common.truth.Truth.assertWithMessage; |
17 | 18 | import static com.google.firebase.firestore.testutil.IntegrationTestUtil.isRunningAgainstEmulator;
|
18 | 19 | import static com.google.firebase.firestore.testutil.IntegrationTestUtil.nullList;
|
19 | 20 | import static com.google.firebase.firestore.testutil.IntegrationTestUtil.querySnapshotToIds;
|
|
39 | 40 | import com.google.firebase.firestore.testutil.EventAccumulator;
|
40 | 41 | import com.google.firebase.firestore.testutil.IntegrationTestUtil;
|
41 | 42 | import java.util.ArrayList;
|
| 43 | +import java.util.HashMap; |
| 44 | +import java.util.HashSet; |
42 | 45 | import java.util.LinkedHashMap;
|
43 | 46 | import java.util.List;
|
44 | 47 | import java.util.Map;
|
@@ -1030,6 +1033,73 @@ public void testMultipleUpdatesWhileOffline() {
|
1030 | 1033 | assertEquals(asList(map("foo", "zzyzx", "bar", "2")), querySnapshotToValues(snapshot2));
|
1031 | 1034 | }
|
1032 | 1035 |
|
| 1036 | + @Test |
| 1037 | + public void resumingAQueryShouldUseExistenceFilterToDetectDeletes() throws Exception { |
| 1038 | + // Prepare the names and contents of the 100 documents to create. |
| 1039 | + Map<String, Map<String, Object>> testData = new HashMap<>(); |
| 1040 | + for (int i = 0; i < 100; i++) { |
| 1041 | + testData.put("doc" + (1000 + i), map("key", 42)); |
| 1042 | + } |
| 1043 | + |
| 1044 | + // Create 100 documents in a new collection. |
| 1045 | + CollectionReference collection = testCollectionWithDocs(testData); |
| 1046 | + |
| 1047 | + // Run a query to populate the local cache with the 100 documents and a resume token. |
| 1048 | + List<DocumentReference> createdDocuments = new ArrayList<>(); |
| 1049 | + { |
| 1050 | + QuerySnapshot querySnapshot = waitFor(collection.get()); |
| 1051 | + assertWithMessage("querySnapshot1").that(querySnapshot.size()).isEqualTo(100); |
| 1052 | + for (DocumentSnapshot documentSnapshot : querySnapshot.getDocuments()) { |
| 1053 | + createdDocuments.add(documentSnapshot.getReference()); |
| 1054 | + } |
| 1055 | + } |
| 1056 | + |
| 1057 | + // Delete 50 of the 100 documents. Do this in a transaction, rather than |
| 1058 | + // DocumentReference.delete(), to avoid affecting the local cache. |
| 1059 | + HashSet<String> deletedDocumentIds = new HashSet<>(); |
| 1060 | + waitFor( |
| 1061 | + collection |
| 1062 | + .getFirestore() |
| 1063 | + .runTransaction( |
| 1064 | + transaction -> { |
| 1065 | + for (int i = 0; i < createdDocuments.size(); i += 2) { |
| 1066 | + DocumentReference documentToDelete = createdDocuments.get(i); |
| 1067 | + transaction.delete(documentToDelete); |
| 1068 | + deletedDocumentIds.add(documentToDelete.getId()); |
| 1069 | + } |
| 1070 | + return null; |
| 1071 | + })); |
| 1072 | + |
| 1073 | + // Wait for 10 seconds, during which Watch will stop tracking the query and will send an |
| 1074 | + // existence filter rather than "delete" events when the query is resumed. |
| 1075 | + Thread.sleep(10000); |
| 1076 | + |
| 1077 | + // Resume the query and save the resulting snapshot for verification. |
| 1078 | + QuerySnapshot snapshot2 = waitFor(collection.get()); |
| 1079 | + |
| 1080 | + // Verify that the snapshot from the resumed query contains the expected documents; that is, |
| 1081 | + // that it contains the 50 documents that were _not_ deleted. |
| 1082 | + // TODO(b/270731363): Remove the "if" condition below once the Firestore Emulator is fixed to |
| 1083 | + // send an existence filter. At the time of writing, the Firestore emulator fails to send an |
| 1084 | + // existence filter, resulting in the client including the deleted documents in the snapshot |
| 1085 | + // of the resumed query. |
| 1086 | + if (!(isRunningAgainstEmulator() && snapshot2.size() == 100)) { |
| 1087 | + HashSet<String> actualDocumentIds = new HashSet<>(); |
| 1088 | + for (DocumentSnapshot documentSnapshot : snapshot2.getDocuments()) { |
| 1089 | + actualDocumentIds.add(documentSnapshot.getId()); |
| 1090 | + } |
| 1091 | + HashSet<String> expectedDocumentIds = new HashSet<>(); |
| 1092 | + for (DocumentReference documentRef : createdDocuments) { |
| 1093 | + if (!deletedDocumentIds.contains(documentRef.getId())) { |
| 1094 | + expectedDocumentIds.add(documentRef.getId()); |
| 1095 | + } |
| 1096 | + } |
| 1097 | + assertWithMessage("snapshot2.docs") |
| 1098 | + .that(actualDocumentIds) |
| 1099 | + .containsExactlyElementsIn(expectedDocumentIds); |
| 1100 | + } |
| 1101 | + } |
| 1102 | + |
1033 | 1103 | @Test
|
1034 | 1104 | public void testOrQueries() {
|
1035 | 1105 | Map<String, Map<String, Object>> testDocs =
|
|
0 commit comments