FIRDatabaseTests.mm 40 KB

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