FIRDatabaseTests.mm 45 KB

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