FIRQueryTests.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. @interface FIRQueryTests : FSTIntegrationTestCase
  21. @end
  22. @implementation FIRQueryTests
  23. - (void)testLimitQueries {
  24. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  25. @"a" : @{@"k" : @"a"},
  26. @"b" : @{@"k" : @"b"},
  27. @"c" : @{@"k" : @"c"}
  28. }];
  29. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:[collRef queryLimitedTo:2]];
  30. XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ @{@"k" : @"a"}, @{@"k" : @"b"} ]));
  31. }
  32. - (void)testLimitQueriesWithDescendingSortOrder {
  33. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  34. @"a" : @{@"k" : @"a", @"sort" : @0},
  35. @"b" : @{@"k" : @"b", @"sort" : @1},
  36. @"c" : @{@"k" : @"c", @"sort" : @1},
  37. @"d" : @{@"k" : @"d", @"sort" : @2},
  38. }];
  39. FIRQuerySnapshot *snapshot =
  40. [self readDocumentSetForRef:[[collRef queryOrderedByField:@"sort"
  41. descending:YES] queryLimitedTo:2]];
  42. XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot),
  43. (@[ @{@"k" : @"d", @"sort" : @2}, @{@"k" : @"c", @"sort" : @1} ]));
  44. }
  45. - (void)testKeyOrderIsDescendingForDescendingInequality {
  46. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  47. @"a" : @{@"foo" : @42},
  48. @"b" : @{@"foo" : @42.0},
  49. @"c" : @{@"foo" : @42},
  50. @"d" : @{@"foo" : @21},
  51. @"e" : @{@"foo" : @21.0},
  52. @"f" : @{@"foo" : @66},
  53. @"g" : @{@"foo" : @66.0},
  54. }];
  55. FIRQuerySnapshot *snapshot =
  56. [self readDocumentSetForRef:[[collRef queryWhereField:@"foo"
  57. isGreaterThan:@21] queryOrderedByField:@"foo"
  58. descending:YES]];
  59. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"g", @"f", @"c", @"b", @"a" ]));
  60. }
  61. - (void)testUnaryFilterQueries {
  62. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  63. @"a" : @{@"null" : [NSNull null], @"nan" : @(NAN)},
  64. @"b" : @{@"null" : [NSNull null], @"nan" : @0},
  65. @"c" : @{@"null" : @NO, @"nan" : @(NAN)}
  66. }];
  67. FIRQuerySnapshot *results =
  68. [self readDocumentSetForRef:[[collRef queryWhereField:@"null"
  69. isEqualTo:[NSNull null]] queryWhereField:@"nan"
  70. isEqualTo:@(NAN)]];
  71. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results),
  72. (@[ @{@"null" : [NSNull null], @"nan" : @(NAN)} ]));
  73. }
  74. - (void)testQueryWithFieldPaths {
  75. FIRCollectionReference *collRef = [self
  76. collectionRefWithDocuments:@{@"a" : @{@"a" : @1}, @"b" : @{@"a" : @2}, @"c" : @{@"a" : @3}}];
  77. FIRQuery *query = [collRef queryWhereFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
  78. isLessThan:@3];
  79. query = [query queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
  80. descending:YES];
  81. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:query];
  82. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
  83. }
  84. - (void)testQueryWithPredicate {
  85. FIRCollectionReference *collRef = [self
  86. collectionRefWithDocuments:@{@"a" : @{@"a" : @1}, @"b" : @{@"a" : @2}, @"c" : @{@"a" : @3}}];
  87. NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a < 3"];
  88. FIRQuery *query = [collRef queryFilteredUsingPredicate:predicate];
  89. query = [query queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
  90. descending:YES];
  91. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:query];
  92. XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
  93. }
  94. - (void)testFilterOnInfinity {
  95. FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
  96. @"a" : @{@"inf" : @(INFINITY)},
  97. @"b" : @{@"inf" : @(-INFINITY)}
  98. }];
  99. FIRQuerySnapshot *results = [self readDocumentSetForRef:[collRef queryWhereField:@"inf"
  100. isEqualTo:@(INFINITY)]];
  101. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), (@[ @{@"inf" : @(INFINITY)} ]));
  102. }
  103. - (void)testCanExplicitlySortByDocumentID {
  104. NSDictionary *testDocs = @{
  105. @"a" : @{@"key" : @"a"},
  106. @"b" : @{@"key" : @"b"},
  107. @"c" : @{@"key" : @"c"},
  108. };
  109. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  110. // Ideally this would be descending to validate it's different than
  111. // the default, but that requires an extra index
  112. FIRQuerySnapshot *docs =
  113. [self readDocumentSetForRef:[collection queryOrderedByFieldPath:[FIRFieldPath documentID]]];
  114. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs),
  115. (@[ testDocs[@"a"], testDocs[@"b"], testDocs[@"c"] ]));
  116. }
  117. - (void)testCanQueryByDocumentID {
  118. NSDictionary *testDocs = @{
  119. @"aa" : @{@"key" : @"aa"},
  120. @"ab" : @{@"key" : @"ab"},
  121. @"ba" : @{@"key" : @"ba"},
  122. @"bb" : @{@"key" : @"bb"},
  123. };
  124. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  125. FIRQuerySnapshot *docs =
  126. [self readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  127. isEqualTo:@"ab"]];
  128. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  129. }
  130. - (void)testCanQueryByDocumentIDs {
  131. NSDictionary *testDocs = @{
  132. @"aa" : @{@"key" : @"aa"},
  133. @"ab" : @{@"key" : @"ab"},
  134. @"ba" : @{@"key" : @"ba"},
  135. @"bb" : @{@"key" : @"bb"},
  136. };
  137. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  138. FIRQuerySnapshot *docs =
  139. [self readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  140. isEqualTo:@"ab"]];
  141. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  142. docs = [self readDocumentSetForRef:[[collection queryWhereFieldPath:[FIRFieldPath documentID]
  143. isGreaterThan:@"aa"]
  144. queryWhereFieldPath:[FIRFieldPath documentID]
  145. isLessThanOrEqualTo:@"ba"]];
  146. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ]));
  147. }
  148. - (void)testCanQueryByDocumentIDsUsingRefs {
  149. NSDictionary *testDocs = @{
  150. @"aa" : @{@"key" : @"aa"},
  151. @"ab" : @{@"key" : @"ab"},
  152. @"ba" : @{@"key" : @"ba"},
  153. @"bb" : @{@"key" : @"bb"},
  154. };
  155. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  156. FIRQuerySnapshot *docs = [self
  157. readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
  158. isEqualTo:[collection documentWithPath:@"ab"]]];
  159. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"] ]));
  160. docs = [self
  161. readDocumentSetForRef:[[collection queryWhereFieldPath:[FIRFieldPath documentID]
  162. isGreaterThan:[collection documentWithPath:@"aa"]]
  163. queryWhereFieldPath:[FIRFieldPath documentID]
  164. isLessThanOrEqualTo:[collection documentWithPath:@"ba"]]];
  165. XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ]));
  166. }
  167. - (void)testWatchSurvivesNetworkDisconnect {
  168. XCTestExpectation *testExpectiation =
  169. [self expectationWithDescription:@"testWatchSurvivesNetworkDisconnect"];
  170. FIRCollectionReference *collectionRef = [self collectionRef];
  171. FIRDocumentReference *docRef = [collectionRef documentWithAutoID];
  172. FIRFirestore *firestore = collectionRef.firestore;
  173. [collectionRef
  174. addSnapshotListenerWithIncludeMetadataChanges:YES
  175. listener:^(FIRQuerySnapshot *snapshot, NSError *error) {
  176. XCTAssertNil(error);
  177. if (!snapshot.empty && !snapshot.metadata.fromCache) {
  178. [testExpectiation fulfill];
  179. }
  180. }];
  181. [firestore disableNetworkWithCompletion:^(NSError *error) {
  182. XCTAssertNil(error);
  183. [docRef setData:@{@"foo" : @"bar"}];
  184. [firestore enableNetworkWithCompletion:^(NSError *error) {
  185. XCTAssertNil(error);
  186. }];
  187. }];
  188. [self awaitExpectations];
  189. }
  190. - (void)testQueriesFireFromCacheWhenOffline {
  191. NSDictionary *testDocs = @{
  192. @"a" : @{@"foo" : @1},
  193. };
  194. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  195. id<FIRListenerRegistration> registration = [collection
  196. addSnapshotListenerWithIncludeMetadataChanges:YES
  197. listener:self.eventAccumulator.valueEventHandler];
  198. FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"initial event"];
  199. XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), @[ @{@"foo" : @1} ]);
  200. XCTAssertEqual(querySnap.metadata.isFromCache, NO);
  201. [self disableNetwork];
  202. querySnap = [self.eventAccumulator awaitEventWithName:@"offline event with isFromCache=YES"];
  203. XCTAssertEqual(querySnap.metadata.isFromCache, YES);
  204. [self enableNetwork];
  205. querySnap = [self.eventAccumulator awaitEventWithName:@"back online event with isFromCache=NO"];
  206. XCTAssertEqual(querySnap.metadata.isFromCache, NO);
  207. [registration remove];
  208. }
  209. - (void)testCanHaveMultipleMutationsWhileOffline {
  210. FIRCollectionReference *col = [self collectionRef];
  211. // set a few docs to known values
  212. NSDictionary *initialDocs = @{@"doc1" : @{@"key1" : @"value1"}, @"doc2" : @{@"key2" : @"value2"}};
  213. [self writeAllDocuments:initialDocs toCollection:col];
  214. // go offline for the rest of this test
  215. [self disableNetwork];
  216. // apply *multiple* mutations while offline
  217. [[col documentWithPath:@"doc1"] setData:@{@"key1b" : @"value1b"}];
  218. [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"}];
  219. FIRQuerySnapshot *result = [self readDocumentSetForRef:col];
  220. XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[
  221. @{@"key1b" : @"value1b"},
  222. @{@"key2b" : @"value2b"},
  223. ]));
  224. }
  225. - (void)testArrayContainsQueries {
  226. NSDictionary *testDocs = @{
  227. @"a" : @{@"array" : @[ @42 ]},
  228. @"b" : @{@"array" : @[ @"a", @42, @"c" ]},
  229. @"c" : @{@"array" : @[ @41.999, @"42", @{@"a" : @[ @42 ]} ]},
  230. @"d" : @{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]}
  231. };
  232. FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
  233. // Search for 42
  234. FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
  235. arrayContains:@42]];
  236. XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[
  237. @{@"array" : @[ @42 ]}, @{@"array" : @[ @"a", @42, @"c" ]},
  238. @{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]}
  239. ]));
  240. // NOTE: The backend doesn't currently support null, NaN, objects, or arrays, so there isn't much
  241. // of anything else interesting to test.
  242. }
  243. @end