FIRArrayTransformTests.mm 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright 2018 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/Source/API/FIRFieldValue+Internal.h"
  19. #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
  20. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  21. /**
  22. * Note: Transforms are tested pretty thoroughly in FIRServerTimestampTests (via set, update,
  23. * transactions, nested in documents, multiple transforms together, etc.) and so these tests
  24. * mostly focus on the array transform semantics.
  25. */
  26. @interface FIRArrayTransformTests : FSTIntegrationTestCase
  27. @end
  28. @implementation FIRArrayTransformTests {
  29. // A document reference to read and write to.
  30. FIRDocumentReference *_docRef;
  31. // Accumulator used to capture events during the test.
  32. FSTEventAccumulator *_accumulator;
  33. // Listener registration for a listener maintained during the course of the test.
  34. id<FIRListenerRegistration> _listenerRegistration;
  35. }
  36. - (void)setUp {
  37. [super setUp];
  38. _docRef = [self documentRef];
  39. _accumulator = [FSTEventAccumulator accumulatorForTest:self];
  40. _listenerRegistration =
  41. [_docRef addSnapshotListenerWithIncludeMetadataChanges:YES
  42. listener:_accumulator.valueEventHandler];
  43. // Wait for initial nil snapshot to avoid potential races.
  44. FIRDocumentSnapshot *initialSnapshot = [_accumulator awaitEventWithName:@"initial event"];
  45. XCTAssertFalse(initialSnapshot.exists);
  46. }
  47. - (void)tearDown {
  48. [_listenerRegistration remove];
  49. [super tearDown];
  50. }
  51. #pragma mark - Test Helpers
  52. /** Waits for a snapshot with local writes. */
  53. - (FIRDocumentSnapshot *)waitForLocalEvent {
  54. FIRDocumentSnapshot *snapshot;
  55. do {
  56. snapshot = [_accumulator awaitEventWithName:@"Local event."];
  57. } while (!snapshot.metadata.hasPendingWrites);
  58. return snapshot;
  59. }
  60. /** Waits for a snapshot that has no pending writes */
  61. - (FIRDocumentSnapshot *)waitForRemoteEvent {
  62. FIRDocumentSnapshot *snapshot;
  63. do {
  64. snapshot = [_accumulator awaitEventWithName:@"Remote event."];
  65. } while (snapshot.metadata.hasPendingWrites);
  66. return snapshot;
  67. }
  68. /** Writes some initial data and consumes the events generated. */
  69. - (void)writeInitialData:(NSDictionary<NSString *, id> *)data {
  70. [self writeDocumentRef:_docRef data:data];
  71. XCTAssertEqualObjects([self waitForLocalEvent].data, data);
  72. XCTAssertEqualObjects([self waitForRemoteEvent].data, data);
  73. }
  74. #pragma mark - Test Cases
  75. - (void)testCreateDocumentWithArrayUnion {
  76. [self writeDocumentRef:_docRef
  77. data:@{
  78. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]]
  79. }];
  80. id expected = @{ @"array" : @[ @1, @2 ] };
  81. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  82. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  83. }
  84. - (void)testAppendToArrayViaUpdate {
  85. [self writeInitialData:@{ @"array" : @[ @1, @3 ] }];
  86. [self updateDocumentRef:_docRef
  87. data:@{
  88. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @1, @4 ]]
  89. }];
  90. id expected = @{ @"array" : @[ @1, @3, @2, @4 ] };
  91. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  92. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  93. }
  94. - (void)testAppendToArrayViaMergeSet {
  95. [self writeInitialData:@{ @"array" : @[ @1, @3 ] }];
  96. [self mergeDocumentRef:_docRef
  97. data:@{
  98. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @1, @4 ]]
  99. }];
  100. id expected = @{ @"array" : @[ @1, @3, @2, @4 ] };
  101. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  102. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  103. }
  104. - (void)testAppendObjectToArrayViaUpdate {
  105. [self writeInitialData:@{ @"array" : @[ @{@"a" : @"hi"} ] }];
  106. [self updateDocumentRef:_docRef
  107. data:@{
  108. @"array" : [FIRFieldValue
  109. fieldValueForArrayUnion:@[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ]]
  110. }];
  111. id expected = @{ @"array" : @[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ] };
  112. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  113. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  114. }
  115. - (void)testRemoveFromArrayViaUpdate {
  116. [self writeInitialData:@{ @"array" : @[ @1, @3, @1, @3 ] }];
  117. [self updateDocumentRef:_docRef
  118. data:@{
  119. @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @4 ]]
  120. }];
  121. id expected = @{ @"array" : @[ @3, @3 ] };
  122. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  123. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  124. }
  125. - (void)testRemoveFromArrayViaMergeSet {
  126. [self writeInitialData:@{ @"array" : @[ @1, @3, @1, @3 ] }];
  127. [self mergeDocumentRef:_docRef
  128. data:@{
  129. @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @4 ]]
  130. }];
  131. id expected = @{ @"array" : @[ @3, @3 ] };
  132. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  133. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  134. }
  135. - (void)testRemoveObjectFromArrayViaUpdate {
  136. [self writeInitialData:@{ @"array" : @[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ] }];
  137. [self updateDocumentRef:_docRef
  138. data:@{
  139. @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @{@"a" : @"hi"} ]]
  140. }];
  141. id expected = @{ @"array" : @[ @{@"a" : @"bye"} ] };
  142. XCTAssertEqualObjects([self waitForLocalEvent].data, expected);
  143. XCTAssertEqualObjects([self waitForRemoteEvent].data, expected);
  144. }
  145. @end
  146. /**
  147. * Unlike the FIRArrayTransformTests above, these tests intentionally avoid having any ongoing
  148. * listeners so that we can test what gets stored in the offline cache based purely on the write
  149. * acknowledgement (without receiving an updated document via watch). As such they also rely on
  150. * persistence being enabled so documents remain in the cache after the write.
  151. */
  152. @interface FIRArrayTransformServerApplicationTests : FSTIntegrationTestCase
  153. @end
  154. @implementation FIRArrayTransformServerApplicationTests {
  155. // A document reference to read and write to.
  156. FIRDocumentReference *_docRef;
  157. }
  158. - (void)setUp {
  159. [super setUp];
  160. _docRef = [self documentRef];
  161. }
  162. /**
  163. * Helper that uses a temporary listener to read from cache (returning nil if no document seems
  164. * to be in cache). Can probably be replaced with get(source=cache) in the future.
  165. */
  166. - (FIRDocumentSnapshot *_Nullable)getFromCache {
  167. FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
  168. id<FIRListenerRegistration> listenerRegistration =
  169. [_docRef addSnapshotListener:accumulator.valueEventHandler];
  170. FIRDocumentSnapshot *snapshot = [accumulator awaitEventWithName:@"listenForOneEvent"];
  171. [listenerRegistration remove];
  172. if (snapshot.metadata.fromCache) {
  173. return snapshot;
  174. } else {
  175. return nil;
  176. }
  177. }
  178. - (void)testServerApplicationOfSetWithNoCachedBaseDoc {
  179. [self writeDocumentRef:_docRef
  180. data:@{
  181. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]]
  182. }];
  183. id expected = @{ @"array" : @[ @1, @2 ] };
  184. XCTAssertEqualObjects([self getFromCache].data, expected);
  185. }
  186. - (void)testServerApplicationOfUpdateWithNoCachedBaseDoc {
  187. // Write an initial document out-of-band so it's not in our cache
  188. [self writeDocumentRef:[[self firestore] documentWithPath:_docRef.path]
  189. data:@{
  190. @"array" : @[ @42 ]
  191. }];
  192. [self updateDocumentRef:_docRef
  193. data:@{
  194. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]]
  195. }];
  196. // Nothing should be cached since it was an update and we had no base doc.
  197. XCTAssertNil([self getFromCache]);
  198. }
  199. - (void)testServerApplicationOfMergeSetWithNoCachedBaseDoc {
  200. // Write an initial document out-of-band so it's not in our cache
  201. [self writeDocumentRef:[[self firestore] documentWithPath:_docRef.path]
  202. data:@{
  203. @"array" : @[ @42 ]
  204. }];
  205. [self mergeDocumentRef:_docRef
  206. data:@{
  207. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]]
  208. }];
  209. // Document will be cached but we'll be missing 42.
  210. id expected = @{ @"array" : @[ @1, @2 ] };
  211. XCTAssertEqualObjects([self getFromCache].data, expected);
  212. }
  213. - (void)testServerApplicationOfArrayUnionUpdateWithCachedBaseDoc {
  214. // Cache a document with an array.
  215. [self writeDocumentRef:_docRef data:@{ @"array" : @[ @42 ] }];
  216. [self updateDocumentRef:_docRef
  217. data:@{
  218. @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]]
  219. }];
  220. // Should have merged the update with the cached doc.
  221. id expected = @{ @"array" : @[ @42, @1, @2 ] };
  222. XCTAssertEqualObjects([self getFromCache].data, expected);
  223. }
  224. - (void)testServerApplicationOfArrayRemoveUpdateWithCachedBaseDoc {
  225. // Cache a document with an array.
  226. [self writeDocumentRef:_docRef data:@{ @"array" : @[ @42, @1, @2 ] }];
  227. [self updateDocumentRef:_docRef
  228. data:@{
  229. @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @2 ]]
  230. }];
  231. // Should have merged the update with the cached doc.
  232. id expected = @{ @"array" : @[ @42 ] };
  233. XCTAssertEqualObjects([self getFromCache].data, expected);
  234. }
  235. @end