FIRDatabaseTests.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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 <XCTest/XCTest.h>
  17. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  18. #import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseReference_Private.h"
  19. #import "FirebaseDatabase/Sources/FIRDatabaseConfig_Private.h"
  20. #import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabase.h"
  21. #import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseReference.h"
  22. #import "FirebaseDatabase/Tests/Helpers/FIRFakeApp.h"
  23. #import "FirebaseDatabase/Tests/Helpers/FMockStorageEngine.h"
  24. #import "FirebaseDatabase/Tests/Helpers/FTestBase.h"
  25. #import "FirebaseDatabase/Tests/Helpers/FTestHelpers.h"
  26. @interface FIRDatabaseTests : FTestBase
  27. @end
  28. static const NSInteger kFErrorCodeWriteCanceled = 3;
  29. static NSString *kFirebaseTestAltNamespace = @"https://foobar.firebaseio.com";
  30. @implementation FIRDatabaseTests
  31. - (void)testFIRDatabaseForNilApp {
  32. XCTAssertThrowsSpecificNamed([FIRDatabase databaseForApp:(FIRApp * _Nonnull) nil], NSException,
  33. @"InvalidFIRApp");
  34. }
  35. - (void)testDatabaseForApp {
  36. FIRDatabase *database = [self databaseForURL:self.databaseURL];
  37. XCTAssertEqualObjects(self.databaseURL, [database reference].URL);
  38. }
  39. - (void)testDatabaseForAppWithInvalidURLs {
  40. XCTAssertThrows([self databaseForURL:@"not-a-url"]);
  41. XCTAssertThrows([self databaseForURL:@"http://x.example.com/paths/are/bad"]);
  42. }
  43. - (void)testDatabaseForAppWithURL {
  44. id app = [[FIRFakeApp alloc] initWithName:@"testDatabaseForAppWithURL"
  45. URL:kFirebaseTestAltNamespace];
  46. FIRDatabase *database = [FIRDatabase databaseForApp:app URL:@"http://foo.bar.com"];
  47. XCTAssertEqualObjects(@"https://foo.bar.com", [database reference].URL);
  48. }
  49. - (void)testDatabaseForAppWithURLAndPort {
  50. id app = [[FIRFakeApp alloc] initWithName:@"testDatabaseForAppWithURLAndPort"
  51. URL:kFirebaseTestAltNamespace];
  52. FIRDatabase *database = [FIRDatabase databaseForApp:app URL:@"http://foo.bar.com:80"];
  53. XCTAssertEqualObjects(@"http://foo.bar.com:80", [database reference].URL);
  54. }
  55. - (void)testDatabaseForAppWithHttpsURL {
  56. id app = [[FIRFakeApp alloc] initWithName:@"testDatabaseForAppWithHttpsURL"
  57. URL:kFirebaseTestAltNamespace];
  58. FIRDatabase *database = [FIRDatabase databaseForApp:app URL:@"https://foo.bar.com"];
  59. XCTAssertEqualObjects(@"https://foo.bar.com", [database reference].URL);
  60. }
  61. - (void)testDatabaseForAppWithProjectId {
  62. id app = [[FIRFakeApp alloc] initWithName:@"testDatabaseForAppWithURL" URL:nil];
  63. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  64. XCTAssertEqualObjects(@"https://fake-project-id-default-rtdb.firebaseio.com",
  65. [database reference].URL);
  66. }
  67. - (void)testDifferentInstanceForAppWithURL {
  68. id app = [[FIRFakeApp alloc] initWithName:@"testDifferentInstanceForAppWithURL"
  69. URL:kFirebaseTestAltNamespace];
  70. FIRDatabase *database1 = [FIRDatabase databaseForApp:app URL:@"https://foo1.bar.com"];
  71. FIRDatabase *database2 = [FIRDatabase databaseForApp:app URL:@"https://foo1.bar.com/"];
  72. FIRDatabase *database3 = [FIRDatabase databaseForApp:app URL:@"https://foo2.bar.com"];
  73. XCTAssertEqual(database1, database2);
  74. XCTAssertNotEqual(database1, database3);
  75. }
  76. - (void)testDatabaseForAppWithInvalidCustomURLs {
  77. id app = [[FIRFakeApp alloc] initWithName:@"testDatabaseForAppWithInvalidCustomURLs"
  78. URL:kFirebaseTestAltNamespace];
  79. XCTAssertThrows([FIRDatabase databaseForApp:app URL:(NSString * _Nonnull) nil]);
  80. XCTAssertThrows([FIRDatabase databaseForApp:app URL:@"not-a-url"]);
  81. XCTAssertThrows([FIRDatabase databaseForApp:app URL:@"http://x.fblocal.com:9000/paths/are/bad"]);
  82. }
  83. - (void)testDeleteDatabase {
  84. // Set up a custom FIRApp with a custom database based on it.
  85. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  86. GCMSenderID:@"gcm_sender_id"];
  87. options.databaseURL = self.databaseURL;
  88. NSString *customAppName = @"MyCustomApp";
  89. [FIRApp configureWithName:customAppName options:options];
  90. FIRApp *customApp = [FIRApp appNamed:customAppName];
  91. FIRDatabase *customDatabase = [FIRDatabase databaseForApp:customApp];
  92. XCTAssertNotNil(customDatabase);
  93. // Delete the custom app and wait for it to be done.
  94. XCTestExpectation *customAppDeletedExpectation =
  95. [self expectationWithDescription:@"Deleting the custom app should be successful."];
  96. [customApp deleteApp:^(BOOL success) {
  97. // The app shouldn't exist anymore, ensure that the databaseForApp throws.
  98. XCTAssertThrows([FIRDatabase databaseForApp:[FIRApp appNamed:customAppName]]);
  99. [customAppDeletedExpectation fulfill];
  100. }];
  101. // Wait for the custom app to be deleted.
  102. [self waitForExpectations:@[ customAppDeletedExpectation ] timeout:2];
  103. // Configure the app again, then grab a reference to the database. Assert it's different.
  104. [FIRApp configureWithName:customAppName options:options];
  105. FIRApp *secondCustomApp = [FIRApp appNamed:customAppName];
  106. FIRDatabase *secondCustomDatabase = [FIRDatabase databaseForApp:secondCustomApp];
  107. XCTAssertNotNil(secondCustomDatabase);
  108. XCTAssertNotEqualObjects(customDatabase, secondCustomDatabase);
  109. }
  110. - (void)testReferenceWithPath {
  111. FIRDatabase *db = [self defaultDatabase];
  112. NSString *expectedURL = [NSString stringWithFormat:@"%@/foo", self.databaseURL];
  113. XCTAssertEqualObjects(expectedURL, [db referenceWithPath:@"foo"].URL);
  114. }
  115. - (void)testReferenceFromURLWithEmptyPath {
  116. FIRDatabaseReference *ref = [[self defaultDatabase] referenceFromURL:self.databaseURL];
  117. XCTAssertEqualObjects(self.databaseURL, ref.URL);
  118. }
  119. - (void)testReferenceFromURLWithPath {
  120. NSString *url = [NSString stringWithFormat:@"%@/foo/bar", self.databaseURL];
  121. FIRDatabaseReference *ref = [[self defaultDatabase] referenceFromURL:url];
  122. XCTAssertEqualObjects(url, ref.URL);
  123. }
  124. - (void)testReferenceFromURLWithWrongURL {
  125. NSString *url = [NSString stringWithFormat:@"%@/foo/bar", @"https://foobar.firebaseio.com"];
  126. XCTAssertThrows([[self defaultDatabase] referenceFromURL:url]);
  127. }
  128. - (void)testReferenceEqualityForFIRDatabase {
  129. FIRDatabase *db1 = [self databaseForURL:self.databaseURL name:@"db1"];
  130. FIRDatabase *db2 = [self databaseForURL:self.databaseURL name:@"db2"];
  131. FIRDatabase *altDb = [self databaseForURL:self.databaseURL name:@"altDb"];
  132. FIRDatabase *wrongHostDb = [self databaseForURL:@"http://tests.example.com"];
  133. FIRDatabaseReference *testRef1 = [db1 reference];
  134. FIRDatabaseReference *testRef2 = [db1 referenceWithPath:@"foo"];
  135. FIRDatabaseReference *testRef3 = [altDb reference];
  136. FIRDatabaseReference *testRef4 = [wrongHostDb reference];
  137. FIRDatabaseReference *testRef5 = [db2 reference];
  138. FIRDatabaseReference *testRef6 = [db2 reference];
  139. // Referential equality
  140. XCTAssertTrue(testRef1.database == testRef2.database);
  141. XCTAssertFalse(testRef1.database == testRef3.database);
  142. XCTAssertFalse(testRef1.database == testRef4.database);
  143. XCTAssertFalse(testRef1.database == testRef5.database);
  144. XCTAssertFalse(testRef1.database == testRef6.database);
  145. // references from same FIRDatabase same identical .database references.
  146. XCTAssertTrue(testRef5.database == testRef6.database);
  147. [db1 goOffline];
  148. [db2 goOffline];
  149. [altDb goOffline];
  150. [wrongHostDb goOffline];
  151. }
  152. - (FIRDatabaseReference *)rootRefWithEngine:(id<FStorageEngine>)engine name:(NSString *)name {
  153. FIRDatabaseConfig *config = [FTestHelpers configForName:name];
  154. config.persistenceEnabled = YES;
  155. config.forceStorageEngine = engine;
  156. return [[FTestHelpers databaseForConfig:config] reference];
  157. }
  158. - (void)testPurgeWritesPurgesAllWrites {
  159. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  160. FIRDatabaseReference *ref = [self rootRefWithEngine:engine name:@"purgeWritesPurgesAllWrites"];
  161. FIRDatabase *database = ref.database;
  162. [database goOffline];
  163. [[ref childByAutoId] setValue:@"test-value-1"];
  164. [[ref childByAutoId] setValue:@"test-value-2"];
  165. [[ref childByAutoId] setValue:@"test-value-3"];
  166. [[ref childByAutoId] setValue:@"test-value-4"];
  167. [self waitForEvents:ref];
  168. XCTAssertEqual(engine.userWrites.count, (NSUInteger)4);
  169. [database purgeOutstandingWrites];
  170. [self waitForEvents:ref];
  171. XCTAssertEqual(engine.userWrites.count, (NSUInteger)0);
  172. [database goOnline];
  173. }
  174. - (void)testPurgeWritesAreCanceledInOrder {
  175. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  176. FIRDatabaseReference *ref = [self rootRefWithEngine:engine name:@"purgeWritesAndCanceledInOrder"];
  177. FIRDatabase *database = ref.database;
  178. [database goOffline];
  179. NSMutableArray *order = [NSMutableArray array];
  180. [[ref childByAutoId] setValue:@"test-value-1"
  181. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  182. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  183. [order addObject:@"1"];
  184. }];
  185. [[ref childByAutoId] setValue:@"test-value-2"
  186. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  187. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  188. [order addObject:@"2"];
  189. }];
  190. [[ref childByAutoId] setValue:@"test-value-3"
  191. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  192. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  193. [order addObject:@"3"];
  194. }];
  195. [[ref childByAutoId] setValue:@"test-value-4"
  196. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  197. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  198. [order addObject:@"4"];
  199. }];
  200. [self waitForEvents:ref];
  201. XCTAssertEqual(engine.userWrites.count, (NSUInteger)4);
  202. [database purgeOutstandingWrites];
  203. [self waitForEvents:ref];
  204. XCTAssertEqual(engine.userWrites.count, (NSUInteger)0);
  205. XCTAssertEqualObjects(order, (@[ @"1", @"2", @"3", @"4" ]));
  206. [database goOnline];
  207. }
  208. - (void)testPurgeWritesCancelsOnDisconnects {
  209. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  210. FIRDatabaseReference *ref = [self rootRefWithEngine:engine
  211. name:@"purgeWritesCancelsOnDisconnects"];
  212. FIRDatabase *database = ref.database;
  213. [database goOffline];
  214. NSMutableArray *events = [NSMutableArray array];
  215. [[ref childByAutoId] onDisconnectSetValue:@"test-value-1"
  216. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  217. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  218. [events addObject:@"1"];
  219. }];
  220. [[ref childByAutoId] onDisconnectSetValue:@"test-value-2"
  221. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  222. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  223. [events addObject:@"2"];
  224. }];
  225. [self waitForEvents:ref];
  226. [database purgeOutstandingWrites];
  227. [self waitForEvents:ref];
  228. XCTAssertEqualObjects(events, (@[ @"1", @"2" ]));
  229. }
  230. - (void)testPurgeWritesReraisesEvents {
  231. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  232. FIRDatabaseReference *ref = [[self rootRefWithEngine:engine
  233. name:@"purgeWritesReraiseEvents"] childByAutoId];
  234. FIRDatabase *database = ref.database;
  235. [self waitForCompletionOf:ref
  236. setValue:@{@"foo" : @"foo-value", @"bar" : @{@"qux" : @"qux-value"}}];
  237. NSMutableArray *fooValues = [NSMutableArray array];
  238. NSMutableArray *barQuuValues = [NSMutableArray array];
  239. NSMutableArray *barQuxValues = [NSMutableArray array];
  240. NSMutableArray *cancelOrder = [NSMutableArray array];
  241. [[ref child:@"foo"] observeEventType:FIRDataEventTypeValue
  242. withBlock:^(FIRDataSnapshot *snapshot) {
  243. [fooValues addObject:snapshot.value];
  244. }];
  245. [[ref child:@"bar/quu"] observeEventType:FIRDataEventTypeValue
  246. withBlock:^(FIRDataSnapshot *snapshot) {
  247. [barQuuValues addObject:snapshot.value];
  248. }];
  249. [[ref child:@"bar/qux"] observeEventType:FIRDataEventTypeValue
  250. withBlock:^(FIRDataSnapshot *snapshot) {
  251. [barQuxValues addObject:snapshot.value];
  252. }];
  253. [database goOffline];
  254. [[ref child:@"foo"] setValue:@"new-foo-value"
  255. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  256. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  257. // This should be after we raised events
  258. XCTAssertEqualObjects(fooValues.lastObject, @"foo-value");
  259. [cancelOrder addObject:@"foo-1"];
  260. }];
  261. [[ref child:@"bar"] updateChildValues:@{@"quu" : @"quu-value", @"qux" : @"new-qux-value"}
  262. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  263. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  264. // This should be after we raised events
  265. XCTAssertEqualObjects(barQuxValues.lastObject, @"qux-value");
  266. XCTAssertEqualObjects(barQuuValues.lastObject, [NSNull null]);
  267. [cancelOrder addObject:@"bar"];
  268. }];
  269. [[ref child:@"foo"] setValue:@"newest-foo-value"
  270. withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  271. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  272. // This should be after we raised events
  273. XCTAssertEqualObjects(fooValues.lastObject, @"foo-value");
  274. [cancelOrder addObject:@"foo-2"];
  275. }];
  276. [database purgeOutstandingWrites];
  277. [self waitForEvents:ref];
  278. XCTAssertEqualObjects(cancelOrder, (@[ @"foo-1", @"bar", @"foo-2" ]));
  279. XCTAssertEqualObjects(fooValues,
  280. (@[ @"foo-value", @"new-foo-value", @"newest-foo-value", @"foo-value" ]));
  281. XCTAssertEqualObjects(barQuuValues, (@[ [NSNull null], @"quu-value", [NSNull null] ]));
  282. XCTAssertEqualObjects(barQuxValues, (@[ @"qux-value", @"new-qux-value", @"qux-value" ]));
  283. [database goOnline];
  284. // Make sure we're back online and reconnected again
  285. [self waitForRoundTrip:ref];
  286. // No events should be reraised
  287. XCTAssertEqualObjects(cancelOrder, (@[ @"foo-1", @"bar", @"foo-2" ]));
  288. XCTAssertEqualObjects(fooValues,
  289. (@[ @"foo-value", @"new-foo-value", @"newest-foo-value", @"foo-value" ]));
  290. XCTAssertEqualObjects(barQuuValues, (@[ [NSNull null], @"quu-value", [NSNull null] ]));
  291. XCTAssertEqualObjects(barQuxValues, (@[ @"qux-value", @"new-qux-value", @"qux-value" ]));
  292. }
  293. - (void)testPurgeWritesCancelsTransactions {
  294. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  295. FIRDatabaseReference *ref =
  296. [[self rootRefWithEngine:engine name:@"purgeWritesCancelsTransactions"] childByAutoId];
  297. FIRDatabase *database = ref.database;
  298. NSMutableArray *events = [NSMutableArray array];
  299. [ref observeEventType:FIRDataEventTypeValue
  300. withBlock:^(FIRDataSnapshot *snapshot) {
  301. [events addObject:[NSString stringWithFormat:@"value-%@", snapshot.value]];
  302. }];
  303. // Make sure the first value event is fired
  304. [self waitForRoundTrip:ref];
  305. [database goOffline];
  306. [ref
  307. runTransactionBlock:^FIRTransactionResult *(FIRMutableData *currentData) {
  308. [currentData setValue:@"1"];
  309. return [FIRTransactionResult successWithValue:currentData];
  310. }
  311. andCompletionBlock:^(NSError *error, BOOL committed, FIRDataSnapshot *snapshot) {
  312. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  313. [events addObject:@"cancel-1"];
  314. }];
  315. [ref
  316. runTransactionBlock:^FIRTransactionResult *(FIRMutableData *currentData) {
  317. [currentData setValue:@"2"];
  318. return [FIRTransactionResult successWithValue:currentData];
  319. }
  320. andCompletionBlock:^(NSError *error, BOOL committed, FIRDataSnapshot *snapshot) {
  321. XCTAssertEqual(error.code, kFErrorCodeWriteCanceled);
  322. [events addObject:@"cancel-2"];
  323. }];
  324. [database purgeOutstandingWrites];
  325. [self waitForEvents:ref];
  326. // The order should really be cancel-1 then cancel-2, but meh, to difficult to implement
  327. // currently...
  328. XCTAssertEqualObjects(
  329. events,
  330. (@[ @"value-<null>", @"value-1", @"value-2", @"value-<null>", @"cancel-2", @"cancel-1" ]));
  331. }
  332. - (void)testPersistenceEnabled {
  333. id app = [[FIRFakeApp alloc] initWithName:@"testPersistenceEnabled" URL:self.databaseURL];
  334. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  335. database.persistenceEnabled = YES;
  336. XCTAssertTrue(database.persistenceEnabled);
  337. // Just do a dummy observe that should get null added to the persistent cache.
  338. FIRDatabaseReference *ref = [[database reference] childByAutoId];
  339. [self waitForValueOf:ref toBe:[NSNull null]];
  340. // Now go offline and since null is cached offline, our observer should still complete.
  341. [database goOffline];
  342. [self waitForValueOf:ref toBe:[NSNull null]];
  343. }
  344. - (void)testPersistenceCacheSizeBytes {
  345. id app = [[FIRFakeApp alloc] initWithName:@"testPersistenceCacheSizeBytes" URL:self.databaseURL];
  346. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  347. database.persistenceEnabled = YES;
  348. int oneMegabyte = 1 * 1024 * 1024;
  349. XCTAssertThrows([database setPersistenceCacheSizeBytes:1], @"Cache must be a least 1 MB.");
  350. XCTAssertThrows([database setPersistenceCacheSizeBytes:101 * oneMegabyte],
  351. @"Cache must be less than 100 MB.");
  352. database.persistenceCacheSizeBytes = 2 * oneMegabyte;
  353. XCTAssertEqual(2 * oneMegabyte, database.persistenceCacheSizeBytes);
  354. [database reference]; // Initialize database.
  355. XCTAssertThrows([database setPersistenceCacheSizeBytes:3 * oneMegabyte],
  356. @"Persistence can't be changed after initialization.");
  357. XCTAssertEqual(2 * oneMegabyte, database.persistenceCacheSizeBytes);
  358. }
  359. - (void)testCallbackQueue {
  360. id app = [[FIRFakeApp alloc] initWithName:@"testCallbackQueue" URL:self.databaseURL];
  361. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  362. dispatch_queue_t callbackQueue = dispatch_queue_create("testCallbackQueue", NULL);
  363. database.callbackQueue = callbackQueue;
  364. XCTAssertEqual(callbackQueue, database.callbackQueue);
  365. __block BOOL done = NO;
  366. [database.reference.childByAutoId observeSingleEventOfType:FIRDataEventTypeValue
  367. withBlock:^(FIRDataSnapshot *snapshot) {
  368. if (@available(iOS 10.0, macOS 10.12, *)) {
  369. dispatch_assert_queue(callbackQueue);
  370. } else {
  371. NSAssert(YES, @"Test requires iOS 10");
  372. }
  373. done = YES;
  374. }];
  375. WAIT_FOR(done);
  376. [database goOffline];
  377. }
  378. - (void)testSetEmulatorSettingsCreatesEmulatedReferences {
  379. id app = [[FIRFakeApp alloc] initWithName:@"testSetEmulatorSettingsCreatesEmulatedReferences"
  380. URL:self.databaseURL];
  381. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  382. [database useEmulatorWithHost:@"localhost" port:1111];
  383. NSString *concatenatedHost = @"localhost:1111";
  384. FIRDatabaseReference *reference = [database reference];
  385. NSString *referenceURLString = reference.URL;
  386. XCTAssert([referenceURLString containsString:concatenatedHost]);
  387. }
  388. - (void)testSetEmulatorSettingsThrowsAfterRepoInit {
  389. id app = [[FIRFakeApp alloc] initWithName:@"testSetEmulatorSettingsThrowsAfterRepoInit"
  390. URL:self.databaseURL];
  391. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  392. [database reference]; // initialize database repo
  393. // Emulator can't be set after initialization of the database's repo.
  394. XCTAssertThrows([database useEmulatorWithHost:@"a" port:1]);
  395. }
  396. - (void)testEmulatedDatabaseValidatesOnlyNonCustomURLs {
  397. // Set a non-custom databaseURL
  398. NSString *databaseURL = @"https://test.example.com";
  399. id app = [[FIRFakeApp alloc] initWithName:@"testEmulatedDatabaseValidatesNonCustomURLs0"
  400. URL:databaseURL];
  401. FIRDatabase *database = [FIRDatabase databaseForApp:app];
  402. // Reference should be retrievable without an exception being raised
  403. NSString *referenceURLString = [databaseURL stringByAppendingString:@"/path"];
  404. FIRDatabaseReference *reference = [database referenceFromURL:referenceURLString];
  405. XCTAssertNotNil(reference);
  406. app = [[FIRFakeApp alloc] initWithName:@"testEmulatedDatabaseValidatesNonCustomURLs1"
  407. URL:databaseURL];
  408. database = [FIRDatabase databaseForApp:app];
  409. [database useEmulatorWithHost:@"localhost" port:1111];
  410. // Expect production url creates a valid (emulated) reference.
  411. reference = [database referenceFromURL:referenceURLString];
  412. XCTAssertNotNil(reference);
  413. XCTAssert([reference.URL containsString:@"localhost:1111"]);
  414. // Test emulated url
  415. referenceURLString = @"http://localhost:1111/path";
  416. reference = [database referenceFromURL:referenceURLString];
  417. XCTAssertNotNil(reference);
  418. XCTAssert([reference.URL containsString:@"localhost:1111"]);
  419. // Test non-custom url with different host throws exception
  420. referenceURLString = @"https://test.firebaseio.com/path";
  421. XCTAssertThrows([database referenceFromURL:referenceURLString]);
  422. }
  423. - (FIRDatabase *)defaultDatabase {
  424. return [self databaseForURL:self.databaseURL];
  425. }
  426. - (FIRDatabase *)databaseForURL:(NSString *)url {
  427. NSString *name = [NSString stringWithFormat:@"url:%@", url];
  428. return [self databaseForURL:url name:name];
  429. }
  430. - (FIRDatabase *)databaseForURL:(NSString *)url name:(NSString *)name {
  431. NSString *defaultDatabaseURL = [NSString stringWithFormat:@"url:%@", self.databaseURL];
  432. if ([url isEqualToString:self.databaseURL] && [name isEqualToString:defaultDatabaseURL]) {
  433. // Use the default app for the default URL to avoid getting out of sync with FRepoManager
  434. // when calling ensureRepo during tests that don't create their own FIRFakeApp.
  435. return [FTestHelpers defaultDatabase];
  436. } else {
  437. id app = [[FIRFakeApp alloc] initWithName:name URL:url];
  438. return [FIRDatabase databaseForApp:app];
  439. }
  440. }
  441. @end