FIRDatabaseTests.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  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;
  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. @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)testCanDeleteFieldUsingMerge {
  146. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  147. NSDictionary<NSString *, id> *initialData = @{
  148. @"untouched" : @YES,
  149. @"foo" : @"bar",
  150. @"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}
  151. };
  152. NSDictionary<NSString *, id> *mergeData = @{
  153. @"foo" : [FIRFieldValue fieldValueForDelete],
  154. @"nested" : @{@"foo" : [FIRFieldValue fieldValueForDelete]}
  155. };
  156. [self writeDocumentRef:doc data:initialData];
  157. XCTestExpectation *completed =
  158. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  159. [doc setData:mergeData
  160. options:[FIRSetOptions merge]
  161. completion:^(NSError *error) {
  162. XCTAssertNil(error);
  163. [completed fulfill];
  164. }];
  165. [self awaitExpectations];
  166. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  167. XCTAssertEqual(document[@"untouched"], @YES);
  168. XCTAssertNil(document[@"foo"]);
  169. XCTAssertEqual(document[@"nested.untouched"], @YES);
  170. XCTAssertNil(document[@"nested.foo"]);
  171. }
  172. - (void)testMergeReplacesArrays {
  173. FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  174. NSDictionary<NSString *, id> *initialData = @{
  175. @"untouched" : @YES,
  176. @"data" : @"old",
  177. @"topLevel" : @[ @"old", @"old" ],
  178. @"mapInArray" : @[ @{@"data" : @"old"} ]
  179. };
  180. NSDictionary<NSString *, id> *mergeData =
  181. @{ @"data" : @"new",
  182. @"topLevel" : @[ @"new" ],
  183. @"mapInArray" : @[ @{@"data" : @"new"} ] };
  184. NSDictionary<NSString *, id> *finalData = @{
  185. @"untouched" : @YES,
  186. @"data" : @"new",
  187. @"topLevel" : @[ @"new" ],
  188. @"mapInArray" : @[ @{@"data" : @"new"} ]
  189. };
  190. [self writeDocumentRef:doc data:initialData];
  191. XCTestExpectation *completed =
  192. [self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
  193. [doc setData:mergeData
  194. options:[FIRSetOptions merge]
  195. completion:^(NSError *error) {
  196. XCTAssertNil(error);
  197. [completed fulfill];
  198. }];
  199. [self awaitExpectations];
  200. FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
  201. XCTAssertEqualObjects(document.data, finalData);
  202. }
  203. - (void)testAddingToACollectionYieldsTheCorrectDocumentReference {
  204. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  205. FIRDocumentReference *ref = [coll addDocumentWithData:@{ @"foo" : @1 }];
  206. XCTestExpectation *getCompletion = [self expectationWithDescription:@"getData"];
  207. [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable document,
  208. NSError *_Nullable error) {
  209. XCTAssertNil(error);
  210. XCTAssertEqualObjects(document.data, (@{ @"foo" : @1 }));
  211. [getCompletion fulfill];
  212. }];
  213. [self awaitExpectations];
  214. }
  215. - (void)testListenCanBeCalledMultipleTimes {
  216. FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
  217. FIRDocumentReference *doc = [coll documentWithAutoID];
  218. XCTestExpectation *completed = [self expectationWithDescription:@"multiple addSnapshotListeners"];
  219. __block NSDictionary<NSString *, id> *resultingData;
  220. // Shut the compiler up about strong references to doc.
  221. FIRDocumentReference *__weak weakDoc = doc;
  222. [doc setData:@{@"foo" : @"bar"}
  223. completion:^(NSError *error1) {
  224. XCTAssertNil(error1);
  225. FIRDocumentReference *strongDoc = weakDoc;
  226. [strongDoc addSnapshotListener:^(FIRDocumentSnapshot *snapshot2, NSError *error2) {
  227. XCTAssertNil(error2);
  228. FIRDocumentReference *strongDoc2 = weakDoc;
  229. [strongDoc2 addSnapshotListener:^(FIRDocumentSnapshot *snapshot3, NSError *error3) {
  230. XCTAssertNil(error3);
  231. resultingData = snapshot3.data;
  232. [completed fulfill];
  233. }];
  234. }];
  235. }];
  236. [self awaitExpectations];
  237. XCTAssertEqualObjects(resultingData, @{@"foo" : @"bar"});
  238. }
  239. - (void)testDocumentSnapshotEvents_nonExistent {
  240. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  241. XCTestExpectation *snapshotCompletion = [self expectationWithDescription:@"snapshot"];
  242. __block int callbacks = 0;
  243. id<FIRListenerRegistration> listenerRegistration =
  244. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  245. callbacks++;
  246. if (callbacks == 1) {
  247. XCTAssertNotNil(doc);
  248. XCTAssertFalse(doc.exists);
  249. [snapshotCompletion fulfill];
  250. } else if (callbacks == 2) {
  251. XCTFail("Should not have received this callback");
  252. }
  253. }];
  254. [self awaitExpectations];
  255. [listenerRegistration remove];
  256. }
  257. - (void)testDocumentSnapshotEvents_forAdd {
  258. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  259. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  260. __block XCTestExpectation *dataCompletion;
  261. __block int callbacks = 0;
  262. id<FIRListenerRegistration> listenerRegistration =
  263. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  264. callbacks++;
  265. if (callbacks == 1) {
  266. XCTAssertNotNil(doc);
  267. XCTAssertFalse(doc.exists);
  268. [emptyCompletion fulfill];
  269. } else if (callbacks == 2) {
  270. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  271. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  272. [dataCompletion fulfill];
  273. } else if (callbacks == 3) {
  274. XCTFail("Should not have received this callback");
  275. }
  276. }];
  277. [self awaitExpectations];
  278. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  279. [docRef setData:@{ @"a" : @1 }];
  280. [self awaitExpectations];
  281. [listenerRegistration remove];
  282. }
  283. - (void)testDocumentSnapshotEvents_forAddIncludingMetadata {
  284. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  285. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  286. __block XCTestExpectation *dataCompletion;
  287. __block int callbacks = 0;
  288. FIRDocumentListenOptions *options =
  289. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  290. id<FIRListenerRegistration> listenerRegistration =
  291. [docRef addSnapshotListenerWithOptions:options
  292. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  293. callbacks++;
  294. if (callbacks == 1) {
  295. XCTAssertNotNil(doc);
  296. XCTAssertFalse(doc.exists);
  297. [emptyCompletion fulfill];
  298. } else if (callbacks == 2) {
  299. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  300. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  301. } else if (callbacks == 3) {
  302. XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
  303. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  304. [dataCompletion fulfill];
  305. } else if (callbacks == 4) {
  306. XCTFail("Should not have received this callback");
  307. }
  308. }];
  309. [self awaitExpectations];
  310. dataCompletion = [self expectationWithDescription:@"data snapshot"];
  311. [docRef setData:@{ @"a" : @1 }];
  312. [self awaitExpectations];
  313. [listenerRegistration remove];
  314. }
  315. - (void)testDocumentSnapshotEvents_forChange {
  316. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  317. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  318. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  319. [self writeDocumentRef:docRef data:initialData];
  320. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  321. __block XCTestExpectation *changeCompletion;
  322. __block int callbacks = 0;
  323. id<FIRListenerRegistration> listenerRegistration =
  324. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  325. callbacks++;
  326. if (callbacks == 1) {
  327. XCTAssertEqualObjects(doc.data, initialData);
  328. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  329. [initialCompletion fulfill];
  330. } else if (callbacks == 2) {
  331. XCTAssertEqualObjects(doc.data, changedData);
  332. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  333. [changeCompletion fulfill];
  334. } else if (callbacks == 3) {
  335. XCTFail("Should not have received this callback");
  336. }
  337. }];
  338. [self awaitExpectations];
  339. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  340. [docRef setData:changedData];
  341. [self awaitExpectations];
  342. [listenerRegistration remove];
  343. }
  344. - (void)testDocumentSnapshotEvents_forChangeIncludingMetadata {
  345. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  346. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  347. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  348. [self writeDocumentRef:docRef data:initialData];
  349. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  350. __block XCTestExpectation *changeCompletion;
  351. __block int callbacks = 0;
  352. FIRDocumentListenOptions *options =
  353. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  354. id<FIRListenerRegistration> listenerRegistration =
  355. [docRef addSnapshotListenerWithOptions:options
  356. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  357. callbacks++;
  358. if (callbacks == 1) {
  359. XCTAssertEqualObjects(doc.data, initialData);
  360. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  361. XCTAssertEqual(doc.metadata.isFromCache, YES);
  362. } else if (callbacks == 2) {
  363. XCTAssertEqualObjects(doc.data, initialData);
  364. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  365. XCTAssertEqual(doc.metadata.isFromCache, NO);
  366. [initialCompletion fulfill];
  367. } else if (callbacks == 3) {
  368. XCTAssertEqualObjects(doc.data, changedData);
  369. XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
  370. XCTAssertEqual(doc.metadata.isFromCache, NO);
  371. } else if (callbacks == 4) {
  372. XCTAssertEqualObjects(doc.data, changedData);
  373. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  374. XCTAssertEqual(doc.metadata.isFromCache, NO);
  375. [changeCompletion fulfill];
  376. } else if (callbacks == 5) {
  377. XCTFail("Should not have received this callback");
  378. }
  379. }];
  380. [self awaitExpectations];
  381. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  382. [docRef setData:changedData];
  383. [self awaitExpectations];
  384. [listenerRegistration remove];
  385. }
  386. - (void)testDocumentSnapshotEvents_forDelete {
  387. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  388. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  389. [self writeDocumentRef:docRef data:initialData];
  390. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  391. __block XCTestExpectation *changeCompletion;
  392. __block int callbacks = 0;
  393. id<FIRListenerRegistration> listenerRegistration =
  394. [docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  395. callbacks++;
  396. if (callbacks == 1) {
  397. XCTAssertEqualObjects(doc.data, initialData);
  398. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  399. XCTAssertEqual(doc.metadata.isFromCache, YES);
  400. [initialCompletion fulfill];
  401. } else if (callbacks == 2) {
  402. XCTAssertFalse(doc.exists);
  403. [changeCompletion fulfill];
  404. } else if (callbacks == 3) {
  405. XCTFail("Should not have received this callback");
  406. }
  407. }];
  408. [self awaitExpectations];
  409. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  410. [docRef deleteDocument];
  411. [self awaitExpectations];
  412. [listenerRegistration remove];
  413. }
  414. - (void)testDocumentSnapshotEvents_forDeleteIncludingMetadata {
  415. FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
  416. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  417. [self writeDocumentRef:docRef data:initialData];
  418. FIRDocumentListenOptions *options =
  419. [[FIRDocumentListenOptions options] includeMetadataChanges:YES];
  420. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  421. __block XCTestExpectation *changeCompletion;
  422. __block int callbacks = 0;
  423. id<FIRListenerRegistration> listenerRegistration =
  424. [docRef addSnapshotListenerWithOptions:options
  425. listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
  426. callbacks++;
  427. if (callbacks == 1) {
  428. XCTAssertEqualObjects(doc.data, initialData);
  429. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  430. XCTAssertEqual(doc.metadata.isFromCache, YES);
  431. } else if (callbacks == 2) {
  432. XCTAssertEqualObjects(doc.data, initialData);
  433. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  434. XCTAssertEqual(doc.metadata.isFromCache, NO);
  435. [initialCompletion fulfill];
  436. } else if (callbacks == 3) {
  437. XCTAssertFalse(doc.exists);
  438. XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
  439. XCTAssertEqual(doc.metadata.isFromCache, NO);
  440. [changeCompletion fulfill];
  441. } else if (callbacks == 4) {
  442. XCTFail("Should not have received this callback");
  443. }
  444. }];
  445. [self awaitExpectations];
  446. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  447. [docRef deleteDocument];
  448. [self awaitExpectations];
  449. [listenerRegistration remove];
  450. }
  451. - (void)testQuerySnapshotEvents_forAdd {
  452. FIRCollectionReference *roomsRef = [self collectionRef];
  453. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  454. NSDictionary<NSString *, id> *newData = @{ @"a" : @1 };
  455. XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
  456. __block XCTestExpectation *changeCompletion;
  457. __block int callbacks = 0;
  458. id<FIRListenerRegistration> listenerRegistration =
  459. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  460. callbacks++;
  461. if (callbacks == 1) {
  462. XCTAssertEqual(docSet.count, 0);
  463. [emptyCompletion fulfill];
  464. } else if (callbacks == 2) {
  465. XCTAssertEqual(docSet.count, 1);
  466. XCTAssertEqualObjects(docSet.documents[0].data, newData);
  467. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  468. [changeCompletion fulfill];
  469. } else if (callbacks == 3) {
  470. XCTFail("Should not have received a third callback");
  471. }
  472. }];
  473. [self awaitExpectations];
  474. changeCompletion = [self expectationWithDescription:@"changed snapshot"];
  475. [docRef setData:newData];
  476. [self awaitExpectations];
  477. [listenerRegistration remove];
  478. }
  479. - (void)testQuerySnapshotEvents_forChange {
  480. FIRCollectionReference *roomsRef = [self collectionRef];
  481. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  482. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  483. NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
  484. [self writeDocumentRef:docRef data:initialData];
  485. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  486. __block XCTestExpectation *changeCompletion;
  487. __block int callbacks = 0;
  488. id<FIRListenerRegistration> listenerRegistration =
  489. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  490. callbacks++;
  491. if (callbacks == 1) {
  492. XCTAssertEqual(docSet.count, 1);
  493. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  494. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  495. [initialCompletion fulfill];
  496. } else if (callbacks == 2) {
  497. XCTAssertEqual(docSet.count, 1);
  498. XCTAssertEqualObjects(docSet.documents[0].data, changedData);
  499. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
  500. [changeCompletion fulfill];
  501. } else if (callbacks == 3) {
  502. XCTFail("Should not have received a third callback");
  503. }
  504. }];
  505. [self awaitExpectations];
  506. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  507. [docRef setData:changedData];
  508. [self awaitExpectations];
  509. [listenerRegistration remove];
  510. }
  511. - (void)testQuerySnapshotEvents_forDelete {
  512. FIRCollectionReference *roomsRef = [self collectionRef];
  513. FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
  514. NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
  515. [self writeDocumentRef:docRef data:initialData];
  516. XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
  517. __block XCTestExpectation *changeCompletion;
  518. __block int callbacks = 0;
  519. id<FIRListenerRegistration> listenerRegistration =
  520. [roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
  521. callbacks++;
  522. if (callbacks == 1) {
  523. XCTAssertEqual(docSet.count, 1);
  524. XCTAssertEqualObjects(docSet.documents[0].data, initialData);
  525. XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
  526. [initialCompletion fulfill];
  527. } else if (callbacks == 2) {
  528. XCTAssertEqual(docSet.count, 0);
  529. [changeCompletion fulfill];
  530. } else if (callbacks == 4) {
  531. XCTFail("Should not have received a third callback");
  532. }
  533. }];
  534. [self awaitExpectations];
  535. changeCompletion = [self expectationWithDescription:@"listen for changed data"];
  536. [docRef deleteDocument];
  537. [self awaitExpectations];
  538. [listenerRegistration remove];
  539. }
  540. - (void)testExposesFirestoreOnDocumentReferences {
  541. FIRDocumentReference *doc = [self.db documentWithPath:@"foo/bar"];
  542. XCTAssertEqual(doc.firestore, self.db);
  543. }
  544. - (void)testExposesFirestoreOnQueries {
  545. FIRQuery *q = [[self.db collectionWithPath:@"foo"] queryLimitedTo:5];
  546. XCTAssertEqual(q.firestore, self.db);
  547. }
  548. - (void)testDocumentReferenceEquality {
  549. FIRFirestore *firestore = self.db;
  550. FIRDocumentReference *docRef = [firestore documentWithPath:@"foo/bar"];
  551. XCTAssertEqualObjects([firestore documentWithPath:@"foo/bar"], docRef);
  552. XCTAssertEqualObjects([docRef collectionWithPath:@"blah"].parent, docRef);
  553. XCTAssertNotEqualObjects([firestore documentWithPath:@"foo/BAR"], docRef);
  554. FIRFirestore *otherFirestore = [self firestore];
  555. XCTAssertNotEqualObjects([otherFirestore documentWithPath:@"foo/bar"], docRef);
  556. }
  557. - (void)testQueryReferenceEquality {
  558. FIRFirestore *firestore = self.db;
  559. FIRQuery *query =
  560. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  561. isEqualTo:@42];
  562. FIRQuery *query2 =
  563. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
  564. isEqualTo:@42];
  565. XCTAssertEqualObjects(query, query2);
  566. FIRQuery *query3 =
  567. [[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"BAR"] queryWhereField:@"baz"
  568. isEqualTo:@42];
  569. XCTAssertNotEqualObjects(query, query3);
  570. FIRFirestore *otherFirestore = [self firestore];
  571. FIRQuery *query4 = [[[otherFirestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"]
  572. queryWhereField:@"baz"
  573. isEqualTo:@42];
  574. XCTAssertNotEqualObjects(query, query4);
  575. }
  576. - (void)testCanTraverseCollectionsAndDocuments {
  577. NSString *expected = @"a/b/c/d";
  578. // doc path from root Firestore.
  579. XCTAssertEqualObjects([self.db documentWithPath:@"a/b/c/d"].path, expected);
  580. // collection path from root Firestore.
  581. XCTAssertEqualObjects([[self.db collectionWithPath:@"a/b/c"] documentWithPath:@"d"].path,
  582. expected);
  583. // doc path from CollectionReference.
  584. XCTAssertEqualObjects([[self.db collectionWithPath:@"a"] documentWithPath:@"b/c/d"].path,
  585. expected);
  586. // collection path from DocumentReference.
  587. XCTAssertEqualObjects([[self.db documentWithPath:@"a/b"] collectionWithPath:@"c/d/e"].path,
  588. @"a/b/c/d/e");
  589. }
  590. - (void)testCanTraverseCollectionAndDocumentParents {
  591. FIRCollectionReference *collection = [self.db collectionWithPath:@"a/b/c"];
  592. XCTAssertEqualObjects(collection.path, @"a/b/c");
  593. FIRDocumentReference *doc = collection.parent;
  594. XCTAssertEqualObjects(doc.path, @"a/b");
  595. collection = doc.parent;
  596. XCTAssertEqualObjects(collection.path, @"a");
  597. FIRDocumentReference *nilDoc = collection.parent;
  598. XCTAssertNil(nilDoc);
  599. }
  600. - (void)testUpdateFieldsWithDots {
  601. FIRDocumentReference *doc = [self documentRef];
  602. [self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}];
  603. [self updateDocumentRef:doc data:@{ [[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new" }];
  604. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"];
  605. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  606. XCTAssertNil(error);
  607. XCTAssertEqualObjects(snapshot.data, (@{@"a.b" : @"new", @"c.d" : @"old"}));
  608. [expectation fulfill];
  609. }];
  610. [self awaitExpectations];
  611. }
  612. - (void)testUpdateNestedFields {
  613. FIRDocumentReference *doc = [self documentRef];
  614. [self writeDocumentRef:doc
  615. data:@{
  616. @"a" : @{@"b" : @"old"},
  617. @"c" : @{@"d" : @"old"},
  618. @"e" : @{@"f" : @"old"}
  619. }];
  620. [self updateDocumentRef:doc
  621. data:@{
  622. @"a.b" : @"new",
  623. [[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new"
  624. }];
  625. XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"];
  626. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  627. XCTAssertNil(error);
  628. XCTAssertEqualObjects(snapshot.data, (@{
  629. @"a" : @{@"b" : @"new"},
  630. @"c" : @{@"d" : @"new"},
  631. @"e" : @{@"f" : @"old"}
  632. }));
  633. [expectation fulfill];
  634. }];
  635. [self awaitExpectations];
  636. }
  637. - (void)testCollectionID {
  638. XCTAssertEqualObjects([self.db collectionWithPath:@"foo"].collectionID, @"foo");
  639. XCTAssertEqualObjects([self.db collectionWithPath:@"foo/bar/baz"].collectionID, @"baz");
  640. }
  641. - (void)testDocumentID {
  642. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar"].documentID, @"bar");
  643. XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar/baz/qux"].documentID, @"qux");
  644. }
  645. - (void)testCanQueueWritesWhileOffline {
  646. XCTestExpectation *writeEpectation = [self expectationWithDescription:@"successfull write"];
  647. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"enable network"];
  648. FIRDocumentReference *doc = [self documentRef];
  649. FIRFirestore *firestore = doc.firestore;
  650. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  651. [firestore.client disableNetworkWithCompletion:^(NSError *error) {
  652. XCTAssertNil(error);
  653. [doc setData:data
  654. completion:^(NSError *error) {
  655. XCTAssertNil(error);
  656. [writeEpectation fulfill];
  657. }];
  658. [firestore.client enableNetworkWithCompletion:^(NSError *error) {
  659. XCTAssertNil(error);
  660. [networkExpectation fulfill];
  661. }];
  662. }];
  663. [self awaitExpectations];
  664. XCTestExpectation *getExpectation = [self expectationWithDescription:@"successfull get"];
  665. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  666. XCTAssertNil(error);
  667. XCTAssertEqualObjects(snapshot.data, data);
  668. XCTAssertFalse(snapshot.metadata.isFromCache);
  669. [getExpectation fulfill];
  670. }];
  671. [self awaitExpectations];
  672. }
  673. - (void)testCantGetDocumentsWhileOffline {
  674. FIRDocumentReference *doc = [self documentRef];
  675. FIRFirestore *firestore = doc.firestore;
  676. NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
  677. XCTestExpectation *onlineExpectation = [self expectationWithDescription:@"online read"];
  678. XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network online"];
  679. __weak FIRDocumentReference *weakDoc = doc;
  680. [firestore.client disableNetworkWithCompletion:^(NSError *error) {
  681. XCTAssertNil(error);
  682. [doc setData:data
  683. completion:^(NSError *_Nullable error) {
  684. XCTAssertNil(error);
  685. [weakDoc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  686. XCTAssertNil(error);
  687. // Verify that we are not reading from cache.
  688. XCTAssertFalse(snapshot.metadata.isFromCache);
  689. [onlineExpectation fulfill];
  690. }];
  691. }];
  692. [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
  693. XCTAssertNil(error);
  694. // Verify that we are reading from cache.
  695. XCTAssertTrue(snapshot.metadata.fromCache);
  696. XCTAssertEqualObjects(snapshot.data, data);
  697. [firestore.client enableNetworkWithCompletion:^(NSError *error) {
  698. [networkExpectation fulfill];
  699. }];
  700. }];
  701. }];
  702. [self awaitExpectations];
  703. }
  704. - (void)testWriteStreamReconnectsAfterIdle {
  705. FIRDocumentReference *doc = [self documentRef];
  706. FIRFirestore *firestore = doc.firestore;
  707. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  708. [self waitForIdleFirestore:firestore];
  709. [self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
  710. }
  711. - (void)testWatchStreamReconnectsAfterIdle {
  712. FIRDocumentReference *doc = [self documentRef];
  713. FIRFirestore *firestore = doc.firestore;
  714. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  715. [self waitForIdleFirestore:firestore];
  716. [self readSnapshotForRef:[self documentRef] requireOnline:YES];
  717. }
  718. @end