FIRQueryTests.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <FirebaseFirestore/FirebaseFirestore.h>
  17. #import <XCTest/XCTest.h>
  18. #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
  19. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  20. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  21. #import "Firestore/Source/Core/FSTFirestoreClient.h"
  22. @interface FIRQueryTests : FSTIntegrationTestCase
  23. @end
  24. @implementation FIRQueryTests
  25. - (void)testLimitQueries {
  26. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  27. @"a" : @{@"k" : @"a"},
  28. @"b" : @{@"k" : @"b"},
  29. @"c" : @{@"k" : @"c"}
  30. }];
  31. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:[collRef queryLimitedTo:2]];
  32. XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ @{@"k" : @"a"}, @{@"k" : @"b"} ]));
  33. }
  34. - (void)testLimitQueriesWithDescendingSortOrder {
  35. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  36. @"a" : @{@"k" : @"a", @"sort" : @0},
  37. @"b" : @{@"k" : @"b", @"sort" : @1},
  38. @"c" : @{@"k" : @"c", @"sort" : @1},
  39. @"d" : @{@"k" : @"d", @"sort" : @2},
  40. }];
  41. FIRQuerySnapshot *snapshot =
  42. [self readDocumentSetForRef:[[collRef queryOrderedByField:@"sort" descending:YES]
  43. queryLimitedTo:2]];
  44. XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[
  45. @{ @"k" : @"d",
  46. @"sort" : @2 },
  47. @{ @"k" : @"c",
  48. @"sort" : @1 }
  49. ]));
  50. }
  51. - (void)testKeyOrderIsDescendingForDescendingInequality {
  52. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  53. @"a" : @{@"foo" : @42},
  54. @"b" : @{@"foo" : @42.0},
  55. @"c" : @{@"foo" : @42},
  56. @"d" : @{@"foo" : @21},
  57. @"e" : @{@"foo" : @21.0},
  58. @"f" : @{@"foo" : @66},
  59. @"g" : @{@"foo" : @66.0},
  60. }];
  61. FIRQuerySnapshot *snapshot =
  62. [self readDocumentSetForRef:[[collRef queryWhereField:@"foo" isGreaterThan:@21]
  63. queryOrderedByField:@"foo"
  64. descending:YES]];
  65. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"g", @"f", @"c", @"b", @"a" ]));
  66. }
  67. - (void)testUnaryFilterQueries {
  68. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  69. @"a" : @{@"null" : [NSNull null], @"nan" : @(NAN)},
  70. @"b" : @{@"null" : [NSNull null], @"nan" : @0},
  71. @"c" : @{@"null" : @NO, @"nan" : @(NAN)}
  72. }];
  73. FIRQuerySnapshot *results =
  74. [self readDocumentSetForRef:[[collRef queryWhereField:@"null" isEqualTo:[NSNull null]]
  75. queryWhereField:@"nan"
  76. isEqualTo:@(NAN)]];
  77. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), (@[
  78. @{ @"null" : [NSNull null],
  79. @"nan" : @(NAN) }
  80. ]));
  81. }
  82. - (void)testQueryWithFieldPaths {
  83. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  84. @"a" : @{@"a" : @1},
  85. @"b" : @{@"a" : @2},
  86. @"c" : @{@"a" : @3}
  87. }];
  88. FIRQuery *query =
  89. [collRef queryWhereFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]] isLessThan:@3];
  90. query = [query queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
  91. descending:YES];
  92. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:query];
  93. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
  94. }
  95. - (void)testQueryWithPredicate {
  96. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  97. @"a" : @{@"a" : @1},
  98. @"b" : @{@"a" : @2},
  99. @"c" : @{@"a" : @3}
  100. }];
  101. NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a < 3"];
  102. FIRQuery *query = [collRef queryFilteredUsingPredicate:predicate];
  103. query = [query queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
  104. descending:YES];
  105. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:query];
  106. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
  107. }
  108. - (void)testFilterOnInfinity {
  109. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  110. @"a" : @{@"inf" : @(INFINITY)},
  111. @"b" : @{@"inf" : @(-INFINITY)}
  112. }];
  113. FIRQuerySnapshot *results =
  114. [self readDocumentSetForRef:[collRef queryWhereField:@"inf" isEqualTo:@(INFINITY)]];
  115. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), (@[ @{ @"inf" : @(INFINITY) } ]));
  116. }
  117. - (void)testCanExplicitlySortByDocumentID {
  118. NSDictionary *testDocs = @{
  119. @"a" : @{@"key" : @"a"},
  120. @"b" : @{@"key" : @"b"},
  121. @"c" : @{@"key" : @"c"},
  122. };
  123. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  124. // Ideally this would be descending to validate it's different than
  125. // the default, but that requires an extra index
  126. FIRQuerySnapshot *docs =
  127. [self readDocumentSetForRef:[collection queryOrderedByFieldPath:[FIRFieldPath documentID]]];
  128. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs),
  129. (@[ testDocs[@"a"], testDocs[@"b"], testDocs[@"c"] ]));
  130. }
  131. - (void)testCanQueryByDocumentID {
  132. NSDictionary *testDocs = @{
  133. @"aa" : @{@"key" : @"aa"},
  134. @"ab" : @{@"key" : @"ab"},
  135. @"ba" : @{@"key" : @"ba"},
  136. @"bb" : @{@"key" : @"bb"},
  137. };
  138. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  139. FIRQuerySnapshot *docs =
  140. [self readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  141. isEqualTo:@"ab"]];
  142. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  143. }
  144. - (void)testCanQueryByDocumentIDs {
  145. NSDictionary *testDocs = @{
  146. @"aa" : @{@"key" : @"aa"},
  147. @"ab" : @{@"key" : @"ab"},
  148. @"ba" : @{@"key" : @"ba"},
  149. @"bb" : @{@"key" : @"bb"},
  150. };
  151. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  152. FIRQuerySnapshot *docs =
  153. [self readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  154. isEqualTo:@"ab"]];
  155. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  156. docs = [self readDocumentSetForRef:[[collection queryWhereFieldPath:[FIRFieldPath documentID]
  157. isGreaterThan:@"aa"]
  158. queryWhereFieldPath:[FIRFieldPath documentID]
  159. isLessThanOrEqualTo:@"ba"]];
  160. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ]));
  161. }
  162. - (void)testCanQueryByDocumentIDsUsingRefs {
  163. NSDictionary *testDocs = @{
  164. @"aa" : @{@"key" : @"aa"},
  165. @"ab" : @{@"key" : @"ab"},
  166. @"ba" : @{@"key" : @"ba"},
  167. @"bb" : @{@"key" : @"bb"},
  168. };
  169. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  170. FIRQuerySnapshot *docs = [self
  171. readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  172. isEqualTo:[collection documentWithPath:@"ab"]]];
  173. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  174. docs = [self
  175. readDocumentSetForRef:[[collection queryWhereFieldPath:[FIRFieldPath documentID]
  176. isGreaterThan:[collection documentWithPath:@"aa"]]
  177. queryWhereFieldPath:[FIRFieldPath documentID]
  178. isLessThanOrEqualTo:[collection documentWithPath:@"ba"]]];
  179. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ]));
  180. }
  181. - (void)testWatchSurvivesNetworkDisconnect {
  182. XCTestExpectation *testExpectiation =
  183. [self expectationWithDescription:@"testWatchSurvivesNetworkDisconnect"];
  184. FIRCollectionReference *collectionRef = [self collectionRef];
  185. FIRDocumentReference *docRef = [collectionRef documentWithAutoID];
  186. FIRFirestore *firestore = collectionRef.firestore;
  187. FIRQueryListenOptions *options = [[[FIRQueryListenOptions options]
  188. includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES];
  189. [collectionRef addSnapshotListenerWithOptions:options
  190. listener:^(FIRQuerySnapshot *snapshot, NSError *error) {
  191. XCTAssertNil(error);
  192. if (!snapshot.empty && !snapshot.metadata.fromCache) {
  193. [testExpectiation fulfill];
  194. }
  195. }];
  196. [firestore disableNetworkWithCompletion:^(NSError *error) {
  197. XCTAssertNil(error);
  198. [docRef setData:@{@"foo" : @"bar"}];
  199. [firestore enableNetworkWithCompletion:^(NSError *error) {
  200. XCTAssertNil(error);
  201. }];
  202. }];
  203. [self awaitExpectations];
  204. }
  205. - (void)testQueriesFireFromCacheWhenOffline {
  206. NSDictionary *testDocs = @{
  207. @"a" : @{@"foo" : @1},
  208. };
  209. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  210. FIRQueryListenOptions *options = [[[FIRQueryListenOptions options]
  211. includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES];
  212. id<FIRListenerRegistration> registration =
  213. [collection addSnapshotListenerWithOptions:options
  214. listener:self.eventAccumulator.valueEventHandler];
  215. FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"initial event"];
  216. XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), @[ @{ @"foo" : @1 } ]);
  217. XCTAssertEqual(querySnap.metadata.isFromCache, NO);
  218. [self disableNetwork];
  219. querySnap = [self.eventAccumulator awaitEventWithName:@"offline event with isFromCache=YES"];
  220. XCTAssertEqual(querySnap.metadata.isFromCache, YES);
  221. // TODO(b/70631617): There's currently a backend bug that prevents us from using a resume token
  222. // right away (against hexa at least). So we sleep. :-( :-( Anything over ~10ms seems to be
  223. // sufficient.
  224. [NSThread sleepForTimeInterval:0.2f];
  225. [self enableNetwork];
  226. querySnap = [self.eventAccumulator awaitEventWithName:@"back online event with isFromCache=NO"];
  227. XCTAssertEqual(querySnap.metadata.isFromCache, NO);
  228. [registration remove];
  229. }
  230. - (void)testCanHaveMultipleMutationsWhileOffline {
  231. FIRCollectionReference *col = [self collectionRef];
  232. // set a few docs to known values
  233. NSDictionary *initialDocs =
  234. @{ @"doc1" : @{@"key1" : @"value1"},
  235. @"doc2" : @{@"key2" : @"value2"} };
  236. [self writeAllDocuments:initialDocs toCollection:col];
  237. // go offline for the rest of this test
  238. [self disableNetwork];
  239. // apply *multiple* mutations while offline
  240. [[col documentWithPath:@"doc1"] setData:@{@"key1b" : @"value1b"}];
  241. [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"}];
  242. FIRQuerySnapshot *result = [self readDocumentSetForRef:col];
  243. XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[
  244. @{@"key1b" : @"value1b"},
  245. @{@"key2b" : @"value2b"},
  246. ]));
  247. }
  248. @end