FIRDatabaseTests.m 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  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 Firestore;
  17. #import <XCTest/XCTest.h>
  18. #import "Core/FSTFirestoreClient.h"
  19. #import "FIRFirestore+Internal.h"
  20. #import "FSTIntegrationTestCase.h"
  21. @interface FIRDatabaseTests : FSTIntegrationTestCase
  22. @end
  23. @implementation FIRDatabaseTests
  24. - (void)testCanUpdateAnExistingDocument {
  25. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  26. NSDictionary<NSString *, id> *initialData =
  27. @{ @"desc" : @"Description",
  28. @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
  29. NSDictionary<NSString *, id> *updateData =
  30. @{@"desc" : @"NewDescription", @"owner.email" : @"new@xyz.com"};
  31. NSDictionary<NSString *, id> *finalData =
  32. @{ @"desc" : @"NewDescription",
  33. @"owner" : @{@"name" : @"Jonny", @"email" : @"new@xyz.com"} };
  34. [self writeDocumentRef:doc data:initialData];
  35. XCTestExpectation *updateCompletion = [self expectationWithDescription:@"updateData"];
  36. [doc updateData:updateData
  37. completion:^(NSError *_Nullable error) {
  38. XCTAssertNil(error);
  39. [updateCompletion fulfill];
  40. }];
  41. [self awaitExpectations];
  42. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  43. XCTAssertTrue(result.exists);
  44. XCTAssertEqualObjects(result.data, finalData);
  45. }
  46. - (void)testCanDeleteAFieldWithAnUpdate {
  47. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  48. NSDictionary<NSString *, id> *initialData =
  49. @{ @"desc" : @"Description",
  50. @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
  51. NSDictionary<NSString *, id> *updateData =
  52. @{@"owner.email" : [FIRFieldValue fieldValueForDelete]};
  53. NSDictionary<NSString *, id> *finalData =
  54. @{ @"desc" : @"Description",
  55. @"owner" : @{@"name" : @"Jonny"} };
  56. [self writeDocumentRef:doc data:initialData];
  57. [self updateDocumentRef:doc data:updateData];
  58. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  59. XCTAssertTrue(result.exists);
  60. XCTAssertEqualObjects(result.data, finalData);
  61. }
  62. - (void)testDeleteDocument {
  63. FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
  64. NSDictionary<NSString *, id> *data = @{@"value" : @"foo"};
  65. [self writeDocumentRef:doc data:data];
  66. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  67. XCTAssertEqualObjects(result.data, data);
  68. [self deleteDocumentRef:doc];
  69. result = [self readDocumentForRef:doc];
  70. XCTAssertFalse(result.exists);
  71. }
  72. - (void)testCannotUpdateNonexistentDocument {
  73. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  74. XCTestExpectation *setCompletion = [self expectationWithDescription:@"setData"];
  75. [doc updateData:@{@"owner" : @"abc"}
  76. completion:^(NSError *_Nullable error) {
  77. XCTAssertNotNil(error);
  78. XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
  79. XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound);
  80. [setCompletion fulfill];
  81. }];
  82. [self awaitExpectations];
  83. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  84. XCTAssertFalse(result.exists);
  85. }
  86. - (void)testCanOverwriteDataAnExistingDocumentUsingSet {
  87. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  88. NSDictionary<NSString *, id> *initialData =
  89. @{ @"desc" : @"Description",
  90. @"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
  91. NSDictionary<NSString *, id> *udpateData = @{@"desc" : @"NewDescription"};
  92. [self writeDocumentRef:doc data:initialData];
  93. [self writeDocumentRef:doc data:udpateData];
  94. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  95. XCTAssertEqualObjects(document.data, udpateData);
  96. }
  97. - (void)testCanMergeDataWithAnExistingDocumentUsingSet {
  98. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  99. NSDictionary<NSString *, id> *initialData = @{
  100. @"desc" : @"Description",
  101. @"owner.data" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}
  102. };
  103. NSDictionary<NSString *, id> *mergeData =
  104. @{ @"updated" : @YES,
  105. @"owner.data" : @{@"name" : @"Sebastian"} };
  106. NSDictionary<NSString *, id> *finalData = @{
  107. @"desc" : @"Description",
  108. @"updated" : @YES,
  109. @"owner.data" : @{@"name" : @"Sebastian", @"email" : @"abc@xyz.com"}
  110. };
  111. [self writeDocumentRef:doc data:initialData];
  112. XCTestExpectation *completed =
  113. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  114. [doc setData:mergeData
  115. options:[FIRSetOptions merge]
  116. completion:^(NSError *error) {
  117. XCTAssertNil(error);
  118. [completed fulfill];
  119. }];
  120. [self awaitExpectations];
  121. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  122. XCTAssertEqualObjects(document.data, finalData);
  123. }
  124. - (void)testCanMergeServerTimestamps {
  125. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  126. NSDictionary<NSString *, id> *initialData = @{
  127. @"updated" : @NO,
  128. };
  129. NSDictionary<NSString *, id> *mergeData =
  130. @{@"time" : [FIRFieldValue fieldValueForServerTimestamp]};
  131. [self writeDocumentRef:doc data:initialData];
  132. XCTestExpectation *completed =
  133. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  134. [doc setData:mergeData
  135. options:[FIRSetOptions merge]
  136. completion:^(NSError *error) {
  137. XCTAssertNil(error);
  138. [completed fulfill];
  139. }];
  140. [self awaitExpectations];
  141. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  142. XCTAssertEqual(document[@"updated"], @NO);
  143. XCTAssertTrue([document[@"time"] isKindOfClass:[NSDate class]]);
  144. }
  145. - (void)testMergeReplacesArrays {
  146. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  147. NSDictionary<NSString *, id> *initialData = @{
  148. @"untouched" : @YES,
  149. @"data" : @"old",
  150. @"topLevel" : @[ @"old", @"old" ],
  151. @"mapInArray" : @[ @{@"data" : @"old"} ]
  152. };
  153. NSDictionary<NSString *, id> *mergeData =
  154. @{ @"data" : @"new",
  155. @"topLevel" : @[ @"new" ],
  156. @"mapInArray" : @[ @{@"data" : @"new"} ] };
  157. NSDictionary<NSString *, id> *finalData = @{
  158. @"untouched" : @YES,
  159. @"data" : @"new",
  160. @"topLevel" : @[ @"new" ],
  161. @"mapInArray" : @[ @{@"data" : @"new"} ]
  162. };
  163. [self writeDocumentRef:doc data:initialData];
  164. XCTestExpectation *completed =
  165. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  166. [doc setData:mergeData
  167. options:[FIRSetOptions merge]
  168. completion:^(NSError *error) {
  169. XCTAssertNil(error);
  170. [completed fulfill];
  171. }];
  172. [self awaitExpectations];
  173. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  174. XCTAssertEqualObjects(document.data, finalData);
  175. }
  176. - (void)testAddingToACollectionYieldsTheCorrectDocumentReference {
  177. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  178. FIRDocumentReference *ref = [coll addDocumentWithData:@{ @"foo" : @1 }];
  179. XCTestExpectation *getCompletion = [self expectationWithDescription:@"getData"];
  180. [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable document,
  181. NSError *_Nullable error) {
  182. XCTAssertNil(error);
  183. XCTAssertEqualObjects(document.data, (@{ @"foo" : @1 }));
  184. [getCompletion fulfill];
  185. }];
  186. [self awaitExpectations];
  187. }
  188. - (void)testListenCanBeCalledMultipleTimes {
  189. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  190. FIRDocumentReference *doc = [coll documentWithAutoID];
  191. XCTestExpectation *completed = [self expectationWithDescription:@"multiple addSnapshotListeners"];
  192. __block NSDictionary<NSString *, id> *resultingData;
  193. // Shut the compiler up about strong references to doc.
  194. FIRDocumentReference *__weak weakDoc = doc;
  195. [doc setData:@{@"foo" : @"bar"}
  196. completion:^(NSError *error1) {
  197. XCTAssertNil(error1);
  198. FIRDocumentReference *strongDoc = weakDoc;
  199. [strongDoc addSnapshotListener:^(FIRDocumentSnapshot *snapshot2, NSError *error2) {
  200. XCTAssertNil(error2);
  201. FIRDocumentReference *strongDoc2 = weakDoc;
  202. [strongDoc2 addSnapshotListener:^(FIRDocumentSnapshot *snapshot3, NSError *error3) {
  203. XCTAssertNil(error3);
  204. resultingData = snapshot3.data;
  205. [completed fulfill];
  206. }];
  207. }];
  208. }];
  209. [self awaitExpectations];
  210. XCTAssertEqualObjects(resultingData, @{@"foo" : @"bar"});
  211. }
  212. - (void)testDocumentSnapshotEvents_nonExistent {
  213. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  214. XCTestExpectation *snapshotCompletion = [self expectationWithDescription:@"snapshot"];
  215. __block int callbacks = 0;
  216. id<FIRListenerRegistration> listenerRegistration =
  217. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  218. callbacks++;
  219. if (callbacks == 1) {
  220. XCTAssertNotNil(doc);
  221. XCTAssertFalse(doc.exists);
  222. [snapshotCompletion fulfill];
  223. } else if (callbacks == 2) {
  224. XCTFail("Should not have received this callback");
  225. }
  226. }];
  227. [self awaitExpectations];
  228. [listenerRegistration remove];
  229. }
  230. - (void)testDocumentSnapshotEvents_forAdd {
  231. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  232. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  233. __block XCTestExpectation *dataCompletion;
  234. __block int callbacks = 0;
  235. id<FIRListenerRegistration> listenerRegistration =
  236. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  237. callbacks++;
  238. if (callbacks == 1) {
  239. XCTAssertNotNil(doc);
  240. XCTAssertFalse(doc.exists);
  241. [emptyCompletion fulfill];
  242. } else if (callbacks == 2) {
  243. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  244. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  245. [dataCompletion fulfill];
  246. } else if (callbacks == 3) {
  247. XCTFail("Should not have received this callback");
  248. }
  249. }];
  250. [self awaitExpectations];
  251. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  252. [docRef setData:@{ @"a" : @1 }];
  253. [self awaitExpectations];
  254. [listenerRegistration remove];
  255. }
  256. - (void)testDocumentSnapshotEvents_forAddIncludingMetadata {
  257. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  258. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  259. __block XCTestExpectation *dataCompletion;
  260. __block int callbacks = 0;
  261. FIRDocumentListenOptions *options =
  262. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  263. id<FIRListenerRegistration> listenerRegistration =
  264. [docRef addSnapshotListenerWithOptions:options
  265. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  266. callbacks++;
  267. if (callbacks == 1) {
  268. XCTAssertNotNil(doc);
  269. XCTAssertFalse(doc.exists);
  270. [emptyCompletion fulfill];
  271. } else if (callbacks == 2) {
  272. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  273. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  274. } else if (callbacks == 3) {
  275. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  276. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  277. [dataCompletion fulfill];
  278. } else if (callbacks == 4) {
  279. XCTFail("Should not have received this callback");
  280. }
  281. }];
  282. [self awaitExpectations];
  283. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  284. [docRef setData:@{ @"a" : @1 }];
  285. [self awaitExpectations];
  286. [listenerRegistration remove];
  287. }
  288. - (void)testDocumentSnapshotEvents_forChange {
  289. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  290. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  291. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  292. [self writeDocumentRef:docRef data:initialData];
  293. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  294. __block XCTestExpectation *changeCompletion;
  295. __block int callbacks = 0;
  296. id<FIRListenerRegistration> listenerRegistration =
  297. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  298. callbacks++;
  299. if (callbacks == 1) {
  300. XCTAssertEqualObjects(doc.data, initialData);
  301. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  302. [initialCompletion fulfill];
  303. } else if (callbacks == 2) {
  304. XCTAssertEqualObjects(doc.data, changedData);
  305. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  306. [changeCompletion fulfill];
  307. } else if (callbacks == 3) {
  308. XCTFail("Should not have received this callback");
  309. }
  310. }];
  311. [self awaitExpectations];
  312. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  313. [docRef setData:changedData];
  314. [self awaitExpectations];
  315. [listenerRegistration remove];
  316. }
  317. - (void)testDocumentSnapshotEvents_forChangeIncludingMetadata {
  318. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  319. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  320. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  321. [self writeDocumentRef:docRef data:initialData];
  322. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  323. __block XCTestExpectation *changeCompletion;
  324. __block int callbacks = 0;
  325. FIRDocumentListenOptions *options =
  326. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  327. id<FIRListenerRegistration> listenerRegistration =
  328. [docRef addSnapshotListenerWithOptions:options
  329. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  330. callbacks++;
  331. if (callbacks == 1) {
  332. XCTAssertEqualObjects(doc.data, initialData);
  333. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  334. XCTAssertEqual(doc.metadata.isFromCache, YES);
  335. } else if (callbacks == 2) {
  336. XCTAssertEqualObjects(doc.data, initialData);
  337. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  338. XCTAssertEqual(doc.metadata.isFromCache, NO);
  339. [initialCompletion fulfill];
  340. } else if (callbacks == 3) {
  341. XCTAssertEqualObjects(doc.data, changedData);
  342. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  343. XCTAssertEqual(doc.metadata.isFromCache, NO);
  344. } else if (callbacks == 4) {
  345. XCTAssertEqualObjects(doc.data, changedData);
  346. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  347. XCTAssertEqual(doc.metadata.isFromCache, NO);
  348. [changeCompletion fulfill];
  349. } else if (callbacks == 5) {
  350. XCTFail("Should not have received this callback");
  351. }
  352. }];
  353. [self awaitExpectations];
  354. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  355. [docRef setData:changedData];
  356. [self awaitExpectations];
  357. [listenerRegistration remove];
  358. }
  359. - (void)testDocumentSnapshotEvents_forDelete {
  360. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  361. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  362. [self writeDocumentRef:docRef data:initialData];
  363. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  364. __block XCTestExpectation *changeCompletion;
  365. __block int callbacks = 0;
  366. id<FIRListenerRegistration> listenerRegistration =
  367. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  368. callbacks++;
  369. if (callbacks == 1) {
  370. XCTAssertEqualObjects(doc.data, initialData);
  371. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  372. XCTAssertEqual(doc.metadata.isFromCache, YES);
  373. [initialCompletion fulfill];
  374. } else if (callbacks == 2) {
  375. XCTAssertFalse(doc.exists);
  376. [changeCompletion fulfill];
  377. } else if (callbacks == 3) {
  378. XCTFail("Should not have received this callback");
  379. }
  380. }];
  381. [self awaitExpectations];
  382. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  383. [docRef deleteDocument];
  384. [self awaitExpectations];
  385. [listenerRegistration remove];
  386. }
  387. - (void)testDocumentSnapshotEvents_forDeleteIncludingMetadata {
  388. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  389. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  390. [self writeDocumentRef:docRef data:initialData];
  391. FIRDocumentListenOptions *options =
  392. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  393. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  394. __block XCTestExpectation *changeCompletion;
  395. __block int callbacks = 0;
  396. id<FIRListenerRegistration> listenerRegistration =
  397. [docRef addSnapshotListenerWithOptions:options
  398. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  399. callbacks++;
  400. if (callbacks == 1) {
  401. XCTAssertEqualObjects(doc.data, initialData);
  402. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  403. XCTAssertEqual(doc.metadata.isFromCache, YES);
  404. } else if (callbacks == 2) {
  405. XCTAssertEqualObjects(doc.data, initialData);
  406. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  407. XCTAssertEqual(doc.metadata.isFromCache, NO);
  408. [initialCompletion fulfill];
  409. } else if (callbacks == 3) {
  410. XCTAssertFalse(doc.exists);
  411. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  412. XCTAssertEqual(doc.metadata.isFromCache, NO);
  413. [changeCompletion fulfill];
  414. } else if (callbacks == 4) {
  415. XCTFail("Should not have received this callback");
  416. }
  417. }];
  418. [self awaitExpectations];
  419. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  420. [docRef deleteDocument];
  421. [self awaitExpectations];
  422. [listenerRegistration remove];
  423. }
  424. - (void)testQuerySnapshotEvents_forAdd {
  425. FIRCollectionReference *roomsRef = [self collectionRef];
  426. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  427. NSDictionary<NSString *, id> *newData = @{ @"a" : @1 };
  428. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  429. __block XCTestExpectation *changeCompletion;
  430. __block int callbacks = 0;
  431. id<FIRListenerRegistration> listenerRegistration =
  432. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  433. callbacks++;
  434. if (callbacks == 1) {
  435. XCTAssertEqual(docSet.count, 0);
  436. [emptyCompletion fulfill];
  437. } else if (callbacks == 2) {
  438. XCTAssertEqual(docSet.count, 1);
  439. XCTAssertEqualObjects(docSet.documents[0].data, newData);
  440. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  441. [changeCompletion fulfill];
  442. } else if (callbacks == 3) {
  443. XCTFail("Should not have received a third callback");
  444. }
  445. }];
  446. [self awaitExpectations];
  447. changeCompletion = [self expectationWithDescription:@"changed snapshot"];
  448. [docRef setData:newData];
  449. [self awaitExpectations];
  450. [listenerRegistration remove];
  451. }
  452. - (void)testQuerySnapshotEvents_forChange {
  453. FIRCollectionReference *roomsRef = [self collectionRef];
  454. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  455. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  456. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  457. [self writeDocumentRef:docRef data:initialData];
  458. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  459. __block XCTestExpectation *changeCompletion;
  460. __block int callbacks = 0;
  461. id<FIRListenerRegistration> listenerRegistration =
  462. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  463. callbacks++;
  464. if (callbacks == 1) {
  465. XCTAssertEqual(docSet.count, 1);
  466. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  467. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  468. [initialCompletion fulfill];
  469. } else if (callbacks == 2) {
  470. XCTAssertEqual(docSet.count, 1);
  471. XCTAssertEqualObjects(docSet.documents[0].data, changedData);
  472. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  473. [changeCompletion fulfill];
  474. } else if (callbacks == 3) {
  475. XCTFail("Should not have received a third callback");
  476. }
  477. }];
  478. [self awaitExpectations];
  479. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  480. [docRef setData:changedData];
  481. [self awaitExpectations];
  482. [listenerRegistration remove];
  483. }
  484. - (void)testQuerySnapshotEvents_forDelete {
  485. FIRCollectionReference *roomsRef = [self collectionRef];
  486. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  487. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  488. [self writeDocumentRef:docRef data:initialData];
  489. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  490. __block XCTestExpectation *changeCompletion;
  491. __block int callbacks = 0;
  492. id<FIRListenerRegistration> listenerRegistration =
  493. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  494. callbacks++;
  495. if (callbacks == 1) {
  496. XCTAssertEqual(docSet.count, 1);
  497. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  498. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  499. [initialCompletion fulfill];
  500. } else if (callbacks == 2) {
  501. XCTAssertEqual(docSet.count, 0);
  502. [changeCompletion fulfill];
  503. } else if (callbacks == 4) {
  504. XCTFail("Should not have received a third callback");
  505. }
  506. }];
  507. [self awaitExpectations];
  508. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  509. [docRef deleteDocument];
  510. [self awaitExpectations];
  511. [listenerRegistration remove];
  512. }
  513. - (void)testExposesFirestoreOnDocumentReferences {
  514. FIRDocumentReference *doc = [self.db documentWithPath:@"foo/bar"];
  515. XCTAssertEqual(doc.firestore, self.db);
  516. }
  517. - (void)testExposesFirestoreOnQueries {
  518. FIRQuery *q = [[self.db collectionWithPath:@"foo"] queryLimitedTo:5];
  519. XCTAssertEqual(q.firestore, self.db);
  520. }
  521. - (void)testCanTraverseCollectionsAndDocuments {
  522. NSString *expected = @"a/b/c/d";
  523. // doc path from root Firestore.
  524. XCTAssertEqualObjects([self.db documentWithPath:@"a/b/c/d"].path, expected);
  525. // collection path from root Firestore.
  526. XCTAssertEqualObjects([[self.db collectionWithPath:@"a/b/c"] documentWithPath:@"d"].path,
  527. expected);
  528. // doc path from CollectionReference.
  529. XCTAssertEqualObjects([[self.db collectionWithPath:@"a"] documentWithPath:@"b/c/d"].path,
  530. expected);
  531. // collection path from DocumentReference.
  532. XCTAssertEqualObjects([[self.db documentWithPath:@"a/b"] collectionWithPath:@"c/d/e"].path,
  533. @"a/b/c/d/e");
  534. }
  535. - (void)testCanTraverseCollectionAndDocumentParents {
  536. FIRCollectionReference *collection = [self.db collectionWithPath:@"a/b/c"];
  537. XCTAssertEqualObjects(collection.path, @"a/b/c");
  538. FIRDocumentReference *doc = collection.parent;
  539. XCTAssertEqualObjects(doc.path, @"a/b");
  540. collection = doc.parent;
  541. XCTAssertEqualObjects(collection.path, @"a");
  542. FIRDocumentReference *nilDoc = collection.parent;
  543. XCTAssertNil(nilDoc);
  544. }
  545. - (void)testUpdateFieldsWithDots {
  546. FIRDocumentReference *doc = [self documentRef];
  547. [self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}];
  548. [self updateDocumentRef:doc data:@{ [[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new" }];
  549. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"];
  550. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  551. XCTAssertNil(error);
  552. XCTAssertEqualObjects(snapshot.data, (@{@"a.b" : @"new", @"c.d" : @"old"}));
  553. [expectation fulfill];
  554. }];
  555. [self awaitExpectations];
  556. }
  557. - (void)testUpdateNestedFields {
  558. FIRDocumentReference *doc = [self documentRef];
  559. [self writeDocumentRef:doc
  560. data:@{
  561. @"a" : @{@"b" : @"old"},
  562. @"c" : @{@"d" : @"old"},
  563. @"e" : @{@"f" : @"old"}
  564. }];
  565. [self updateDocumentRef:doc
  566. data:@{
  567. @"a.b" : @"new",
  568. [[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new"
  569. }];
  570. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"];
  571. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  572. XCTAssertNil(error);
  573. XCTAssertEqualObjects(snapshot.data, (@{
  574. @"a" : @{@"b" : @"new"},
  575. @"c" : @{@"d" : @"new"},
  576. @"e" : @{@"f" : @"old"}
  577. }));
  578. [expectation fulfill];
  579. }];
  580. [self awaitExpectations];
  581. }
  582. - (void)testCollectionID {
  583. XCTAssertEqualObjects([self.db collectionWithPath:@"foo"].collectionID, @"foo");
  584. XCTAssertEqualObjects([self.db collectionWithPath:@"foo/bar/baz"].collectionID, @"baz");
  585. }
  586. - (void)testDocumentID {
  587. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar"].documentID, @"bar");
  588. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar/baz/qux"].documentID, @"qux");
  589. }
  590. - (void)testCanQueueWritesWhileOffline {
  591. XCTestExpectation *writeEpectation = [self expectationWithDescription:@"successfull write"];
  592. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"enable network"];
  593. FIRDocumentReference *doc = [self documentRef];
  594. FIRFirestore *firestore = doc.firestore;
  595. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  596. [firestore.client disableNetworkWithCompletion:^(NSError *error) {
  597. XCTAssertNil(error);
  598. [doc setData:data
  599. completion:^(NSError *error) {
  600. XCTAssertNil(error);
  601. [writeEpectation fulfill];
  602. }];
  603. [firestore.client enableNetworkWithCompletion:^(NSError *error) {
  604. XCTAssertNil(error);
  605. [networkExpectation fulfill];
  606. }];
  607. }];
  608. [self awaitExpectations];
  609. XCTestExpectation *getExpectation = [self expectationWithDescription:@"successfull get"];
  610. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  611. XCTAssertNil(error);
  612. XCTAssertEqualObjects(snapshot.data, data);
  613. XCTAssertFalse(snapshot.metadata.isFromCache);
  614. [getExpectation fulfill];
  615. }];
  616. [self awaitExpectations];
  617. }
  618. - (void)testCantGetDocumentsWhileOffline {
  619. FIRDocumentReference *doc = [self documentRef];
  620. FIRFirestore *firestore = doc.firestore;
  621. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  622. XCTestExpectation *onlineExpectation = [self expectationWithDescription:@"online read"];
  623. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network online"];
  624. __weak FIRDocumentReference *weakDoc = doc;
  625. [firestore.client disableNetworkWithCompletion:^(NSError *error) {
  626. XCTAssertNil(error);
  627. [doc setData:data
  628. completion:^(NSError *_Nullable error) {
  629. XCTAssertNil(error);
  630. [weakDoc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  631. XCTAssertNil(error);
  632. // Verify that we are not reading from cache.
  633. XCTAssertFalse(snapshot.metadata.isFromCache);
  634. [onlineExpectation fulfill];
  635. }];
  636. }];
  637. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  638. XCTAssertNil(error);
  639. // Verify that we are reading from cache.
  640. XCTAssertTrue(snapshot.metadata.fromCache);
  641. XCTAssertEqualObjects(snapshot.data, data);
  642. [firestore.client enableNetworkWithCompletion:^(NSError *error) {
  643. [networkExpectation fulfill];
  644. }];
  645. }];
  646. }];
  647. [self awaitExpectations];
  648. }
  649. @end