FIRDatabaseTests.mm 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323
  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 <FirebaseCore/FIRAppInternal.h>
  17. #import <FirebaseFirestore/FirebaseFirestore.h>
  18. #import <XCTest/XCTest.h>
  19. #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
  20. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  21. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  22. #import "Firestore/Source/Core/FSTFirestoreClient.h"
  23. using firebase::firestore::util::TimerId;
  24. @interface FIRDatabaseTests : FSTIntegrationTestCase
  25. @end
  26. @implementation FIRDatabaseTests
  27. - (void)testCanUpdateAnExistingDocument {
  28. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  29. NSDictionary<NSString *, id> *initialData =
  30. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  31. NSDictionary<NSString *, id> *updateData =
  32. @{@"desc" : @"NewDescription", @"owner.email" : @"new@xyz.com"};
  33. NSDictionary<NSString *, id> *finalData =
  34. @{@"desc" : @"NewDescription", @"owner" : @{@"name" : @"Jonny", @"email" : @"new@xyz.com"}};
  35. [self writeDocumentRef:doc data:initialData];
  36. XCTestExpectation *updateCompletion = [self expectationWithDescription:@"updateData"];
  37. [doc updateData:updateData
  38. completion:^(NSError *_Nullable error) {
  39. XCTAssertNil(error);
  40. [updateCompletion fulfill];
  41. }];
  42. [self awaitExpectations];
  43. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  44. XCTAssertTrue(result.exists);
  45. XCTAssertEqualObjects(result.data, finalData);
  46. }
  47. - (void)testCanUpdateAnUnknownDocument {
  48. [self readerAndWriterOnDocumentRef:^(NSString *path, FIRDocumentReference *readerRef,
  49. FIRDocumentReference *writerRef) {
  50. [self writeDocumentRef:writerRef data:@{@"a" : @"a"}];
  51. [self updateDocumentRef:readerRef data:@{@"b" : @"b"}];
  52. FIRDocumentSnapshot *writerSnap = [self readDocumentForRef:writerRef
  53. source:FIRFirestoreSourceCache];
  54. XCTAssertTrue(writerSnap.exists);
  55. XCTestExpectation *expectation =
  56. [self expectationWithDescription:@"testCanUpdateAnUnknownDocument"];
  57. [readerRef getDocumentWithSource:FIRFirestoreSourceCache
  58. completion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) {
  59. XCTAssertNotNil(error);
  60. [expectation fulfill];
  61. }];
  62. [self awaitExpectations];
  63. writerSnap = [self readDocumentForRef:writerRef];
  64. XCTAssertEqualObjects(writerSnap.data, (@{@"a" : @"a", @"b" : @"b"}));
  65. FIRDocumentSnapshot *readerSnap = [self readDocumentForRef:writerRef];
  66. XCTAssertEqualObjects(readerSnap.data, (@{@"a" : @"a", @"b" : @"b"}));
  67. }];
  68. }
  69. - (void)testCanDeleteAFieldWithAnUpdate {
  70. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  71. NSDictionary<NSString *, id> *initialData =
  72. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  73. NSDictionary<NSString *, id> *updateData =
  74. @{@"owner.email" : [FIRFieldValue fieldValueForDelete]};
  75. NSDictionary<NSString *, id> *finalData =
  76. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny"}};
  77. [self writeDocumentRef:doc data:initialData];
  78. [self updateDocumentRef:doc data:updateData];
  79. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  80. XCTAssertTrue(result.exists);
  81. XCTAssertEqualObjects(result.data, finalData);
  82. }
  83. - (void)testDeleteDocument {
  84. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  85. NSDictionary<NSString *, id> *data = @{@"value" : @"foo"};
  86. [self writeDocumentRef:doc data:data];
  87. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  88. XCTAssertEqualObjects(result.data, data);
  89. [self deleteDocumentRef:doc];
  90. result = [self readDocumentForRef:doc];
  91. XCTAssertFalse(result.exists);
  92. }
  93. - (void)testCanRetrieveDocumentThatDoesNotExist {
  94. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  95. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  96. XCTAssertNil(result.data);
  97. XCTAssertNil(result[@"foo"]);
  98. }
  99. - (void)testCannotUpdateNonexistentDocument {
  100. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  101. XCTestExpectation *setCompletion = [self expectationWithDescription:@"setData"];
  102. [doc updateData:@{@"owner" : @"abc"}
  103. completion:^(NSError *_Nullable error) {
  104. XCTAssertNotNil(error);
  105. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  106. XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound);
  107. [setCompletion fulfill];
  108. }];
  109. [self awaitExpectations];
  110. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  111. XCTAssertFalse(result.exists);
  112. }
  113. - (void)testCanOverwriteDataAnExistingDocumentUsingSet {
  114. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  115. NSDictionary<NSString *, id> *initialData =
  116. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  117. NSDictionary<NSString *, id> *udpateData = @{@"desc" : @"NewDescription"};
  118. [self writeDocumentRef:doc data:initialData];
  119. [self writeDocumentRef:doc data:udpateData];
  120. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  121. XCTAssertEqualObjects(document.data, udpateData);
  122. }
  123. - (void)testCanMergeDataWithAnExistingDocumentUsingSet {
  124. if ([FSTIntegrationTestCase isRunningAgainstEmulator]) return; // b/112104025
  125. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  126. NSDictionary<NSString *, id> *initialData =
  127. @{@"desc" : @"Description", @"owner.data" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  128. NSDictionary<NSString *, id> *mergeData =
  129. @{@"updated" : @YES, @"owner.data" : @{@"name" : @"Sebastian"}};
  130. NSDictionary<NSString *, id> *finalData = @{
  131. @"desc" : @"Description",
  132. @"updated" : @YES,
  133. @"owner.data" : @{@"name" : @"Sebastian", @"email" : @"abc@xyz.com"}
  134. };
  135. [self writeDocumentRef:doc data:initialData];
  136. XCTestExpectation *completed =
  137. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  138. [doc setData:mergeData
  139. merge:YES
  140. completion:^(NSError *error) {
  141. XCTAssertNil(error);
  142. [completed fulfill];
  143. }];
  144. [self awaitExpectations];
  145. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  146. XCTAssertEqualObjects(document.data, finalData);
  147. }
  148. - (void)testCanMergeEmptyObject {
  149. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  150. FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
  151. id<FIRListenerRegistration> listenerRegistration =
  152. [doc addSnapshotListener:[accumulator valueEventHandler]];
  153. [self writeDocumentRef:doc data:@{}];
  154. FIRDocumentSnapshot *snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  155. XCTAssertEqualObjects(snapshot.data, @{});
  156. [self mergeDocumentRef:doc data:@{@"a" : @{}} fields:@[ @"a" ]];
  157. snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  158. XCTAssertEqualObjects(snapshot.data, @{@"a" : @{}});
  159. [self mergeDocumentRef:doc data:@{@"b" : @{}}];
  160. snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  161. XCTAssertEqualObjects(snapshot.data, (@{@"a" : @{}, @"b" : @{}}));
  162. snapshot = [self readDocumentForRef:doc source:FIRFirestoreSourceServer];
  163. XCTAssertEqualObjects(snapshot.data, (@{@"a" : @{}, @"b" : @{}}));
  164. [listenerRegistration remove];
  165. }
  166. - (void)testCanMergeServerTimestamps {
  167. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  168. NSDictionary<NSString *, id> *initialData = @{
  169. @"updated" : @NO,
  170. };
  171. NSDictionary<NSString *, id> *mergeData = @{
  172. @"time" : [FIRFieldValue fieldValueForServerTimestamp],
  173. @"nested" : @{@"time" : [FIRFieldValue fieldValueForServerTimestamp]}
  174. };
  175. [self writeDocumentRef:doc data:initialData];
  176. XCTestExpectation *completed =
  177. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  178. [doc setData:mergeData
  179. merge:YES
  180. completion:^(NSError *error) {
  181. XCTAssertNil(error);
  182. [completed fulfill];
  183. }];
  184. [self awaitExpectations];
  185. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  186. XCTAssertEqual(document[@"updated"], @NO);
  187. XCTAssertTrue([document[@"time"] isKindOfClass:[FIRTimestamp class]]);
  188. XCTAssertTrue([document[@"nested.time"] isKindOfClass:[FIRTimestamp class]]);
  189. }
  190. - (void)testCanDeleteFieldUsingMerge {
  191. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  192. NSDictionary<NSString *, id> *initialData =
  193. @{@"untouched" : @YES, @"foo" : @"bar", @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}};
  194. NSDictionary<NSString *, id> *mergeData = @{
  195. @"foo" : [FIRFieldValue fieldValueForDelete],
  196. @"nested" : @{@"foo" : [FIRFieldValue fieldValueForDelete]}
  197. };
  198. [self writeDocumentRef:doc data:initialData];
  199. XCTestExpectation *completed =
  200. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  201. [doc setData:mergeData
  202. merge:YES
  203. completion:^(NSError *error) {
  204. XCTAssertNil(error);
  205. [completed fulfill];
  206. }];
  207. [self awaitExpectations];
  208. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  209. XCTAssertEqual(document[@"untouched"], @YES);
  210. XCTAssertNil(document[@"foo"]);
  211. XCTAssertEqual(document[@"nested.untouched"], @YES);
  212. XCTAssertNil(document[@"nested.foo"]);
  213. }
  214. - (void)testCanDeleteFieldUsingMergeFields {
  215. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  216. NSDictionary<NSString *, id> *initialData = @{
  217. @"untouched" : @YES,
  218. @"foo" : @"bar",
  219. @"inner" : @{@"removed" : @YES, @"foo" : @"bar"},
  220. @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}
  221. };
  222. NSDictionary<NSString *, id> *mergeData = @{
  223. @"foo" : [FIRFieldValue fieldValueForDelete],
  224. @"inner" : @{@"foo" : [FIRFieldValue fieldValueForDelete]},
  225. @"nested" : @{
  226. @"untouched" : [FIRFieldValue fieldValueForDelete],
  227. @"foo" : [FIRFieldValue fieldValueForDelete]
  228. }
  229. };
  230. NSDictionary<NSString *, id> *finalData =
  231. @{@"untouched" : @YES, @"inner" : @{}, @"nested" : @{@"untouched" : @YES}};
  232. [self writeDocumentRef:doc data:initialData];
  233. XCTestExpectation *completed =
  234. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  235. [doc setData:mergeData
  236. mergeFields:@[ @"foo", @"inner", @"nested.foo" ]
  237. completion:^(NSError *error) {
  238. XCTAssertNil(error);
  239. [completed fulfill];
  240. }];
  241. [self awaitExpectations];
  242. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  243. XCTAssertEqualObjects([document data], finalData);
  244. }
  245. - (void)testCanSetServerTimestampsUsingMergeFields {
  246. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  247. NSDictionary<NSString *, id> *initialData =
  248. @{@"untouched" : @YES, @"foo" : @"bar", @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}};
  249. NSDictionary<NSString *, id> *mergeData = @{
  250. @"foo" : [FIRFieldValue fieldValueForServerTimestamp],
  251. @"inner" : @{@"foo" : [FIRFieldValue fieldValueForServerTimestamp]},
  252. @"nested" : @{@"foo" : [FIRFieldValue fieldValueForServerTimestamp]}
  253. };
  254. [self writeDocumentRef:doc data:initialData];
  255. XCTestExpectation *completed =
  256. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  257. [doc setData:mergeData
  258. mergeFields:@[ @"foo", @"inner", @"nested.foo" ]
  259. completion:^(NSError *error) {
  260. XCTAssertNil(error);
  261. [completed fulfill];
  262. }];
  263. [self awaitExpectations];
  264. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  265. XCTAssertTrue([document exists]);
  266. XCTAssertTrue([document[@"foo"] isKindOfClass:[FIRTimestamp class]]);
  267. XCTAssertTrue([document[@"inner.foo"] isKindOfClass:[FIRTimestamp class]]);
  268. XCTAssertTrue([document[@"nested.foo"] isKindOfClass:[FIRTimestamp class]]);
  269. }
  270. - (void)testMergeReplacesArrays {
  271. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  272. NSDictionary<NSString *, id> *initialData = @{
  273. @"untouched" : @YES,
  274. @"data" : @"old",
  275. @"topLevel" : @[ @"old", @"old" ],
  276. @"mapInArray" : @[ @{@"data" : @"old"} ]
  277. };
  278. NSDictionary<NSString *, id> *mergeData =
  279. @{@"data" : @"new", @"topLevel" : @[ @"new" ], @"mapInArray" : @[ @{@"data" : @"new"} ]};
  280. NSDictionary<NSString *, id> *finalData = @{
  281. @"untouched" : @YES,
  282. @"data" : @"new",
  283. @"topLevel" : @[ @"new" ],
  284. @"mapInArray" : @[ @{@"data" : @"new"} ]
  285. };
  286. [self writeDocumentRef:doc data:initialData];
  287. XCTestExpectation *completed =
  288. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  289. [doc setData:mergeData
  290. merge:YES
  291. completion:^(NSError *error) {
  292. XCTAssertNil(error);
  293. [completed fulfill];
  294. }];
  295. [self awaitExpectations];
  296. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  297. XCTAssertEqualObjects(document.data, finalData);
  298. }
  299. - (void)testCannotSpecifyFieldMaskForMissingField {
  300. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  301. XCTAssertThrowsSpecific(
  302. { [doc setData:@{} mergeFields:@[ @"foo" ]]; }, NSException,
  303. @"Field 'foo' is specified in your field mask but missing from your input data.");
  304. }
  305. - (void)testCanSetASubsetOfFieldsUsingMask {
  306. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  307. NSDictionary<NSString *, id> *initialData =
  308. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  309. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  310. [self writeDocumentRef:doc data:initialData];
  311. XCTestExpectation *completed =
  312. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  313. [doc setData:@{@"desc" : @"NewDescription", @"owner" : @"Sebastian"}
  314. mergeFields:@[ @"owner" ]
  315. completion:^(NSError *error) {
  316. XCTAssertNil(error);
  317. [completed fulfill];
  318. }];
  319. [self awaitExpectations];
  320. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  321. XCTAssertEqualObjects(document.data, finalData);
  322. }
  323. - (void)testDoesNotApplyFieldDeleteOutsideOfMask {
  324. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  325. NSDictionary<NSString *, id> *initialData =
  326. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  327. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  328. [self writeDocumentRef:doc data:initialData];
  329. XCTestExpectation *completed =
  330. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  331. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForDelete], @"owner" : @"Sebastian"}
  332. mergeFields:@[ @"owner" ]
  333. completion:^(NSError *error) {
  334. XCTAssertNil(error);
  335. [completed fulfill];
  336. }];
  337. [self awaitExpectations];
  338. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  339. XCTAssertEqualObjects(document.data, finalData);
  340. }
  341. - (void)testDoesNotApplyFieldTransformOutsideOfMask {
  342. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  343. NSDictionary<NSString *, id> *initialData =
  344. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  345. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  346. [self writeDocumentRef:doc data:initialData];
  347. XCTestExpectation *completed =
  348. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  349. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForServerTimestamp], @"owner" : @"Sebastian"}
  350. mergeFields:@[ @"owner" ]
  351. completion:^(NSError *error) {
  352. XCTAssertNil(error);
  353. [completed fulfill];
  354. }];
  355. [self awaitExpectations];
  356. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  357. XCTAssertEqualObjects(document.data, finalData);
  358. }
  359. - (void)testCanSetEmptyFieldMask {
  360. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  361. NSDictionary<NSString *, id> *initialData =
  362. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  363. NSDictionary<NSString *, id> *finalData = initialData;
  364. [self writeDocumentRef:doc data:initialData];
  365. XCTestExpectation *completed =
  366. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  367. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForServerTimestamp], @"owner" : @"Sebastian"}
  368. mergeFields:@[]
  369. completion:^(NSError *error) {
  370. XCTAssertNil(error);
  371. [completed fulfill];
  372. }];
  373. [self awaitExpectations];
  374. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  375. XCTAssertEqualObjects(document.data, finalData);
  376. }
  377. - (void)testCanSpecifyFieldsMultipleTimesInFieldMask {
  378. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  379. NSDictionary<NSString *, id> *initialData =
  380. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  381. NSDictionary<NSString *, id> *finalData =
  382. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Sebastian", @"email" : @"new@xyz.com"}};
  383. [self writeDocumentRef:doc data:initialData];
  384. XCTestExpectation *completed =
  385. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  386. [doc setData:@{
  387. @"desc" : @"NewDescription",
  388. @"owner" : @{@"name" : @"Sebastian", @"email" : @"new@xyz.com"}
  389. }
  390. mergeFields:@[ @"owner.name", @"owner", @"owner" ]
  391. completion:^(NSError *error) {
  392. XCTAssertNil(error);
  393. [completed fulfill];
  394. }];
  395. [self awaitExpectations];
  396. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  397. XCTAssertEqualObjects(document.data, finalData);
  398. }
  399. - (void)testAddingToACollectionYieldsTheCorrectDocumentReference {
  400. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  401. FIRDocumentReference *ref = [coll addDocumentWithData:@{@"foo" : @1}];
  402. XCTestExpectation *getCompletion = [self expectationWithDescription:@"getData"];
  403. [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable document,
  404. NSError *_Nullable error) {
  405. XCTAssertNil(error);
  406. XCTAssertEqualObjects(document.data, (@{@"foo" : @1}));
  407. [getCompletion fulfill];
  408. }];
  409. [self awaitExpectations];
  410. }
  411. - (void)testListenCanBeCalledMultipleTimes {
  412. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  413. FIRDocumentReference *doc = [coll documentWithAutoID];
  414. XCTestExpectation *completed = [self expectationWithDescription:@"multiple addSnapshotListeners"];
  415. __block NSDictionary<NSString *, id> *resultingData;
  416. // Shut the compiler up about strong references to doc.
  417. FIRDocumentReference *__weak weakDoc = doc;
  418. [doc setData:@{@"foo" : @"bar"}
  419. completion:^(NSError *error1) {
  420. XCTAssertNil(error1);
  421. FIRDocumentReference *strongDoc = weakDoc;
  422. [strongDoc addSnapshotListener:^(FIRDocumentSnapshot *snapshot2, NSError *error2) {
  423. XCTAssertNil(error2);
  424. FIRDocumentReference *strongDoc2 = weakDoc;
  425. [strongDoc2 addSnapshotListener:^(FIRDocumentSnapshot *snapshot3, NSError *error3) {
  426. XCTAssertNil(error3);
  427. resultingData = snapshot3.data;
  428. [completed fulfill];
  429. }];
  430. }];
  431. }];
  432. [self awaitExpectations];
  433. XCTAssertEqualObjects(resultingData, @{@"foo" : @"bar"});
  434. }
  435. - (void)testDocumentSnapshotEvents_nonExistent {
  436. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  437. XCTestExpectation *snapshotCompletion = [self expectationWithDescription:@"snapshot"];
  438. __block int callbacks = 0;
  439. id<FIRListenerRegistration> listenerRegistration =
  440. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  441. callbacks++;
  442. if (callbacks == 1) {
  443. XCTAssertNotNil(doc);
  444. XCTAssertFalse(doc.exists);
  445. [snapshotCompletion fulfill];
  446. } else {
  447. XCTFail("Should not have received this callback");
  448. }
  449. }];
  450. [self awaitExpectations];
  451. [listenerRegistration remove];
  452. }
  453. - (void)testDocumentSnapshotEvents_forAdd {
  454. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  455. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  456. __block XCTestExpectation *dataCompletion;
  457. __block int callbacks = 0;
  458. id<FIRListenerRegistration> listenerRegistration =
  459. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  460. callbacks++;
  461. if (callbacks == 1) {
  462. XCTAssertNotNil(doc);
  463. XCTAssertFalse(doc.exists);
  464. [emptyCompletion fulfill];
  465. } else if (callbacks == 2) {
  466. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  467. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  468. [dataCompletion fulfill];
  469. } else {
  470. XCTFail("Should not have received this callback");
  471. }
  472. }];
  473. [self awaitExpectations];
  474. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  475. [docRef setData:@{@"a" : @1}];
  476. [self awaitExpectations];
  477. [listenerRegistration remove];
  478. }
  479. - (void)testDocumentSnapshotEvents_forAddIncludingMetadata {
  480. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  481. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  482. __block XCTestExpectation *dataCompletion;
  483. __block int callbacks = 0;
  484. id<FIRListenerRegistration> listenerRegistration = [docRef
  485. addSnapshotListenerWithIncludeMetadataChanges:YES
  486. listener:^(FIRDocumentSnapshot *_Nullable doc,
  487. NSError *error) {
  488. callbacks++;
  489. if (callbacks == 1) {
  490. XCTAssertNotNil(doc);
  491. XCTAssertFalse(doc.exists);
  492. [emptyCompletion fulfill];
  493. } else if (callbacks == 2) {
  494. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  495. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  496. } else if (callbacks == 3) {
  497. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  498. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  499. [dataCompletion fulfill];
  500. } else {
  501. XCTFail("Should not have received this callback");
  502. }
  503. }];
  504. [self awaitExpectations];
  505. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  506. [docRef setData:@{@"a" : @1}];
  507. [self awaitExpectations];
  508. [listenerRegistration remove];
  509. }
  510. - (void)testDocumentSnapshotEvents_forChange {
  511. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  512. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  513. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  514. [self writeDocumentRef:docRef data:initialData];
  515. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  516. __block XCTestExpectation *changeCompletion;
  517. __block int callbacks = 0;
  518. id<FIRListenerRegistration> listenerRegistration =
  519. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  520. callbacks++;
  521. if (callbacks == 1) {
  522. XCTAssertEqualObjects(doc.data, initialData);
  523. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  524. [initialCompletion fulfill];
  525. } else if (callbacks == 2) {
  526. XCTAssertEqualObjects(doc.data, changedData);
  527. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  528. [changeCompletion fulfill];
  529. } else {
  530. XCTFail("Should not have received this callback");
  531. }
  532. }];
  533. [self awaitExpectations];
  534. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  535. [docRef setData:changedData];
  536. [self awaitExpectations];
  537. [listenerRegistration remove];
  538. }
  539. - (void)testDocumentSnapshotEvents_forChangeIncludingMetadata {
  540. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  541. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  542. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  543. [self writeDocumentRef:docRef data:initialData];
  544. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  545. __block XCTestExpectation *changeCompletion;
  546. __block int callbacks = 0;
  547. id<FIRListenerRegistration> listenerRegistration = [docRef
  548. addSnapshotListenerWithIncludeMetadataChanges:YES
  549. listener:^(FIRDocumentSnapshot *_Nullable doc,
  550. NSError *error) {
  551. callbacks++;
  552. if (callbacks == 1) {
  553. XCTAssertEqualObjects(doc.data, initialData);
  554. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  555. XCTAssertEqual(doc.metadata.isFromCache, YES);
  556. } else if (callbacks == 2) {
  557. XCTAssertEqualObjects(doc.data, initialData);
  558. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  559. XCTAssertEqual(doc.metadata.isFromCache, NO);
  560. [initialCompletion fulfill];
  561. } else if (callbacks == 3) {
  562. XCTAssertEqualObjects(doc.data, changedData);
  563. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  564. XCTAssertEqual(doc.metadata.isFromCache, NO);
  565. } else if (callbacks == 4) {
  566. XCTAssertEqualObjects(doc.data, changedData);
  567. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  568. XCTAssertEqual(doc.metadata.isFromCache, NO);
  569. [changeCompletion fulfill];
  570. } else {
  571. XCTFail("Should not have received this callback");
  572. }
  573. }];
  574. [self awaitExpectations];
  575. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  576. [docRef setData:changedData];
  577. [self awaitExpectations];
  578. [listenerRegistration remove];
  579. }
  580. - (void)testDocumentSnapshotEvents_forDelete {
  581. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  582. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  583. [self writeDocumentRef:docRef data:initialData];
  584. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  585. __block XCTestExpectation *changeCompletion;
  586. __block int callbacks = 0;
  587. id<FIRListenerRegistration> listenerRegistration =
  588. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  589. callbacks++;
  590. if (callbacks == 1) {
  591. XCTAssertEqualObjects(doc.data, initialData);
  592. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  593. XCTAssertEqual(doc.metadata.isFromCache, YES);
  594. [initialCompletion fulfill];
  595. } else if (callbacks == 2) {
  596. XCTAssertFalse(doc.exists);
  597. [changeCompletion fulfill];
  598. } else {
  599. XCTFail("Should not have received this callback");
  600. }
  601. }];
  602. [self awaitExpectations];
  603. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  604. [docRef deleteDocument];
  605. [self awaitExpectations];
  606. [listenerRegistration remove];
  607. }
  608. - (void)testDocumentSnapshotEvents_forDeleteIncludingMetadata {
  609. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  610. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  611. [self writeDocumentRef:docRef data:initialData];
  612. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  613. __block XCTestExpectation *changeCompletion;
  614. __block int callbacks = 0;
  615. id<FIRListenerRegistration> listenerRegistration = [docRef
  616. addSnapshotListenerWithIncludeMetadataChanges:YES
  617. listener:^(FIRDocumentSnapshot *_Nullable doc,
  618. NSError *error) {
  619. callbacks++;
  620. if (callbacks == 1) {
  621. XCTAssertEqualObjects(doc.data, initialData);
  622. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  623. XCTAssertEqual(doc.metadata.isFromCache, YES);
  624. } else if (callbacks == 2) {
  625. XCTAssertEqualObjects(doc.data, initialData);
  626. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  627. XCTAssertEqual(doc.metadata.isFromCache, NO);
  628. [initialCompletion fulfill];
  629. } else if (callbacks == 3) {
  630. XCTAssertFalse(doc.exists);
  631. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  632. XCTAssertEqual(doc.metadata.isFromCache, NO);
  633. [changeCompletion fulfill];
  634. } else {
  635. XCTFail("Should not have received this callback");
  636. }
  637. }];
  638. [self awaitExpectations];
  639. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  640. [docRef deleteDocument];
  641. [self awaitExpectations];
  642. [listenerRegistration remove];
  643. }
  644. - (void)testQuerySnapshotEvents_forAdd {
  645. FIRCollectionReference *roomsRef = [self collectionRef];
  646. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  647. NSDictionary<NSString *, id> *newData = @{@"a" : @1};
  648. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  649. __block XCTestExpectation *changeCompletion;
  650. __block int callbacks = 0;
  651. id<FIRListenerRegistration> listenerRegistration =
  652. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  653. callbacks++;
  654. if (callbacks == 1) {
  655. XCTAssertEqual(docSet.count, 0);
  656. [emptyCompletion fulfill];
  657. } else if (callbacks == 2) {
  658. XCTAssertEqual(docSet.count, 1);
  659. XCTAssertTrue([docSet.documents[0] isKindOfClass:[FIRQueryDocumentSnapshot class]]);
  660. XCTAssertEqualObjects(docSet.documents[0].data, newData);
  661. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  662. [changeCompletion fulfill];
  663. } else {
  664. XCTFail("Should not have received a third callback");
  665. }
  666. }];
  667. [self awaitExpectations];
  668. changeCompletion = [self expectationWithDescription:@"changed snapshot"];
  669. [docRef setData:newData];
  670. [self awaitExpectations];
  671. [listenerRegistration remove];
  672. }
  673. - (void)testQuerySnapshotEvents_forChange {
  674. FIRCollectionReference *roomsRef = [self collectionRef];
  675. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  676. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  677. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  678. [self writeDocumentRef:docRef data:initialData];
  679. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  680. __block XCTestExpectation *changeCompletion;
  681. __block int callbacks = 0;
  682. id<FIRListenerRegistration> listenerRegistration =
  683. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  684. callbacks++;
  685. if (callbacks == 1) {
  686. XCTAssertEqual(docSet.count, 1);
  687. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  688. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  689. [initialCompletion fulfill];
  690. } else if (callbacks == 2) {
  691. XCTAssertEqual(docSet.count, 1);
  692. XCTAssertEqualObjects(docSet.documents[0].data, changedData);
  693. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  694. [changeCompletion fulfill];
  695. } else {
  696. XCTFail("Should not have received a third callback");
  697. }
  698. }];
  699. [self awaitExpectations];
  700. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  701. [docRef setData:changedData];
  702. [self awaitExpectations];
  703. [listenerRegistration remove];
  704. }
  705. - (void)testQuerySnapshotEvents_forDelete {
  706. FIRCollectionReference *roomsRef = [self collectionRef];
  707. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  708. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  709. [self writeDocumentRef:docRef data:initialData];
  710. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  711. __block XCTestExpectation *changeCompletion;
  712. __block int callbacks = 0;
  713. id<FIRListenerRegistration> listenerRegistration =
  714. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  715. callbacks++;
  716. if (callbacks == 1) {
  717. XCTAssertEqual(docSet.count, 1);
  718. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  719. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  720. [initialCompletion fulfill];
  721. } else if (callbacks == 2) {
  722. XCTAssertEqual(docSet.count, 0);
  723. [changeCompletion fulfill];
  724. } else {
  725. XCTFail("Should not have received a third callback");
  726. }
  727. }];
  728. [self awaitExpectations];
  729. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  730. [docRef deleteDocument];
  731. [self awaitExpectations];
  732. [listenerRegistration remove];
  733. }
  734. - (void)testExposesFirestoreOnDocumentReferences {
  735. FIRDocumentReference *doc = [self.db documentWithPath:@"foo/bar"];
  736. XCTAssertEqual(doc.firestore, self.db);
  737. }
  738. - (void)testExposesFirestoreOnQueries {
  739. FIRQuery *q = [[self.db collectionWithPath:@"foo"] queryLimitedTo:5];
  740. XCTAssertEqual(q.firestore, self.db);
  741. }
  742. - (void)testDocumentReferenceEquality {
  743. FIRFirestore *firestore = self.db;
  744. FIRDocumentReference *docRef = [firestore documentWithPath:@"foo/bar"];
  745. XCTAssertEqualObjects([firestore documentWithPath:@"foo/bar"], docRef);
  746. XCTAssertEqualObjects([docRef collectionWithPath:@"blah"].parent, docRef);
  747. XCTAssertNotEqualObjects([firestore documentWithPath:@"foo/BAR"], docRef);
  748. FIRFirestore *otherFirestore = [self firestore];
  749. XCTAssertNotEqualObjects([otherFirestore documentWithPath:@"foo/bar"], docRef);
  750. }
  751. - (void)testQueryReferenceEquality {
  752. FIRFirestore *firestore = self.db;
  753. FIRQuery *query =
  754. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  755. isEqualTo:@42];
  756. FIRQuery *query2 =
  757. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  758. isEqualTo:@42];
  759. XCTAssertEqualObjects(query, query2);
  760. FIRQuery *query3 =
  761. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"BAR"] queryWhereField:@"baz"
  762. isEqualTo:@42];
  763. XCTAssertNotEqualObjects(query, query3);
  764. FIRFirestore *otherFirestore = [self firestore];
  765. FIRQuery *query4 = [[[otherFirestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"]
  766. queryWhereField:@"baz"
  767. isEqualTo:@42];
  768. XCTAssertNotEqualObjects(query, query4);
  769. }
  770. - (void)testCanTraverseCollectionsAndDocuments {
  771. NSString *expected = @"a/b/c/d";
  772. // doc path from root Firestore.
  773. XCTAssertEqualObjects([self.db documentWithPath:@"a/b/c/d"].path, expected);
  774. // collection path from root Firestore.
  775. XCTAssertEqualObjects([[self.db collectionWithPath:@"a/b/c"] documentWithPath:@"d"].path,
  776. expected);
  777. // doc path from CollectionReference.
  778. XCTAssertEqualObjects([[self.db collectionWithPath:@"a"] documentWithPath:@"b/c/d"].path,
  779. expected);
  780. // collection path from DocumentReference.
  781. XCTAssertEqualObjects([[self.db documentWithPath:@"a/b"] collectionWithPath:@"c/d/e"].path,
  782. @"a/b/c/d/e");
  783. }
  784. - (void)testCanTraverseCollectionAndDocumentParents {
  785. FIRCollectionReference *collection = [self.db collectionWithPath:@"a/b/c"];
  786. XCTAssertEqualObjects(collection.path, @"a/b/c");
  787. FIRDocumentReference *doc = collection.parent;
  788. XCTAssertEqualObjects(doc.path, @"a/b");
  789. collection = doc.parent;
  790. XCTAssertEqualObjects(collection.path, @"a");
  791. FIRDocumentReference *nilDoc = collection.parent;
  792. XCTAssertNil(nilDoc);
  793. }
  794. - (void)testUpdateFieldsWithDots {
  795. if ([FSTIntegrationTestCase isRunningAgainstEmulator]) return; // b/112104025
  796. FIRDocumentReference *doc = [self documentRef];
  797. [self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}];
  798. [self updateDocumentRef:doc
  799. data:@{(id)[[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new"}];
  800. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"];
  801. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  802. XCTAssertNil(error);
  803. XCTAssertEqualObjects(snapshot.data, (@{@"a.b" : @"new", @"c.d" : @"old"}));
  804. [expectation fulfill];
  805. }];
  806. [self awaitExpectations];
  807. }
  808. - (void)testUpdateNestedFields {
  809. FIRDocumentReference *doc = [self documentRef];
  810. [self writeDocumentRef:doc
  811. data:@{
  812. @"a" : @{@"b" : @"old"},
  813. @"c" : @{@"d" : @"old"},
  814. @"e" : @{@"f" : @"old"}
  815. }];
  816. [self updateDocumentRef:doc
  817. data:@{
  818. (id) @"a.b" : @"new",
  819. (id)[[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new"
  820. }];
  821. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"];
  822. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  823. XCTAssertNil(error);
  824. XCTAssertEqualObjects(snapshot.data, (@{
  825. @"a" : @{@"b" : @"new"},
  826. @"c" : @{@"d" : @"new"},
  827. @"e" : @{@"f" : @"old"}
  828. }));
  829. [expectation fulfill];
  830. }];
  831. [self awaitExpectations];
  832. }
  833. - (void)testCollectionID {
  834. XCTAssertEqualObjects([self.db collectionWithPath:@"foo"].collectionID, @"foo");
  835. XCTAssertEqualObjects([self.db collectionWithPath:@"foo/bar/baz"].collectionID, @"baz");
  836. }
  837. - (void)testDocumentID {
  838. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar"].documentID, @"bar");
  839. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar/baz/qux"].documentID, @"qux");
  840. }
  841. - (void)testCanQueueWritesWhileOffline {
  842. XCTestExpectation *writeEpectation = [self expectationWithDescription:@"successfull write"];
  843. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"enable network"];
  844. FIRDocumentReference *doc = [self documentRef];
  845. FIRFirestore *firestore = doc.firestore;
  846. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  847. [firestore disableNetworkWithCompletion:^(NSError *error) {
  848. XCTAssertNil(error);
  849. [doc setData:data
  850. completion:^(NSError *error) {
  851. XCTAssertNil(error);
  852. [writeEpectation fulfill];
  853. }];
  854. [firestore enableNetworkWithCompletion:^(NSError *error) {
  855. XCTAssertNil(error);
  856. [networkExpectation fulfill];
  857. }];
  858. }];
  859. [self awaitExpectations];
  860. XCTestExpectation *getExpectation = [self expectationWithDescription:@"successfull get"];
  861. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  862. XCTAssertNil(error);
  863. XCTAssertEqualObjects(snapshot.data, data);
  864. XCTAssertFalse(snapshot.metadata.isFromCache);
  865. [getExpectation fulfill];
  866. }];
  867. [self awaitExpectations];
  868. }
  869. - (void)testCanGetDocumentsWhileOffline {
  870. FIRDocumentReference *doc = [self documentRef];
  871. FIRFirestore *firestore = doc.firestore;
  872. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  873. XCTestExpectation *failExpectation =
  874. [self expectationWithDescription:@"offline read with no cached data"];
  875. XCTestExpectation *onlineExpectation = [self expectationWithDescription:@"online read"];
  876. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network online"];
  877. __weak FIRDocumentReference *weakDoc = doc;
  878. [firestore disableNetworkWithCompletion:^(NSError *error) {
  879. XCTAssertNil(error);
  880. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  881. XCTAssertNotNil(error);
  882. [failExpectation fulfill];
  883. }];
  884. [doc setData:data
  885. completion:^(NSError *_Nullable error) {
  886. XCTAssertNil(error);
  887. [weakDoc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  888. XCTAssertNil(error);
  889. // Verify that we are not reading from cache.
  890. XCTAssertFalse(snapshot.metadata.isFromCache);
  891. [onlineExpectation fulfill];
  892. }];
  893. }];
  894. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  895. XCTAssertNil(error);
  896. // Verify that we are reading from cache.
  897. XCTAssertTrue(snapshot.metadata.fromCache);
  898. XCTAssertEqualObjects(snapshot.data, data);
  899. [firestore enableNetworkWithCompletion:^(NSError *error) {
  900. [networkExpectation fulfill];
  901. }];
  902. }];
  903. }];
  904. [self awaitExpectations];
  905. }
  906. - (void)testWriteStreamReconnectsAfterIdle {
  907. FIRDocumentReference *doc = [self documentRef];
  908. FIRFirestore *firestore = doc.firestore;
  909. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  910. [self queueForFirestore:firestore] -> RunScheduledOperationsUntil(TimerId::WriteStreamIdle);
  911. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  912. }
  913. - (void)testWatchStreamReconnectsAfterIdle {
  914. FIRDocumentReference *doc = [self documentRef];
  915. FIRFirestore *firestore = doc.firestore;
  916. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  917. [self queueForFirestore:firestore] -> RunScheduledOperationsUntil(TimerId::ListenStreamIdle);
  918. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  919. }
  920. - (void)testCanDisableNetwork {
  921. FIRDocumentReference *doc = [self documentRef];
  922. FIRFirestore *firestore = doc.firestore;
  923. [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  924. [self awaitExpectations];
  925. [firestore
  926. enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network again"]];
  927. [self awaitExpectations];
  928. [firestore
  929. disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable network"]];
  930. [self awaitExpectations];
  931. [firestore
  932. disableNetworkWithCompletion:[self
  933. completionForExpectationWithName:@"Disable network again"]];
  934. [self awaitExpectations];
  935. [firestore
  936. enableNetworkWithCompletion:[self completionForExpectationWithName:@"Final enable network"]];
  937. [self awaitExpectations];
  938. }
  939. - (void)testClientCallsAfterShutdownFail {
  940. FIRDocumentReference *doc = [self documentRef];
  941. FIRFirestore *firestore = doc.firestore;
  942. [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  943. [self awaitExpectations];
  944. [firestore shutdownWithCompletion:[self completionForExpectationWithName:@"Shutdown"]];
  945. [self awaitExpectations];
  946. XCTAssertThrowsSpecific(
  947. {
  948. [firestore disableNetworkWithCompletion:^(NSError *error){
  949. }];
  950. },
  951. NSException, @"The client has already been shutdown.");
  952. }
  953. - (void)testMaintainsPersistenceAfterRestarting {
  954. FIRDocumentReference *doc = [self documentRef];
  955. FIRFirestore *firestore = doc.firestore;
  956. FIRApp *app = firestore.app;
  957. NSString *appName = app.name;
  958. FIROptions *options = app.options;
  959. NSDictionary<NSString *, id> *initialData = @{@"foo" : @"42"};
  960. [self writeDocumentRef:doc data:initialData];
  961. // -clearPersistence() requires Firestore to be shut down. Shutdown FIRApp and remove the
  962. // firestore instance to emulate the way an end user would do this.
  963. [self shutdownFirestore:firestore];
  964. [self.firestores removeObject:firestore];
  965. [self deleteApp:app];
  966. // We restart the app with the same name and options to check that the previous instance's
  967. // persistent storage persists its data after restarting. Calling [self firestore] here would
  968. // create a new instance of firestore, which defeats the purpose of this test.
  969. [FIRApp configureWithName:appName options:options];
  970. FIRApp *app2 = [FIRApp appNamed:appName];
  971. FIRFirestore *firestore2 = [self firestoreWithApp:app2];
  972. FIRDocumentReference *docRef2 = [firestore2 documentWithPath:doc.path];
  973. FIRDocumentSnapshot *snap = [self readDocumentForRef:docRef2 source:FIRFirestoreSourceCache];
  974. XCTAssertTrue(snap.exists);
  975. }
  976. - (void)testCanClearPersistenceAfterRestarting {
  977. FIRDocumentReference *doc = [self documentRef];
  978. FIRFirestore *firestore = doc.firestore;
  979. FIRApp *app = firestore.app;
  980. NSString *appName = app.name;
  981. FIROptions *options = app.options;
  982. NSDictionary<NSString *, id> *initialData = @{@"foo" : @"42"};
  983. [self writeDocumentRef:doc data:initialData];
  984. // -clearPersistence() requires Firestore to be shut down. Shutdown FIRApp and remove the
  985. // firestore instance to emulate the way an end user would do this.
  986. [self shutdownFirestore:firestore];
  987. [self.firestores removeObject:firestore];
  988. [firestore
  989. clearPersistenceWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  990. [self awaitExpectations];
  991. [self deleteApp:app];
  992. // We restart the app with the same name and options to check that the previous instance's
  993. // persistent storage is actually cleared after the restart. Calling [self firestore] here would
  994. // create a new instance of firestore, which defeats the purpose of this test.
  995. [FIRApp configureWithName:appName options:options];
  996. FIRApp *app2 = [FIRApp appNamed:appName];
  997. FIRFirestore *firestore2 = [self firestoreWithApp:app2];
  998. FIRDocumentReference *docRef2 = [firestore2 documentWithPath:doc.path];
  999. XCTestExpectation *expectation2 = [self expectationWithDescription:@"getData"];
  1000. [docRef2 getDocumentWithSource:FIRFirestoreSourceCache
  1001. completion:^(FIRDocumentSnapshot *doc2, NSError *_Nullable error) {
  1002. XCTAssertNotNil(error);
  1003. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1004. XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable);
  1005. [expectation2 fulfill];
  1006. }];
  1007. [self awaitExpectations];
  1008. }
  1009. - (void)testClearPersistenceWhileRunningFails {
  1010. FIRDocumentReference *doc = [self documentRef];
  1011. FIRFirestore *firestore = doc.firestore;
  1012. [self enableNetwork];
  1013. XCTestExpectation *expectation = [self expectationWithDescription:@"clearPersistence"];
  1014. [firestore clearPersistenceWithCompletion:^(NSError *_Nullable error) {
  1015. XCTAssertNotNil(error);
  1016. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1017. XCTAssertEqual(error.code, FIRFirestoreErrorCodeFailedPrecondition);
  1018. [expectation fulfill];
  1019. }];
  1020. [self awaitExpectations];
  1021. }
  1022. @end