FIRDatabaseTests.mm 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  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. #include "Firestore/core/src/api/query_snapshot.h"
  23. #include "Firestore/core/src/core/firestore_client.h"
  24. #include "Firestore/core/test/unit/testutil/app_testing.h"
  25. namespace testutil = firebase::firestore::testutil;
  26. using firebase::firestore::util::TimerId;
  27. @interface FIRDatabaseTests : FSTIntegrationTestCase
  28. @end
  29. @implementation FIRDatabaseTests
  30. - (void)testCanUpdateAnExistingDocument {
  31. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  32. NSDictionary<NSString *, id> *initialData =
  33. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  34. NSDictionary<NSString *, id> *updateData =
  35. @{@"desc" : @"NewDescription", @"owner.email" : @"new@xyz.com"};
  36. NSDictionary<NSString *, id> *finalData =
  37. @{@"desc" : @"NewDescription", @"owner" : @{@"name" : @"Jonny", @"email" : @"new@xyz.com"}};
  38. [self writeDocumentRef:doc data:initialData];
  39. XCTestExpectation *updateCompletion = [self expectationWithDescription:@"updateData"];
  40. [doc updateData:updateData
  41. completion:^(NSError *_Nullable error) {
  42. XCTAssertNil(error);
  43. [updateCompletion fulfill];
  44. }];
  45. [self awaitExpectations];
  46. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  47. XCTAssertTrue(result.exists);
  48. XCTAssertEqualObjects(result.data, finalData);
  49. }
  50. - (void)testCanUpdateAnUnknownDocument {
  51. [self readerAndWriterOnDocumentRef:^(FIRDocumentReference *readerRef,
  52. FIRDocumentReference *writerRef) {
  53. [self writeDocumentRef:writerRef data:@{@"a" : @"a"}];
  54. [self updateDocumentRef:readerRef data:@{@"b" : @"b"}];
  55. FIRDocumentSnapshot *writerSnap = [self readDocumentForRef:writerRef
  56. source:FIRFirestoreSourceCache];
  57. XCTAssertTrue(writerSnap.exists);
  58. XCTestExpectation *expectation =
  59. [self expectationWithDescription:@"testCanUpdateAnUnknownDocument"];
  60. [readerRef getDocumentWithSource:FIRFirestoreSourceCache
  61. completion:^(FIRDocumentSnapshot *, NSError *_Nullable error) {
  62. XCTAssertNotNil(error);
  63. [expectation fulfill];
  64. }];
  65. [self awaitExpectations];
  66. writerSnap = [self readDocumentForRef:writerRef];
  67. XCTAssertEqualObjects(writerSnap.data, (@{@"a" : @"a", @"b" : @"b"}));
  68. FIRDocumentSnapshot *readerSnap = [self readDocumentForRef:writerRef];
  69. XCTAssertEqualObjects(readerSnap.data, (@{@"a" : @"a", @"b" : @"b"}));
  70. }];
  71. }
  72. - (void)testCanDeleteAFieldWithAnUpdate {
  73. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  74. NSDictionary<NSString *, id> *initialData =
  75. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  76. NSDictionary<NSString *, id> *updateData =
  77. @{@"owner.email" : [FIRFieldValue fieldValueForDelete]};
  78. NSDictionary<NSString *, id> *finalData =
  79. @{@"desc" : @"Description", @"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", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  120. NSDictionary<NSString *, id> *udpateData = @{@"desc" : @"NewDescription"};
  121. [self writeDocumentRef:doc data:initialData];
  122. [self writeDocumentRef:doc data:udpateData];
  123. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  124. XCTAssertEqualObjects(document.data, udpateData);
  125. }
  126. - (void)testCanMergeDataWithAnExistingDocumentUsingSet {
  127. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  128. NSDictionary<NSString *, id> *initialData =
  129. @{@"desc" : @"Description", @"owner.data" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  130. NSDictionary<NSString *, id> *mergeData =
  131. @{@"updated" : @YES, @"owner.data" : @{@"name" : @"Sebastian"}};
  132. NSDictionary<NSString *, id> *finalData = @{
  133. @"desc" : @"Description",
  134. @"updated" : @YES,
  135. @"owner.data" : @{@"name" : @"Sebastian", @"email" : @"abc@xyz.com"}
  136. };
  137. [self writeDocumentRef:doc data:initialData];
  138. XCTestExpectation *completed =
  139. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  140. [doc setData:mergeData
  141. merge:YES
  142. completion:^(NSError *error) {
  143. XCTAssertNil(error);
  144. [completed fulfill];
  145. }];
  146. [self awaitExpectations];
  147. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  148. XCTAssertEqualObjects(document.data, finalData);
  149. }
  150. - (void)testCanMergeEmptyObject {
  151. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  152. FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
  153. id<FIRListenerRegistration> listenerRegistration =
  154. [doc addSnapshotListener:[accumulator valueEventHandler]];
  155. [self writeDocumentRef:doc data:@{}];
  156. FIRDocumentSnapshot *snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  157. XCTAssertEqualObjects(snapshot.data, @{});
  158. [self mergeDocumentRef:doc data:@{@"a" : @{}} fields:@[ @"a" ]];
  159. snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  160. XCTAssertEqualObjects(snapshot.data, @{@"a" : @{}});
  161. [self mergeDocumentRef:doc data:@{@"b" : @{}}];
  162. snapshot = [accumulator awaitEventWithName:@"Snapshot"];
  163. XCTAssertEqualObjects(snapshot.data, (@{@"a" : @{}, @"b" : @{}}));
  164. snapshot = [self readDocumentForRef:doc source:FIRFirestoreSourceServer];
  165. XCTAssertEqualObjects(snapshot.data, (@{@"a" : @{}, @"b" : @{}}));
  166. [listenerRegistration remove];
  167. }
  168. - (void)testCanMergeServerTimestamps {
  169. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  170. NSDictionary<NSString *, id> *initialData = @{
  171. @"updated" : @NO,
  172. };
  173. NSDictionary<NSString *, id> *mergeData = @{
  174. @"time" : [FIRFieldValue fieldValueForServerTimestamp],
  175. @"nested" : @{@"time" : [FIRFieldValue fieldValueForServerTimestamp]}
  176. };
  177. [self writeDocumentRef:doc data:initialData];
  178. XCTestExpectation *completed =
  179. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  180. [doc setData:mergeData
  181. merge:YES
  182. completion:^(NSError *error) {
  183. XCTAssertNil(error);
  184. [completed fulfill];
  185. }];
  186. [self awaitExpectations];
  187. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  188. XCTAssertEqual(document[@"updated"], @NO);
  189. XCTAssertTrue([document[@"time"] isKindOfClass:[FIRTimestamp class]]);
  190. XCTAssertTrue([document[@"nested.time"] isKindOfClass:[FIRTimestamp class]]);
  191. }
  192. - (void)testCanDeleteFieldUsingMerge {
  193. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  194. NSDictionary<NSString *, id> *initialData =
  195. @{@"untouched" : @YES, @"foo" : @"bar", @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}};
  196. NSDictionary<NSString *, id> *mergeData = @{
  197. @"foo" : [FIRFieldValue fieldValueForDelete],
  198. @"nested" : @{@"foo" : [FIRFieldValue fieldValueForDelete]}
  199. };
  200. [self writeDocumentRef:doc data:initialData];
  201. XCTestExpectation *completed =
  202. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  203. [doc setData:mergeData
  204. merge:YES
  205. completion:^(NSError *error) {
  206. XCTAssertNil(error);
  207. [completed fulfill];
  208. }];
  209. [self awaitExpectations];
  210. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  211. XCTAssertEqual(document[@"untouched"], @YES);
  212. XCTAssertNil(document[@"foo"]);
  213. XCTAssertEqual(document[@"nested.untouched"], @YES);
  214. XCTAssertNil(document[@"nested.foo"]);
  215. }
  216. - (void)testCanDeleteFieldUsingMergeFields {
  217. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  218. NSDictionary<NSString *, id> *initialData = @{
  219. @"untouched" : @YES,
  220. @"foo" : @"bar",
  221. @"inner" : @{@"removed" : @YES, @"foo" : @"bar"},
  222. @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}
  223. };
  224. NSDictionary<NSString *, id> *mergeData = @{
  225. @"foo" : [FIRFieldValue fieldValueForDelete],
  226. @"inner" : @{@"foo" : [FIRFieldValue fieldValueForDelete]},
  227. @"nested" : @{
  228. @"untouched" : [FIRFieldValue fieldValueForDelete],
  229. @"foo" : [FIRFieldValue fieldValueForDelete]
  230. }
  231. };
  232. NSDictionary<NSString *, id> *finalData =
  233. @{@"untouched" : @YES, @"inner" : @{}, @"nested" : @{@"untouched" : @YES}};
  234. [self writeDocumentRef:doc data:initialData];
  235. XCTestExpectation *completed =
  236. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  237. [doc setData:mergeData
  238. mergeFields:@[ @"foo", @"inner", @"nested.foo" ]
  239. completion:^(NSError *error) {
  240. XCTAssertNil(error);
  241. [completed fulfill];
  242. }];
  243. [self awaitExpectations];
  244. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  245. XCTAssertEqualObjects([document data], finalData);
  246. }
  247. - (void)testCanSetServerTimestampsUsingMergeFields {
  248. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  249. NSDictionary<NSString *, id> *initialData =
  250. @{@"untouched" : @YES, @"foo" : @"bar", @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}};
  251. NSDictionary<NSString *, id> *mergeData = @{
  252. @"foo" : [FIRFieldValue fieldValueForServerTimestamp],
  253. @"inner" : @{@"foo" : [FIRFieldValue fieldValueForServerTimestamp]},
  254. @"nested" : @{@"foo" : [FIRFieldValue fieldValueForServerTimestamp]}
  255. };
  256. [self writeDocumentRef:doc data:initialData];
  257. XCTestExpectation *completed =
  258. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  259. [doc setData:mergeData
  260. mergeFields:@[ @"foo", @"inner", @"nested.foo" ]
  261. completion:^(NSError *error) {
  262. XCTAssertNil(error);
  263. [completed fulfill];
  264. }];
  265. [self awaitExpectations];
  266. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  267. XCTAssertTrue([document exists]);
  268. XCTAssertTrue([document[@"foo"] isKindOfClass:[FIRTimestamp class]]);
  269. XCTAssertTrue([document[@"inner.foo"] isKindOfClass:[FIRTimestamp class]]);
  270. XCTAssertTrue([document[@"nested.foo"] isKindOfClass:[FIRTimestamp class]]);
  271. }
  272. - (void)testMergeReplacesArrays {
  273. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  274. NSDictionary<NSString *, id> *initialData = @{
  275. @"untouched" : @YES,
  276. @"data" : @"old",
  277. @"topLevel" : @[ @"old", @"old" ],
  278. @"mapInArray" : @[ @{@"data" : @"old"} ]
  279. };
  280. NSDictionary<NSString *, id> *mergeData =
  281. @{@"data" : @"new", @"topLevel" : @[ @"new" ], @"mapInArray" : @[ @{@"data" : @"new"} ]};
  282. NSDictionary<NSString *, id> *finalData = @{
  283. @"untouched" : @YES,
  284. @"data" : @"new",
  285. @"topLevel" : @[ @"new" ],
  286. @"mapInArray" : @[ @{@"data" : @"new"} ]
  287. };
  288. [self writeDocumentRef:doc data:initialData];
  289. XCTestExpectation *completed =
  290. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  291. [doc setData:mergeData
  292. merge:YES
  293. completion:^(NSError *error) {
  294. XCTAssertNil(error);
  295. [completed fulfill];
  296. }];
  297. [self awaitExpectations];
  298. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  299. XCTAssertEqualObjects(document.data, finalData);
  300. }
  301. - (void)testCannotSpecifyFieldMaskForMissingField {
  302. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  303. XCTAssertThrowsSpecific(
  304. { [doc setData:@{} mergeFields:@[ @"foo" ]]; }, NSException,
  305. @"Field 'foo' is specified in your field mask but missing from your input data.");
  306. }
  307. - (void)testCanSetASubsetOfFieldsUsingMask {
  308. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  309. NSDictionary<NSString *, id> *initialData =
  310. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  311. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  312. [self writeDocumentRef:doc data:initialData];
  313. XCTestExpectation *completed =
  314. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  315. [doc setData:@{@"desc" : @"NewDescription", @"owner" : @"Sebastian"}
  316. mergeFields:@[ @"owner" ]
  317. completion:^(NSError *error) {
  318. XCTAssertNil(error);
  319. [completed fulfill];
  320. }];
  321. [self awaitExpectations];
  322. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  323. XCTAssertEqualObjects(document.data, finalData);
  324. }
  325. - (void)testDoesNotApplyFieldDeleteOutsideOfMask {
  326. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  327. NSDictionary<NSString *, id> *initialData =
  328. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  329. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  330. [self writeDocumentRef:doc data:initialData];
  331. XCTestExpectation *completed =
  332. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  333. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForDelete], @"owner" : @"Sebastian"}
  334. mergeFields:@[ @"owner" ]
  335. completion:^(NSError *error) {
  336. XCTAssertNil(error);
  337. [completed fulfill];
  338. }];
  339. [self awaitExpectations];
  340. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  341. XCTAssertEqualObjects(document.data, finalData);
  342. }
  343. - (void)testDoesNotApplyFieldTransformOutsideOfMask {
  344. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  345. NSDictionary<NSString *, id> *initialData =
  346. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  347. NSDictionary<NSString *, id> *finalData = @{@"desc" : @"Description", @"owner" : @"Sebastian"};
  348. [self writeDocumentRef:doc data:initialData];
  349. XCTestExpectation *completed =
  350. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  351. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForServerTimestamp], @"owner" : @"Sebastian"}
  352. mergeFields:@[ @"owner" ]
  353. completion:^(NSError *error) {
  354. XCTAssertNil(error);
  355. [completed fulfill];
  356. }];
  357. [self awaitExpectations];
  358. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  359. XCTAssertEqualObjects(document.data, finalData);
  360. }
  361. - (void)testCanSetEmptyFieldMask {
  362. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  363. NSDictionary<NSString *, id> *initialData =
  364. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  365. NSDictionary<NSString *, id> *finalData = initialData;
  366. [self writeDocumentRef:doc data:initialData];
  367. XCTestExpectation *completed =
  368. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  369. [doc setData:@{@"desc" : [FIRFieldValue fieldValueForServerTimestamp], @"owner" : @"Sebastian"}
  370. mergeFields:@[]
  371. completion:^(NSError *error) {
  372. XCTAssertNil(error);
  373. [completed fulfill];
  374. }];
  375. [self awaitExpectations];
  376. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  377. XCTAssertEqualObjects(document.data, finalData);
  378. }
  379. - (void)testCanSpecifyFieldsMultipleTimesInFieldMask {
  380. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  381. NSDictionary<NSString *, id> *initialData =
  382. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  383. NSDictionary<NSString *, id> *finalData =
  384. @{@"desc" : @"Description", @"owner" : @{@"name" : @"Sebastian", @"email" : @"new@xyz.com"}};
  385. [self writeDocumentRef:doc data:initialData];
  386. XCTestExpectation *completed =
  387. [self expectationWithDescription:@"testCanSetASubsetOfFieldsUsingMask"];
  388. [doc setData:@{
  389. @"desc" : @"NewDescription",
  390. @"owner" : @{@"name" : @"Sebastian", @"email" : @"new@xyz.com"}
  391. }
  392. mergeFields:@[ @"owner.name", @"owner", @"owner" ]
  393. completion:^(NSError *error) {
  394. XCTAssertNil(error);
  395. [completed fulfill];
  396. }];
  397. [self awaitExpectations];
  398. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  399. XCTAssertEqualObjects(document.data, finalData);
  400. }
  401. - (void)testAddingToACollectionYieldsTheCorrectDocumentReference {
  402. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  403. FIRDocumentReference *ref = [coll addDocumentWithData:@{@"foo" : @1}];
  404. XCTestExpectation *getCompletion = [self expectationWithDescription:@"getData"];
  405. [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable document,
  406. NSError *_Nullable error) {
  407. XCTAssertNil(error);
  408. XCTAssertEqualObjects(document.data, (@{@"foo" : @1}));
  409. [getCompletion fulfill];
  410. }];
  411. [self awaitExpectations];
  412. }
  413. - (void)testSnapshotsInSyncListenerFiresAfterListenersInSync {
  414. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  415. FIRDocumentReference *ref = [coll addDocumentWithData:@{@"foo" : @1}];
  416. NSMutableArray<NSString *> *events = [NSMutableArray array];
  417. XCTestExpectation *gotInitialSnapshot = [self expectationWithDescription:@"gotInitialSnapshot"];
  418. __block bool setupComplete = false;
  419. [ref addSnapshotListener:^(FIRDocumentSnapshot *, NSError *error) {
  420. XCTAssertNil(error);
  421. [events addObject:@"doc"];
  422. // Wait for the initial event from the backend so that we know we'll get exactly one snapshot
  423. // event for our local write below.
  424. if (!setupComplete) {
  425. setupComplete = true;
  426. [gotInitialSnapshot fulfill];
  427. }
  428. }];
  429. [self awaitExpectations];
  430. [events removeAllObjects];
  431. XCTestExpectation *done = [self expectationWithDescription:@"SnapshotsInSyncListenerDone"];
  432. [ref.firestore addSnapshotsInSyncListener:^() {
  433. [events addObject:@"snapshots-in-sync"];
  434. if ([events count] == 3) {
  435. // We should have an initial snapshots-in-sync event, then a snapshot event
  436. // for set(), then another event to indicate we're in sync again.
  437. NSArray<NSString *> *expected = @[ @"snapshots-in-sync", @"doc", @"snapshots-in-sync" ];
  438. XCTAssertEqualObjects(events, expected);
  439. [done fulfill];
  440. }
  441. }];
  442. [self writeDocumentRef:ref data:@{@"foo" : @3}];
  443. }
  444. - (void)testListenCanBeCalledMultipleTimes {
  445. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  446. FIRDocumentReference *doc = [coll documentWithAutoID];
  447. XCTestExpectation *completed = [self expectationWithDescription:@"multiple addSnapshotListeners"];
  448. __block NSDictionary<NSString *, id> *resultingData;
  449. // Shut the compiler up about strong references to doc.
  450. FIRDocumentReference *__weak weakDoc = doc;
  451. [doc setData:@{@"foo" : @"bar"}
  452. completion:^(NSError *error1) {
  453. XCTAssertNil(error1);
  454. FIRDocumentReference *strongDoc = weakDoc;
  455. [strongDoc addSnapshotListener:^(FIRDocumentSnapshot *, NSError *error2) {
  456. XCTAssertNil(error2);
  457. FIRDocumentReference *strongDoc2 = weakDoc;
  458. [strongDoc2 addSnapshotListener:^(FIRDocumentSnapshot *snapshot3, NSError *error3) {
  459. XCTAssertNil(error3);
  460. resultingData = snapshot3.data;
  461. [completed fulfill];
  462. }];
  463. }];
  464. }];
  465. [self awaitExpectations];
  466. XCTAssertEqualObjects(resultingData, @{@"foo" : @"bar"});
  467. }
  468. - (void)testDocumentSnapshotEvents_nonExistent {
  469. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  470. XCTestExpectation *snapshotCompletion = [self expectationWithDescription:@"snapshot"];
  471. __block int callbacks = 0;
  472. id<FIRListenerRegistration> listenerRegistration =
  473. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *) {
  474. callbacks++;
  475. if (callbacks == 1) {
  476. XCTAssertNotNil(doc);
  477. XCTAssertFalse(doc.exists);
  478. [snapshotCompletion fulfill];
  479. } else {
  480. XCTFail("Should not have received this callback");
  481. }
  482. }];
  483. [self awaitExpectations];
  484. [listenerRegistration remove];
  485. }
  486. - (void)testDocumentSnapshotEvents_forAdd {
  487. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  488. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  489. __block XCTestExpectation *dataCompletion;
  490. __block int callbacks = 0;
  491. id<FIRListenerRegistration> listenerRegistration =
  492. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *) {
  493. callbacks++;
  494. if (callbacks == 1) {
  495. XCTAssertNotNil(doc);
  496. XCTAssertFalse(doc.exists);
  497. [emptyCompletion fulfill];
  498. } else if (callbacks == 2) {
  499. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  500. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  501. [dataCompletion fulfill];
  502. } else {
  503. XCTFail("Should not have received this callback");
  504. }
  505. }];
  506. [self awaitExpectations];
  507. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  508. [docRef setData:@{@"a" : @1}];
  509. [self awaitExpectations];
  510. [listenerRegistration remove];
  511. }
  512. - (void)testDocumentSnapshotEvents_forAddIncludingMetadata {
  513. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  514. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  515. __block XCTestExpectation *dataCompletion;
  516. __block int callbacks = 0;
  517. id<FIRListenerRegistration> listenerRegistration = [docRef
  518. addSnapshotListenerWithIncludeMetadataChanges:YES
  519. listener:^(FIRDocumentSnapshot *_Nullable doc,
  520. NSError *) {
  521. callbacks++;
  522. if (callbacks == 1) {
  523. XCTAssertNotNil(doc);
  524. XCTAssertFalse(doc.exists);
  525. [emptyCompletion fulfill];
  526. } else if (callbacks == 2) {
  527. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  528. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  529. } else if (callbacks == 3) {
  530. XCTAssertEqualObjects(doc.data, (@{@"a" : @1}));
  531. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  532. [dataCompletion fulfill];
  533. } else {
  534. XCTFail("Should not have received this callback");
  535. }
  536. }];
  537. [self awaitExpectations];
  538. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  539. [docRef setData:@{@"a" : @1}];
  540. [self awaitExpectations];
  541. [listenerRegistration remove];
  542. }
  543. - (void)testDocumentSnapshotEvents_forChange {
  544. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  545. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  546. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  547. [self writeDocumentRef:docRef data:initialData];
  548. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  549. __block XCTestExpectation *changeCompletion;
  550. __block int callbacks = 0;
  551. id<FIRListenerRegistration> listenerRegistration =
  552. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *) {
  553. callbacks++;
  554. if (callbacks == 1) {
  555. XCTAssertEqualObjects(doc.data, initialData);
  556. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  557. [initialCompletion fulfill];
  558. } else if (callbacks == 2) {
  559. XCTAssertEqualObjects(doc.data, changedData);
  560. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  561. [changeCompletion fulfill];
  562. } else {
  563. XCTFail("Should not have received this callback");
  564. }
  565. }];
  566. [self awaitExpectations];
  567. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  568. [docRef setData:changedData];
  569. [self awaitExpectations];
  570. [listenerRegistration remove];
  571. }
  572. - (void)testDocumentSnapshotEvents_forChangeIncludingMetadata {
  573. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  574. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  575. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  576. [self writeDocumentRef:docRef data:initialData];
  577. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  578. __block XCTestExpectation *changeCompletion;
  579. __block int callbacks = 0;
  580. id<FIRListenerRegistration> listenerRegistration = [docRef
  581. addSnapshotListenerWithIncludeMetadataChanges:YES
  582. listener:^(FIRDocumentSnapshot *_Nullable doc,
  583. NSError *) {
  584. callbacks++;
  585. if (callbacks == 1) {
  586. XCTAssertEqualObjects(doc.data, initialData);
  587. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  588. XCTAssertEqual(doc.metadata.isFromCache, YES);
  589. } else if (callbacks == 2) {
  590. XCTAssertEqualObjects(doc.data, initialData);
  591. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  592. XCTAssertEqual(doc.metadata.isFromCache, NO);
  593. [initialCompletion fulfill];
  594. } else if (callbacks == 3) {
  595. XCTAssertEqualObjects(doc.data, changedData);
  596. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  597. XCTAssertEqual(doc.metadata.isFromCache, NO);
  598. } else if (callbacks == 4) {
  599. XCTAssertEqualObjects(doc.data, changedData);
  600. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  601. XCTAssertEqual(doc.metadata.isFromCache, NO);
  602. [changeCompletion fulfill];
  603. } else {
  604. XCTFail("Should not have received this callback");
  605. }
  606. }];
  607. [self awaitExpectations];
  608. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  609. [docRef setData:changedData];
  610. [self awaitExpectations];
  611. [listenerRegistration remove];
  612. }
  613. - (void)testDocumentSnapshotEvents_forDelete {
  614. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  615. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  616. [self writeDocumentRef:docRef data:initialData];
  617. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  618. __block XCTestExpectation *changeCompletion;
  619. __block int callbacks = 0;
  620. id<FIRListenerRegistration> listenerRegistration =
  621. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *) {
  622. callbacks++;
  623. if (callbacks == 1) {
  624. XCTAssertEqualObjects(doc.data, initialData);
  625. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  626. XCTAssertEqual(doc.metadata.isFromCache, YES);
  627. [initialCompletion fulfill];
  628. } else if (callbacks == 2) {
  629. XCTAssertFalse(doc.exists);
  630. [changeCompletion fulfill];
  631. } else {
  632. XCTFail("Should not have received this callback");
  633. }
  634. }];
  635. [self awaitExpectations];
  636. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  637. [docRef deleteDocument];
  638. [self awaitExpectations];
  639. [listenerRegistration remove];
  640. }
  641. - (void)testDocumentSnapshotEvents_forDeleteIncludingMetadata {
  642. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  643. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  644. [self writeDocumentRef:docRef data:initialData];
  645. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  646. __block XCTestExpectation *changeCompletion;
  647. __block int callbacks = 0;
  648. id<FIRListenerRegistration> listenerRegistration = [docRef
  649. addSnapshotListenerWithIncludeMetadataChanges:YES
  650. listener:^(FIRDocumentSnapshot *_Nullable doc,
  651. NSError *) {
  652. callbacks++;
  653. if (callbacks == 1) {
  654. XCTAssertEqualObjects(doc.data, initialData);
  655. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  656. XCTAssertEqual(doc.metadata.isFromCache, YES);
  657. } else if (callbacks == 2) {
  658. XCTAssertEqualObjects(doc.data, initialData);
  659. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  660. XCTAssertEqual(doc.metadata.isFromCache, NO);
  661. [initialCompletion fulfill];
  662. } else if (callbacks == 3) {
  663. XCTAssertFalse(doc.exists);
  664. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  665. XCTAssertEqual(doc.metadata.isFromCache, NO);
  666. [changeCompletion fulfill];
  667. } else {
  668. XCTFail("Should not have received this callback");
  669. }
  670. }];
  671. [self awaitExpectations];
  672. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  673. [docRef deleteDocument];
  674. [self awaitExpectations];
  675. [listenerRegistration remove];
  676. }
  677. - (void)testQuerySnapshotEvents_forAdd {
  678. FIRCollectionReference *roomsRef = [self collectionRef];
  679. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  680. NSDictionary<NSString *, id> *newData = @{@"a" : @1};
  681. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  682. __block XCTestExpectation *changeCompletion;
  683. __block int callbacks = 0;
  684. id<FIRListenerRegistration> listenerRegistration =
  685. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *) {
  686. callbacks++;
  687. if (callbacks == 1) {
  688. XCTAssertEqual(docSet.count, 0);
  689. [emptyCompletion fulfill];
  690. } else if (callbacks == 2) {
  691. XCTAssertEqual(docSet.count, 1);
  692. XCTAssertTrue([docSet.documents[0] isKindOfClass:[FIRQueryDocumentSnapshot class]]);
  693. XCTAssertEqualObjects(docSet.documents[0].data, newData);
  694. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  695. [changeCompletion fulfill];
  696. } else {
  697. XCTFail("Should not have received a third callback");
  698. }
  699. }];
  700. [self awaitExpectations];
  701. changeCompletion = [self expectationWithDescription:@"changed snapshot"];
  702. [docRef setData:newData];
  703. [self awaitExpectations];
  704. [listenerRegistration remove];
  705. }
  706. - (void)testQuerySnapshotEvents_forChange {
  707. FIRCollectionReference *roomsRef = [self collectionRef];
  708. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  709. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  710. NSDictionary<NSString *, id> *changedData = @{@"b" : @2};
  711. [self writeDocumentRef:docRef data:initialData];
  712. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  713. __block XCTestExpectation *changeCompletion;
  714. __block int callbacks = 0;
  715. id<FIRListenerRegistration> listenerRegistration =
  716. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *) {
  717. callbacks++;
  718. if (callbacks == 1) {
  719. XCTAssertEqual(docSet.count, 1);
  720. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  721. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  722. [initialCompletion fulfill];
  723. } else if (callbacks == 2) {
  724. XCTAssertEqual(docSet.count, 1);
  725. XCTAssertEqualObjects(docSet.documents[0].data, changedData);
  726. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  727. [changeCompletion fulfill];
  728. } else {
  729. XCTFail("Should not have received a third callback");
  730. }
  731. }];
  732. [self awaitExpectations];
  733. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  734. [docRef setData:changedData];
  735. [self awaitExpectations];
  736. [listenerRegistration remove];
  737. }
  738. - (void)testQuerySnapshotEvents_forDelete {
  739. FIRCollectionReference *roomsRef = [self collectionRef];
  740. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  741. NSDictionary<NSString *, id> *initialData = @{@"a" : @1};
  742. [self writeDocumentRef:docRef data:initialData];
  743. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  744. __block XCTestExpectation *changeCompletion;
  745. __block int callbacks = 0;
  746. id<FIRListenerRegistration> listenerRegistration =
  747. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *) {
  748. callbacks++;
  749. if (callbacks == 1) {
  750. XCTAssertEqual(docSet.count, 1);
  751. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  752. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  753. [initialCompletion fulfill];
  754. } else if (callbacks == 2) {
  755. XCTAssertEqual(docSet.count, 0);
  756. [changeCompletion fulfill];
  757. } else {
  758. XCTFail("Should not have received a third callback");
  759. }
  760. }];
  761. [self awaitExpectations];
  762. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  763. [docRef deleteDocument];
  764. [self awaitExpectations];
  765. [listenerRegistration remove];
  766. }
  767. - (void)testExposesFirestoreOnDocumentReferences {
  768. FIRDocumentReference *doc = [self.db documentWithPath:@"foo/bar"];
  769. XCTAssertEqual(doc.firestore, self.db);
  770. }
  771. - (void)testExposesFirestoreOnQueries {
  772. FIRQuery *q = [[self.db collectionWithPath:@"foo"] queryLimitedTo:5];
  773. XCTAssertEqual(q.firestore, self.db);
  774. }
  775. - (void)testDocumentReferenceEquality {
  776. FIRFirestore *firestore = self.db;
  777. FIRDocumentReference *docRef = [firestore documentWithPath:@"foo/bar"];
  778. XCTAssertEqualObjects([firestore documentWithPath:@"foo/bar"], docRef);
  779. XCTAssertEqualObjects([docRef collectionWithPath:@"blah"].parent, docRef);
  780. XCTAssertNotEqualObjects([firestore documentWithPath:@"foo/BAR"], docRef);
  781. FIRFirestore *otherFirestore = [self firestore];
  782. XCTAssertNotEqualObjects([otherFirestore documentWithPath:@"foo/bar"], docRef);
  783. }
  784. - (void)testQueryReferenceEquality {
  785. FIRFirestore *firestore = self.db;
  786. FIRQuery *query =
  787. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  788. isEqualTo:@42];
  789. FIRQuery *query2 =
  790. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  791. isEqualTo:@42];
  792. XCTAssertEqualObjects(query, query2);
  793. FIRQuery *query3 =
  794. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"BAR"] queryWhereField:@"baz"
  795. isEqualTo:@42];
  796. XCTAssertNotEqualObjects(query, query3);
  797. FIRFirestore *otherFirestore = [self firestore];
  798. FIRQuery *query4 = [[[otherFirestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"]
  799. queryWhereField:@"baz"
  800. isEqualTo:@42];
  801. XCTAssertNotEqualObjects(query, query4);
  802. }
  803. - (void)testCanTraverseCollectionsAndDocuments {
  804. NSString *expected = @"a/b/c/d";
  805. // doc path from root Firestore.
  806. XCTAssertEqualObjects([self.db documentWithPath:@"a/b/c/d"].path, expected);
  807. // collection path from root Firestore.
  808. XCTAssertEqualObjects([[self.db collectionWithPath:@"a/b/c"] documentWithPath:@"d"].path,
  809. expected);
  810. // doc path from CollectionReference.
  811. XCTAssertEqualObjects([[self.db collectionWithPath:@"a"] documentWithPath:@"b/c/d"].path,
  812. expected);
  813. // collection path from DocumentReference.
  814. XCTAssertEqualObjects([[self.db documentWithPath:@"a/b"] collectionWithPath:@"c/d/e"].path,
  815. @"a/b/c/d/e");
  816. }
  817. - (void)testCanTraverseCollectionAndDocumentParents {
  818. FIRCollectionReference *collection = [self.db collectionWithPath:@"a/b/c"];
  819. XCTAssertEqualObjects(collection.path, @"a/b/c");
  820. FIRDocumentReference *doc = collection.parent;
  821. XCTAssertEqualObjects(doc.path, @"a/b");
  822. collection = doc.parent;
  823. XCTAssertEqualObjects(collection.path, @"a");
  824. FIRDocumentReference *nilDoc = collection.parent;
  825. XCTAssertNil(nilDoc);
  826. }
  827. - (void)testUpdateFieldsWithDots {
  828. FIRDocumentReference *doc = [self documentRef];
  829. [self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}];
  830. [self updateDocumentRef:doc
  831. data:@{(id)[[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new"}];
  832. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"];
  833. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  834. XCTAssertNil(error);
  835. XCTAssertEqualObjects(snapshot.data, (@{@"a.b" : @"new", @"c.d" : @"old"}));
  836. [expectation fulfill];
  837. }];
  838. [self awaitExpectations];
  839. }
  840. - (void)testUpdateNestedFields {
  841. FIRDocumentReference *doc = [self documentRef];
  842. [self writeDocumentRef:doc
  843. data:@{
  844. @"a" : @{@"b" : @"old"},
  845. @"c" : @{@"d" : @"old"},
  846. @"e" : @{@"f" : @"old"}
  847. }];
  848. [self updateDocumentRef:doc
  849. data:@{
  850. (id) @"a.b" : @"new",
  851. (id)[[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new"
  852. }];
  853. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"];
  854. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  855. XCTAssertNil(error);
  856. XCTAssertEqualObjects(snapshot.data, (@{
  857. @"a" : @{@"b" : @"new"},
  858. @"c" : @{@"d" : @"new"},
  859. @"e" : @{@"f" : @"old"}
  860. }));
  861. [expectation fulfill];
  862. }];
  863. [self awaitExpectations];
  864. }
  865. - (void)testCollectionID {
  866. XCTAssertEqualObjects([self.db collectionWithPath:@"foo"].collectionID, @"foo");
  867. XCTAssertEqualObjects([self.db collectionWithPath:@"foo/bar/baz"].collectionID, @"baz");
  868. }
  869. - (void)testDocumentID {
  870. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar"].documentID, @"bar");
  871. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar/baz/qux"].documentID, @"qux");
  872. }
  873. - (void)testCanQueueWritesWhileOffline {
  874. XCTestExpectation *writeEpectation = [self expectationWithDescription:@"successfull write"];
  875. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"enable network"];
  876. FIRDocumentReference *doc = [self documentRef];
  877. FIRFirestore *firestore = doc.firestore;
  878. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  879. [firestore disableNetworkWithCompletion:^(NSError *error) {
  880. XCTAssertNil(error);
  881. [doc setData:data
  882. completion:^(NSError *error) {
  883. XCTAssertNil(error);
  884. [writeEpectation fulfill];
  885. }];
  886. [firestore enableNetworkWithCompletion:^(NSError *error) {
  887. XCTAssertNil(error);
  888. [networkExpectation fulfill];
  889. }];
  890. }];
  891. [self awaitExpectations];
  892. XCTestExpectation *getExpectation = [self expectationWithDescription:@"successfull get"];
  893. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  894. XCTAssertNil(error);
  895. XCTAssertEqualObjects(snapshot.data, data);
  896. XCTAssertFalse(snapshot.metadata.isFromCache);
  897. [getExpectation fulfill];
  898. }];
  899. [self awaitExpectations];
  900. }
  901. - (void)testCanGetDocumentsWhileOffline {
  902. FIRDocumentReference *doc = [self documentRef];
  903. FIRFirestore *firestore = doc.firestore;
  904. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  905. XCTestExpectation *failExpectation =
  906. [self expectationWithDescription:@"offline read with no cached data"];
  907. XCTestExpectation *onlineExpectation = [self expectationWithDescription:@"online read"];
  908. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network online"];
  909. __weak FIRDocumentReference *weakDoc = doc;
  910. [firestore disableNetworkWithCompletion:^(NSError *error) {
  911. XCTAssertNil(error);
  912. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *, NSError *error) {
  913. XCTAssertNotNil(error);
  914. [failExpectation fulfill];
  915. }];
  916. [doc setData:data
  917. completion:^(NSError *_Nullable error) {
  918. XCTAssertNil(error);
  919. [weakDoc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  920. XCTAssertNil(error);
  921. // Verify that we are not reading from cache.
  922. XCTAssertFalse(snapshot.metadata.isFromCache);
  923. [onlineExpectation fulfill];
  924. }];
  925. }];
  926. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  927. XCTAssertNil(error);
  928. // Verify that we are reading from cache.
  929. XCTAssertTrue(snapshot.metadata.fromCache);
  930. XCTAssertEqualObjects(snapshot.data, data);
  931. [firestore enableNetworkWithCompletion:^(NSError *) {
  932. [networkExpectation fulfill];
  933. }];
  934. }];
  935. }];
  936. [self awaitExpectations];
  937. }
  938. - (void)testWriteStreamReconnectsAfterIdle {
  939. FIRDocumentReference *doc = [self documentRef];
  940. FIRFirestore *firestore = doc.firestore;
  941. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  942. [firestore workerQueue]->RunScheduledOperationsUntil(TimerId::WriteStreamIdle);
  943. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  944. }
  945. - (void)testWatchStreamReconnectsAfterIdle {
  946. FIRDocumentReference *doc = [self documentRef];
  947. FIRFirestore *firestore = doc.firestore;
  948. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  949. [firestore workerQueue]->RunScheduledOperationsUntil(TimerId::ListenStreamIdle);
  950. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  951. }
  952. - (void)testCanDisableNetwork {
  953. FIRDocumentReference *doc = [self documentRef];
  954. FIRFirestore *firestore = doc.firestore;
  955. [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  956. [self awaitExpectations];
  957. [firestore
  958. enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network again"]];
  959. [self awaitExpectations];
  960. [firestore
  961. disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable network"]];
  962. [self awaitExpectations];
  963. [firestore
  964. disableNetworkWithCompletion:[self
  965. completionForExpectationWithName:@"Disable network again"]];
  966. [self awaitExpectations];
  967. [firestore
  968. enableNetworkWithCompletion:[self completionForExpectationWithName:@"Final enable network"]];
  969. [self awaitExpectations];
  970. }
  971. - (void)testClientCallsAfterTerminationFail {
  972. FIRDocumentReference *doc = [self documentRef];
  973. FIRFirestore *firestore = doc.firestore;
  974. [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  975. [self awaitExpectations];
  976. [firestore terminateWithCompletion:[self completionForExpectationWithName:@"Terminate"]];
  977. [self awaitExpectations];
  978. XCTAssertThrowsSpecific(
  979. {
  980. [firestore disableNetworkWithCompletion:^(NSError *){
  981. }];
  982. },
  983. NSException, @"The client has already been terminated.");
  984. }
  985. - (void)testMaintainsPersistenceAfterRestarting {
  986. FIRDocumentReference *doc = [self documentRef];
  987. FIRFirestore *firestore = doc.firestore;
  988. FIRApp *app = firestore.app;
  989. NSString *appName = app.name;
  990. FIROptions *options = app.options;
  991. NSDictionary<NSString *, id> *initialData = @{@"foo" : @"42"};
  992. [self writeDocumentRef:doc data:initialData];
  993. // -clearPersistence() requires Firestore to be terminated. Shutdown FIRApp and remove the
  994. // firestore instance to emulate the way an end user would do this.
  995. [self terminateFirestore:firestore];
  996. [self.firestores removeObject:firestore];
  997. [self deleteApp:app];
  998. // We restart the app with the same name and options to check that the previous instance's
  999. // persistent storage persists its data after restarting. Calling [self firestore] here would
  1000. // create a new instance of firestore, which defeats the purpose of this test.
  1001. [FIRApp configureWithName:appName options:options];
  1002. FIRApp *app2 = [FIRApp appNamed:appName];
  1003. FIRFirestore *firestore2 = [self firestoreWithApp:app2];
  1004. FIRDocumentReference *docRef2 = [firestore2 documentWithPath:doc.path];
  1005. FIRDocumentSnapshot *snap = [self readDocumentForRef:docRef2 source:FIRFirestoreSourceCache];
  1006. XCTAssertTrue(snap.exists);
  1007. }
  1008. - (void)testCanClearPersistenceAfterRestarting {
  1009. FIRDocumentReference *doc = [self documentRef];
  1010. FIRFirestore *firestore = doc.firestore;
  1011. FIRApp *app = firestore.app;
  1012. NSString *appName = app.name;
  1013. FIROptions *options = app.options;
  1014. NSDictionary<NSString *, id> *initialData = @{@"foo" : @"42"};
  1015. [self writeDocumentRef:doc data:initialData];
  1016. // -clearPersistence() requires Firestore to be terminated. Shutdown FIRApp and remove the
  1017. // firestore instance to emulate the way an end user would do this.
  1018. [self terminateFirestore:firestore];
  1019. [self.firestores removeObject:firestore];
  1020. [firestore
  1021. clearPersistenceWithCompletion:[self completionForExpectationWithName:@"ClearPersistence"]];
  1022. [self awaitExpectations];
  1023. [self deleteApp:app];
  1024. // We restart the app with the same name and options to check that the previous instance's
  1025. // persistent storage is actually cleared after the restart. Calling [self firestore] here would
  1026. // create a new instance of firestore, which defeats the purpose of this test.
  1027. [FIRApp configureWithName:appName options:options];
  1028. FIRApp *app2 = [FIRApp appNamed:appName];
  1029. FIRFirestore *firestore2 = [self firestoreWithApp:app2];
  1030. FIRDocumentReference *docRef2 = [firestore2 documentWithPath:doc.path];
  1031. XCTestExpectation *expectation2 = [self expectationWithDescription:@"getData"];
  1032. [docRef2 getDocumentWithSource:FIRFirestoreSourceCache
  1033. completion:^(FIRDocumentSnapshot *, NSError *_Nullable error) {
  1034. XCTAssertNotNil(error);
  1035. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1036. XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable);
  1037. [expectation2 fulfill];
  1038. }];
  1039. [self awaitExpectations];
  1040. }
  1041. - (void)testCanClearPersistenceOnANewFirestoreInstance {
  1042. FIRDocumentReference *doc = [self documentRef];
  1043. FIRFirestore *firestore = doc.firestore;
  1044. FIRApp *app = firestore.app;
  1045. NSString *appName = app.name;
  1046. FIROptions *options = app.options;
  1047. NSDictionary<NSString *, id> *initialData = @{@"foo" : @"42"};
  1048. [self writeDocumentRef:doc data:initialData];
  1049. [firestore terminateWithCompletion:[self completionForExpectationWithName:@"Terminate"]];
  1050. [self.firestores removeObject:firestore];
  1051. [self awaitExpectations];
  1052. [self deleteApp:app];
  1053. // We restart the app with the same name and options to check that the previous instance's
  1054. // persistent storage is actually cleared after the restart. Calling [self firestore] here would
  1055. // create a new instance of firestore, which defeats the purpose of this test.
  1056. [FIRApp configureWithName:appName options:options];
  1057. FIRApp *app2 = [FIRApp appNamed:appName];
  1058. FIRFirestore *firestore2 = [self firestoreWithApp:app2];
  1059. [firestore2
  1060. clearPersistenceWithCompletion:[self completionForExpectationWithName:@"ClearPersistence"]];
  1061. [self awaitExpectations];
  1062. FIRDocumentReference *docRef2 = [firestore2 documentWithPath:doc.path];
  1063. XCTestExpectation *expectation2 = [self expectationWithDescription:@"getData"];
  1064. [docRef2 getDocumentWithSource:FIRFirestoreSourceCache
  1065. completion:^(FIRDocumentSnapshot *, NSError *_Nullable error) {
  1066. XCTAssertNotNil(error);
  1067. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1068. XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable);
  1069. [expectation2 fulfill];
  1070. }];
  1071. [self awaitExpectations];
  1072. }
  1073. - (void)testClearPersistenceWhileRunningFails {
  1074. FIRDocumentReference *doc = [self documentRef];
  1075. FIRFirestore *firestore = doc.firestore;
  1076. [self enableNetwork];
  1077. XCTestExpectation *expectation = [self expectationWithDescription:@"clearPersistence"];
  1078. [firestore clearPersistenceWithCompletion:^(NSError *_Nullable error) {
  1079. XCTAssertNotNil(error);
  1080. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1081. XCTAssertEqual(error.code, FIRFirestoreErrorCodeFailedPrecondition);
  1082. [expectation fulfill];
  1083. }];
  1084. [self awaitExpectations];
  1085. }
  1086. - (void)testRestartFirestoreLeadsToNewInstance {
  1087. FIRApp *app = testutil::AppForUnitTesting(util::MakeString([FSTIntegrationTestCase projectID]));
  1088. FIRFirestore *firestore = [FIRFirestore firestoreForApp:app];
  1089. FIRFirestore *sameInstance = [FIRFirestore firestoreForApp:app];
  1090. firestore.settings = [FSTIntegrationTestCase settings];
  1091. XCTAssertEqual(firestore, sameInstance);
  1092. NSDictionary<NSString *, id> *data =
  1093. @{@"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  1094. [self writeDocumentRef:[firestore documentWithPath:@"abc/123"] data:data];
  1095. [self terminateFirestore:firestore];
  1096. // Create a new instance, check it's a different instance.
  1097. FIRFirestore *newInstance = [FIRFirestore firestoreForApp:app];
  1098. newInstance.settings = [FSTIntegrationTestCase settings];
  1099. XCTAssertNotEqual(firestore, newInstance);
  1100. // New instance still functions.
  1101. FIRDocumentSnapshot *snapshot =
  1102. [self readDocumentForRef:[newInstance documentWithPath:@"abc/123"]];
  1103. XCTAssertTrue([data isEqualToDictionary:[snapshot data]]);
  1104. }
  1105. - (void)testAppDeleteLeadsToFirestoreTermination {
  1106. FIRApp *app = testutil::AppForUnitTesting(util::MakeString([FSTIntegrationTestCase projectID]));
  1107. FIRFirestore *firestore = [FIRFirestore firestoreForApp:app];
  1108. firestore.settings = [FSTIntegrationTestCase settings];
  1109. NSDictionary<NSString *, id> *data =
  1110. @{@"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}};
  1111. [self writeDocumentRef:[firestore documentWithPath:@"abc/123"] data:data];
  1112. [self deleteApp:app];
  1113. XCTAssertTrue(firestore.wrapped->client()->is_terminated());
  1114. }
  1115. - (void)testTerminateCanBeCalledMultipleTimes {
  1116. FIRApp *app = testutil::AppForUnitTesting(util::MakeString([FSTIntegrationTestCase projectID]));
  1117. FIRFirestore *firestore = [FIRFirestore firestoreForApp:app];
  1118. [firestore terminateWithCompletion:[self completionForExpectationWithName:@"Terminate1"]];
  1119. [self awaitExpectations];
  1120. XCTAssertThrowsSpecific(
  1121. {
  1122. [firestore disableNetworkWithCompletion:^(NSError *){
  1123. }];
  1124. },
  1125. NSException, @"The client has already been terminated.");
  1126. [firestore terminateWithCompletion:[self completionForExpectationWithName:@"Terminate2"]];
  1127. [self awaitExpectations];
  1128. XCTAssertThrowsSpecific(
  1129. {
  1130. [firestore enableNetworkWithCompletion:^(NSError *){
  1131. }];
  1132. },
  1133. NSException, @"The client has already been terminated.");
  1134. }
  1135. - (void)testCanRemoveListenerAfterTermination {
  1136. FIRApp *app = testutil::AppForUnitTesting(util::MakeString([FSTIntegrationTestCase projectID]));
  1137. FIRFirestore *firestore = [FIRFirestore firestoreForApp:app];
  1138. firestore.settings = [FSTIntegrationTestCase settings];
  1139. FIRDocumentReference *doc = [[firestore collectionWithPath:@"rooms"] documentWithAutoID];
  1140. FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
  1141. [self writeDocumentRef:doc data:@{}];
  1142. id<FIRListenerRegistration> listenerRegistration =
  1143. [doc addSnapshotListener:[accumulator valueEventHandler]];
  1144. [accumulator awaitEventWithName:@"Snapshot"];
  1145. [firestore terminateWithCompletion:[self completionForExpectationWithName:@"terminate"]];
  1146. [self awaitExpectations];
  1147. // This should proceed without error.
  1148. [listenerRegistration remove];
  1149. // Multiple calls should proceed as well.
  1150. [listenerRegistration remove];
  1151. }
  1152. - (void)testWaitForPendingWritesCompletes {
  1153. FIRDocumentReference *doc = [self documentRef];
  1154. FIRFirestore *firestore = doc.firestore;
  1155. [self disableNetwork];
  1156. [doc setData:@{@"foo" : @"bar"}];
  1157. [firestore waitForPendingWritesWithCompletion:
  1158. [self completionForExpectationWithName:@"Wait for pending writes"]];
  1159. [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
  1160. [self awaitExpectations];
  1161. }
  1162. - (void)testWaitForPendingWritesFailsWhenUserChanges {
  1163. FIRFirestore *firestore = self.db;
  1164. [self disableNetwork];
  1165. // Writes to local to prevent immediate call to the completion of waitForPendingWrites.
  1166. NSDictionary<NSString *, id> *data =
  1167. @{@"owner" : @{@"name" : @"Andy", @"email" : @"abc@example.com"}};
  1168. [[self documentRef] setData:data];
  1169. XCTestExpectation *expectation = [self expectationWithDescription:@"waitForPendingWrites"];
  1170. [firestore waitForPendingWritesWithCompletion:^(NSError *_Nullable error) {
  1171. XCTAssertNotNil(error);
  1172. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  1173. XCTAssertEqual(error.code, FIRFirestoreErrorCodeCancelled);
  1174. [expectation fulfill];
  1175. }];
  1176. [self triggerUserChangeWithUid:@"user-to-fail-pending-writes"];
  1177. [self awaitExpectations];
  1178. }
  1179. - (void)testWaitForPendingWritesCompletesWhenOfflineIfNoPending {
  1180. FIRFirestore *firestore = self.db;
  1181. [self disableNetwork];
  1182. [firestore waitForPendingWritesWithCompletion:
  1183. [self completionForExpectationWithName:@"Wait for pending writes"]];
  1184. [self awaitExpectations];
  1185. }
  1186. @end