FIRAuthTests.m 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  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 <Foundation/Foundation.h>
  17. #import <XCTest/XCTest.h>
  18. #import "FIRAppInternal.h"
  19. #import "EmailPassword/FIREmailAuthProvider.h"
  20. #import "Google/FIRGoogleAuthProvider.h"
  21. #import "Phone/FIRPhoneAuthCredential.h"
  22. #import "FIRAdditionalUserInfo.h"
  23. #import "FIRAuth_Internal.h"
  24. #import "FIRAuthErrorUtils.h"
  25. #import "FIRAuthDispatcher.h"
  26. #import "FIRAuthGlobalWorkQueue.h"
  27. #import "FIRUser_Internal.h"
  28. #import "FIRAuthBackend.h"
  29. #import "FIRCreateAuthURIRequest.h"
  30. #import "FIRCreateAuthURIResponse.h"
  31. #import "FIRGetAccountInfoRequest.h"
  32. #import "FIRGetAccountInfoResponse.h"
  33. #import "FIRGetOOBConfirmationCodeRequest.h"
  34. #import "FIRGetOOBConfirmationCodeResponse.h"
  35. #import "FIRSecureTokenRequest.h"
  36. #import "FIRSecureTokenResponse.h"
  37. #import "FIRResetPasswordRequest.h"
  38. #import "FIRResetPasswordResponse.h"
  39. #import "FIRSetAccountInfoRequest.h"
  40. #import "FIRSetAccountInfoResponse.h"
  41. #import "FIRSignUpNewUserRequest.h"
  42. #import "FIRSignUpNewUserResponse.h"
  43. #import "FIRVerifyCustomTokenRequest.h"
  44. #import "FIRVerifyCustomTokenResponse.h"
  45. #import "FIRVerifyAssertionRequest.h"
  46. #import "FIRVerifyAssertionResponse.h"
  47. #import "FIRVerifyPasswordRequest.h"
  48. #import "FIRVerifyPasswordResponse.h"
  49. #import "FIRVerifyPhoneNumberRequest.h"
  50. #import "FIRVerifyPhoneNumberResponse.h"
  51. #import "FIRApp+FIRAuthUnitTests.h"
  52. #import "OCMStubRecorder+FIRAuthUnitTests.h"
  53. #import <OCMock/OCMock.h>
  54. #if TARGET_OS_IOS
  55. #import "Phone/FIRPhoneAuthProvider.h"
  56. #endif
  57. /** @var kFirebaseAppName1
  58. @brief A fake Firebase app name.
  59. */
  60. static NSString *const kFirebaseAppName1 = @"FIREBASE_APP_NAME_1";
  61. /** @var kFirebaseAppName2
  62. @brief Another fake Firebase app name.
  63. */
  64. static NSString *const kFirebaseAppName2 = @"FIREBASE_APP_NAME_2";
  65. /** @var kAPIKey
  66. @brief The fake API key.
  67. */
  68. static NSString *const kAPIKey = @"FAKE_API_KEY";
  69. /** @var kAccessToken
  70. @brief The fake access token.
  71. */
  72. static NSString *const kAccessToken = @"ACCESS_TOKEN";
  73. /** @var kNewAccessToken
  74. @brief Another fake access token used to simulate token refreshed via automatic token refresh.
  75. */
  76. NSString *kNewAccessToken = @"NewAccessToken";
  77. /** @var kAccessTokenValidInterval
  78. @brief The time to live for the fake access token.
  79. */
  80. static const NSTimeInterval kAccessTokenTimeToLive = 60 * 60;
  81. /** @var kTestTokenExpirationTimeInterval
  82. @brief The fake time interval that it takes a token to expire.
  83. */
  84. static const NSTimeInterval kTestTokenExpirationTimeInterval = 55 * 60;
  85. /** @var kRefreshToken
  86. @brief The fake refresh token.
  87. */
  88. static NSString *const kRefreshToken = @"REFRESH_TOKEN";
  89. /** @var kEmail
  90. @brief The fake user email.
  91. */
  92. static NSString *const kEmail = @"user@company.com";
  93. /** @var kFakePassword
  94. @brief The fake user password.
  95. */
  96. static NSString *const kFakePassword = @"!@#$%^";
  97. /** @var kPasswordHash
  98. @brief The fake user password hash.
  99. */
  100. static NSString *const kPasswordHash = @"UkVEQUNURUQ=";
  101. /** @var kLocalID
  102. @brief The fake local user ID.
  103. */
  104. static NSString *const kLocalID = @"LOCAL_ID";
  105. /** @var kDisplayName
  106. @brief The fake user display name.
  107. */
  108. static NSString *const kDisplayName = @"User Doe";
  109. /** @var kGoogleUD
  110. @brief The fake user ID under Google Sign-In.
  111. */
  112. static NSString *const kGoogleID = @"GOOGLE_ID";
  113. /** @var kGoogleEmail
  114. @brief The fake user email under Google Sign-In.
  115. */
  116. static NSString *const kGoogleEmail = @"user@gmail.com";
  117. /** @var kGoogleDisplayName
  118. @brief The fake user display name under Google Sign-In.
  119. */
  120. static NSString *const kGoogleDisplayName = @"Google Doe";
  121. /** @var kGoogleAccessToken
  122. @brief The fake access token from Google Sign-In.
  123. */
  124. static NSString *const kGoogleAccessToken = @"GOOGLE_ACCESS_TOKEN";
  125. /** @var kGoogleIDToken
  126. @brief The fake ID token from Google Sign-In.
  127. */
  128. static NSString *const kGoogleIDToken = @"GOOGLE_ID_TOKEN";
  129. /** @var kCustomToken
  130. @brief The fake custom token to sign in.
  131. */
  132. static NSString *const kCustomToken = @"CUSTOM_TOKEN";
  133. /** @var kVerificationCode
  134. @brief Fake verification code used for testing.
  135. */
  136. static NSString *const kVerificationCode = @"12345678";
  137. /** @var kVerificationID
  138. @brief Fake verification ID for testing.
  139. */
  140. static NSString *const kVerificationID = @"55432";
  141. /** @var kExpectationTimeout
  142. @brief The maximum time waiting for expectations to fulfill.
  143. */
  144. static const NSTimeInterval kExpectationTimeout = 1;
  145. /** @var kWaitInterval
  146. @brief The time waiting for background tasks to finish before continue when necessary.
  147. */
  148. static const NSTimeInterval kWaitInterval = .5;
  149. /** @class FIRAuthTests
  150. @brief Tests for @c FIRAuth.
  151. */
  152. @interface FIRAuthTests : XCTestCase
  153. @end
  154. @implementation FIRAuthTests {
  155. /** @var _mockBackend
  156. @brief The mock @c FIRAuthBackendImplementation .
  157. */
  158. id _mockBackend;
  159. /** @var _FIRAuthDispatcherCallback
  160. @brief Used to save a task from FIRAuthDispatcher to be executed later.
  161. */
  162. __block void (^_Nonnull _FIRAuthDispatcherCallback)(void);
  163. }
  164. /** @fn googleProfile
  165. @brief The fake user profile under additional user data in @c FIRVerifyAssertionResponse.
  166. */
  167. + (NSDictionary *)googleProfile {
  168. static NSDictionary *kGoogleProfile = nil;
  169. static dispatch_once_t onceToken;
  170. dispatch_once(&onceToken, ^{
  171. kGoogleProfile = @{
  172. @"iss": @"https://accounts.google.com\\",
  173. @"email": kGoogleEmail,
  174. @"given_name": @"User",
  175. @"family_name": @"Doe"
  176. };
  177. });
  178. return kGoogleProfile;
  179. }
  180. - (void)setUp {
  181. [super setUp];
  182. _mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
  183. [FIRAuthBackend setBackendImplementation:_mockBackend];
  184. [FIRApp resetAppForAuthUnitTests];
  185. // Set FIRAuthDispatcher implementation in order to save the token refresh task for later
  186. // execution.
  187. [[FIRAuthDispatcher sharedInstance]
  188. setDispatchAfterImplementation:^(NSTimeInterval delay,
  189. dispatch_queue_t _Nonnull queue,
  190. void (^task)(void)) {
  191. XCTAssertNotNil(task);
  192. XCTAssert(delay > 0);
  193. XCTAssertEqualObjects(FIRAuthGlobalWorkQueue(), queue);
  194. _FIRAuthDispatcherCallback = task;
  195. }];
  196. }
  197. - (void)tearDown {
  198. [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
  199. [[FIRAuthDispatcher sharedInstance] setDispatchAfterImplementation:nil];
  200. [super tearDown];
  201. }
  202. #pragma mark - Life Cycle Tests
  203. /** @fn testSingleton
  204. @brief Verifies the @c auth method behaves like a singleton.
  205. */
  206. - (void)testSingleton {
  207. FIRAuth *auth1 = [FIRAuth auth];
  208. XCTAssertNotNil(auth1);
  209. FIRAuth *auth2 = [FIRAuth auth];
  210. XCTAssertEqual(auth1, auth2);
  211. }
  212. /** @fn testDefaultAuth
  213. @brief Verifies the @c auth method associates with the default Firebase app.
  214. */
  215. - (void)testDefaultAuth {
  216. FIRAuth *auth1 = [FIRAuth auth];
  217. FIRAuth *auth2 = [FIRAuth authWithApp:[FIRApp defaultApp]];
  218. XCTAssertEqual(auth1, auth2);
  219. XCTAssertEqual(auth1.app, [FIRApp defaultApp]);
  220. }
  221. /** @fn testNilAppException
  222. @brief Verifies the @c auth method raises an exception if the default FIRApp is not configured.
  223. */
  224. - (void)testNilAppException {
  225. [FIRApp resetApps];
  226. XCTAssertThrows([FIRAuth auth]);
  227. }
  228. /** @fn testAppAPIkey
  229. @brief Verifies the API key is correctly copied from @c FIRApp to @c FIRAuth .
  230. */
  231. - (void)testAppAPIkey {
  232. FIRAuth *auth = [FIRAuth auth];
  233. XCTAssertEqualObjects(auth.APIKey, kAPIKey);
  234. }
  235. /** @fn testAppAssociation
  236. @brief Verifies each @c FIRApp instance associates with a @c FIRAuth .
  237. */
  238. - (void)testAppAssociation {
  239. FIRApp *app1 = [self app1];
  240. FIRAuth *auth1 = [FIRAuth authWithApp:app1];
  241. XCTAssertNotNil(auth1);
  242. XCTAssertEqual(auth1.app, app1);
  243. FIRApp *app2 = [self app2];
  244. FIRAuth *auth2 = [FIRAuth authWithApp:app2];
  245. XCTAssertNotNil(auth2);
  246. XCTAssertEqual(auth2.app, app2);
  247. XCTAssertNotEqual(auth1, auth2);
  248. }
  249. /** @fn testLifeCycle
  250. @brief Verifies the life cycle of @c FIRAuth is the same as its associated @c FIRApp .
  251. */
  252. - (void)testLifeCycle {
  253. __weak FIRApp *app;
  254. __weak FIRAuth *auth;
  255. @autoreleasepool {
  256. FIRApp *app1 = [self app1];
  257. app = app1;
  258. auth = [FIRAuth authWithApp:app1];
  259. // Verify that neither the app nor the auth is released yet, i.e., the app owns the auth
  260. // because nothing else retains the auth.
  261. XCTAssertNotNil(app);
  262. XCTAssertNotNil(auth);
  263. }
  264. [self waitForTimeIntervel:kWaitInterval];
  265. // Verify that both the app and the auth are released upon exit of the autorelease pool,
  266. // i.e., the app is the sole owner of the auth.
  267. XCTAssertNil(app);
  268. XCTAssertNil(auth);
  269. }
  270. /** @fn testGetUID
  271. @brief Verifies that FIRApp's getUIDImplementation is correctly set by FIRAuth.
  272. */
  273. - (void)testGetUID {
  274. FIRApp *app = [FIRApp defaultApp];
  275. XCTAssertNotNil(app.getUIDImplementation);
  276. [[FIRAuth auth] signOut:NULL];
  277. XCTAssertNil(app.getUIDImplementation());
  278. [self waitForSignIn];
  279. XCTAssertEqualObjects(app.getUIDImplementation(), kLocalID);
  280. }
  281. #pragma mark - Server API Tests
  282. /** @fn testFetchProvidersForEmailSuccess
  283. @brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call.
  284. */
  285. - (void)testFetchProvidersForEmailSuccess {
  286. NSArray<NSString *> *allProviders =
  287. @[ FIRGoogleAuthProviderID, FIREmailAuthProviderID ];
  288. OCMExpect([_mockBackend createAuthURI:[OCMArg any]
  289. callback:[OCMArg any]])
  290. .andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request,
  291. FIRCreateAuthURIResponseCallback callback) {
  292. XCTAssertEqualObjects(request.identifier, kEmail);
  293. XCTAssertNotNil(request.endpoint);
  294. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  295. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  296. id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]);
  297. OCMStub([mockCreateAuthURIResponse allProviders]).andReturn(allProviders);
  298. callback(mockCreateAuthURIResponse, nil);
  299. });
  300. });
  301. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  302. [[FIRAuth auth] fetchProvidersForEmail:kEmail
  303. completion:^(NSArray<NSString *> *_Nullable providers,
  304. NSError *_Nullable error) {
  305. XCTAssertTrue([NSThread isMainThread]);
  306. XCTAssertEqualObjects(providers, allProviders);
  307. XCTAssertNil(error);
  308. [expectation fulfill];
  309. }];
  310. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  311. OCMVerifyAll(_mockBackend);
  312. }
  313. /** @fn testFetchProvidersForEmailSuccessDeprecatedProviderID
  314. @brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call using the
  315. deprecated FIREmailPasswordAuthProviderID.
  316. */
  317. - (void)testFetchProvidersForEmailSuccessDeprecatedProviderID {
  318. #pragma clang diagnostic push
  319. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  320. NSArray<NSString *> *allProviders =
  321. @[ FIRGoogleAuthProviderID, FIREmailPasswordAuthProviderID ];
  322. #pragma clang diagnostic pop
  323. OCMExpect([_mockBackend createAuthURI:[OCMArg any]
  324. callback:[OCMArg any]])
  325. .andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request,
  326. FIRCreateAuthURIResponseCallback callback) {
  327. XCTAssertEqualObjects(request.identifier, kEmail);
  328. XCTAssertNotNil(request.endpoint);
  329. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  330. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  331. id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]);
  332. OCMStub([mockCreateAuthURIResponse allProviders]).andReturn(allProviders);
  333. callback(mockCreateAuthURIResponse, nil);
  334. });
  335. });
  336. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  337. [[FIRAuth auth] fetchProvidersForEmail:kEmail
  338. completion:^(NSArray<NSString *> *_Nullable providers,
  339. NSError *_Nullable error) {
  340. XCTAssertTrue([NSThread isMainThread]);
  341. XCTAssertEqualObjects(providers, allProviders);
  342. XCTAssertNil(error);
  343. [expectation fulfill];
  344. }];
  345. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  346. OCMVerifyAll(_mockBackend);
  347. }
  348. /** @fn testFetchProvidersForEmailFailure
  349. @brief Tests the flow of a failed @c fetchProvidersForEmail:completion: call.
  350. */
  351. - (void)testFetchProvidersForEmailFailure {
  352. OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]])
  353. .andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]);
  354. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  355. [[FIRAuth auth] fetchProvidersForEmail:kEmail
  356. completion:^(NSArray<NSString *> *_Nullable providers,
  357. NSError *_Nullable error) {
  358. XCTAssertTrue([NSThread isMainThread]);
  359. XCTAssertNil(providers);
  360. XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
  361. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  362. [expectation fulfill];
  363. }];
  364. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  365. OCMVerifyAll(_mockBackend);
  366. }
  367. #if TARGET_OS_IOS
  368. /** @fn testPhoneAuthSuccess
  369. @brief Tests the flow of a successful @c signInWithCredential:completion for phone auth.
  370. */
  371. - (void)testPhoneAuthSuccess {
  372. OCMExpect([_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
  373. .andCallBlock2(^(FIRVerifyPhoneNumberRequest *_Nullable request,
  374. FIRVerifyPhoneNumberResponseCallback callback) {
  375. XCTAssertEqualObjects(request.verificationCode, kVerificationCode);
  376. XCTAssertEqualObjects(request.verificationID, kVerificationID);
  377. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  378. id mockVerifyPhoneResponse = OCMClassMock([FIRVerifyPhoneNumberResponse class]);
  379. [self stubTokensWithMockResponse:mockVerifyPhoneResponse];
  380. callback(mockVerifyPhoneResponse, nil);
  381. });
  382. });
  383. [self expectGetAccountInfo];
  384. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  385. [[FIRAuth auth] signOut:NULL];
  386. FIRAuthCredential *credential =
  387. [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
  388. verificationCode:kVerificationCode];
  389. [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
  390. NSError *_Nullable error) {
  391. XCTAssertTrue([NSThread isMainThread]);
  392. [self assertUser:user];
  393. XCTAssertNil(error);
  394. [expectation fulfill];
  395. }];
  396. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  397. [self assertUser:[FIRAuth auth].currentUser];
  398. OCMVerifyAll(_mockBackend);
  399. }
  400. /** @fn testPhoneAuthMissingVerificationCode
  401. @brief Tests the flow of an unsuccessful @c signInWithCredential:completion for phone auth due
  402. to an empty verification code
  403. */
  404. - (void)testPhoneAuthMissingVerificationCode {
  405. [self expectGetAccountInfo];
  406. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  407. [[FIRAuth auth] signOut:NULL];
  408. FIRAuthCredential *credential =
  409. [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
  410. verificationCode:@""];
  411. [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
  412. NSError *_Nullable error) {
  413. XCTAssertTrue([NSThread isMainThread]);
  414. XCTAssertNil(user);
  415. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationCode);
  416. [expectation fulfill];
  417. }];
  418. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  419. }
  420. /** @fn testPhoneAuthMissingVerificationID
  421. @brief Tests the flow of an unsuccessful @c signInWithCredential:completion for phone auth due
  422. to an empty verification ID.
  423. */
  424. - (void)testPhoneAuthMissingVerificationID {
  425. [self expectGetAccountInfo];
  426. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  427. [[FIRAuth auth] signOut:NULL];
  428. FIRAuthCredential *credential =
  429. [[FIRPhoneAuthProvider provider] credentialWithVerificationID:@""
  430. verificationCode:kVerificationCode];
  431. [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
  432. NSError *_Nullable error) {
  433. XCTAssertTrue([NSThread isMainThread]);
  434. XCTAssertNil(user);
  435. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationID);
  436. [expectation fulfill];
  437. }];
  438. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  439. }
  440. #endif
  441. /** @fn testSignInWithEmailPasswordSuccess
  442. @brief Tests the flow of a successful @c signInWithEmail:password:completion: call.
  443. */
  444. - (void)testSignInWithEmailPasswordSuccess {
  445. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  446. .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
  447. FIRVerifyPasswordResponseCallback callback) {
  448. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  449. XCTAssertEqualObjects(request.email, kEmail);
  450. XCTAssertEqualObjects(request.password, kFakePassword);
  451. XCTAssertTrue(request.returnSecureToken);
  452. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  453. id mockVerifyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
  454. [self stubTokensWithMockResponse:mockVerifyPasswordResponse];
  455. callback(mockVerifyPasswordResponse, nil);
  456. });
  457. });
  458. [self expectGetAccountInfo];
  459. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  460. [[FIRAuth auth] signOut:NULL];
  461. [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
  462. NSError *_Nullable error) {
  463. XCTAssertTrue([NSThread isMainThread]);
  464. [self assertUser:user];
  465. XCTAssertNil(error);
  466. [expectation fulfill];
  467. }];
  468. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  469. [self assertUser:[FIRAuth auth].currentUser];
  470. OCMVerifyAll(_mockBackend);
  471. }
  472. /** @fn testSignInWithEmailPasswordFailure
  473. @brief Tests the flow of a failed @c signInWithEmail:password:completion: call.
  474. */
  475. - (void)testSignInWithEmailPasswordFailure {
  476. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  477. .andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]);
  478. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  479. [[FIRAuth auth] signOut:NULL];
  480. [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
  481. NSError *_Nullable error) {
  482. XCTAssertTrue([NSThread isMainThread]);
  483. XCTAssertNil(user);
  484. XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
  485. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  486. [expectation fulfill];
  487. }];
  488. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  489. XCTAssertNil([FIRAuth auth].currentUser);
  490. OCMVerifyAll(_mockBackend);
  491. }
  492. /** @fn testResetPasswordSuccess
  493. @brief Tests the flow of a successful @c confirmPasswordResetWithCode:newPassword:completion:
  494. call.
  495. */
  496. - (void)testResetPasswordSuccess {
  497. NSString *fakeEmail = @"fakeEmail";
  498. NSString *fakeCode = @"fakeCode";
  499. NSString *fakeNewPassword = @"fakeNewPassword";
  500. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  501. .andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
  502. FIRResetPasswordCallback callback) {
  503. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  504. XCTAssertEqualObjects(request.oobCode, fakeCode);
  505. XCTAssertEqualObjects(request.updatedPassword, fakeNewPassword);
  506. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  507. id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
  508. OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
  509. callback(mockResetPasswordResponse, nil);
  510. });
  511. });
  512. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  513. [[FIRAuth auth] signOut:NULL];
  514. [[FIRAuth auth] confirmPasswordResetWithCode:fakeCode
  515. newPassword:fakeNewPassword
  516. completion:^(NSError *_Nullable error) {
  517. XCTAssertTrue([NSThread isMainThread]);
  518. XCTAssertNil(error);
  519. [expectation fulfill];
  520. }];
  521. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  522. OCMVerifyAll(_mockBackend);
  523. }
  524. /** @fn testResetPasswordFailure
  525. @brief Tests the flow of a failed @c confirmPasswordResetWithCode:newPassword:completion:
  526. call.
  527. */
  528. - (void)testResetPasswordFailure {
  529. NSString *fakeCode = @"fakeCode";
  530. NSString *fakeNewPassword = @"fakeNewPassword";
  531. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  532. ._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
  533. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  534. [[FIRAuth auth] signOut:NULL];
  535. [[FIRAuth auth] confirmPasswordResetWithCode:fakeCode
  536. newPassword:fakeNewPassword
  537. completion:^(NSError *_Nullable error) {
  538. XCTAssertTrue([NSThread isMainThread]);
  539. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
  540. [expectation fulfill];
  541. }];
  542. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  543. OCMVerifyAll(_mockBackend);
  544. }
  545. /** @fn testCheckActionCodeSuccess
  546. @brief Tests the flow of a successful @c checkActionCode:completion call.
  547. */
  548. - (void)testCheckActionCodeSuccess {
  549. NSString *verifyEmailRequestType = @"VERIFY_EMAIL";
  550. NSString *fakeEmail = @"fakeEmail";
  551. NSString *fakeNewEmail = @"fakeNewEmail";
  552. NSString *fakeCode = @"fakeCode";
  553. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  554. .andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
  555. FIRResetPasswordCallback callback) {
  556. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  557. XCTAssertEqualObjects(request.oobCode, fakeCode);
  558. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  559. id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
  560. OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
  561. OCMStub([mockResetPasswordResponse verifiedEmail]).andReturn(fakeNewEmail);
  562. OCMStubRecorder *stub =
  563. OCMStub([(FIRResetPasswordResponse *) mockResetPasswordResponse requestType]);
  564. stub.andReturn(verifyEmailRequestType);
  565. callback(mockResetPasswordResponse, nil);
  566. });
  567. });
  568. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  569. [[FIRAuth auth] checkActionCode:fakeCode completion:^(FIRActionCodeInfo *_Nullable info,
  570. NSError *_Nullable error) {
  571. XCTAssertTrue([NSThread isMainThread]);
  572. XCTAssertNil(error);
  573. XCTAssertEqual(info.operation, FIRActionCodeOperationVerifyEmail);
  574. XCTAssert([fakeNewEmail isEqualToString:[info dataForKey:FIRActionCodeEmailKey]]);
  575. [expectation fulfill];
  576. }];
  577. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  578. OCMVerifyAll(_mockBackend);
  579. }
  580. /** @fn testCheckActionCodeFailure
  581. @brief Tests the flow of a failed @c checkActionCode:completion call.
  582. */
  583. - (void)testCheckActionCodeFailure {
  584. NSString *fakeCode = @"fakeCode";
  585. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  586. ._andDispatchError2([FIRAuthErrorUtils expiredActionCodeErrorWithMessage:nil]);
  587. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  588. [[FIRAuth auth] signOut:NULL];
  589. [[FIRAuth auth] checkActionCode:fakeCode completion:^(FIRActionCodeInfo *_Nullable info,
  590. NSError *_Nullable error) {
  591. XCTAssertTrue([NSThread isMainThread]);
  592. XCTAssertNotNil(error);
  593. XCTAssertEqual(error.code, FIRAuthErrorCodeExpiredActionCode);
  594. [expectation fulfill];
  595. }];
  596. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  597. OCMVerifyAll(_mockBackend);
  598. }
  599. /** @fn testApplyActionCodeSuccess
  600. @brief Tests the flow of a successful @c applyActionCode:completion call.
  601. */
  602. - (void)testApplyActionCodeSuccess {
  603. NSString *fakeCode = @"fakeCode";
  604. OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
  605. .andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request,
  606. FIRSetAccountInfoResponseCallback callback) {
  607. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  608. XCTAssertEqualObjects(request.OOBCode, fakeCode);
  609. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  610. id mockSetAccountInfoResponse = OCMClassMock([FIRSetAccountInfoResponse class]);
  611. callback(mockSetAccountInfoResponse, nil);
  612. });
  613. });
  614. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  615. [[FIRAuth auth] applyActionCode:fakeCode completion:^(NSError *_Nullable error) {
  616. XCTAssertTrue([NSThread isMainThread]);
  617. XCTAssertNil(error);
  618. [expectation fulfill];
  619. }];
  620. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  621. OCMVerifyAll(_mockBackend);
  622. }
  623. /** @fn testApplyActionCodeFailure
  624. @brief Tests the flow of a failed @c checkActionCode:completion call.
  625. */
  626. - (void)testApplyActionCodeFailure {
  627. NSString *fakeCode = @"fakeCode";
  628. OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
  629. ._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
  630. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  631. [[FIRAuth auth] signOut:NULL];
  632. [[FIRAuth auth] applyActionCode:fakeCode completion:^(NSError *_Nullable error) {
  633. XCTAssertTrue([NSThread isMainThread]);
  634. XCTAssertNotNil(error);
  635. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
  636. [expectation fulfill];
  637. }];
  638. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  639. OCMVerifyAll(_mockBackend);
  640. }
  641. /** @fn testVerifyPasswordResetCodeSuccess
  642. @brief Tests the flow of a successful @c verifyPasswordResetCode:completion call.
  643. */
  644. - (void)testVerifyPasswordResetCodeSuccess {
  645. NSString *passwordResetRequestType = @"PASSWORD_RESET";
  646. NSString *fakeEmail = @"fakeEmail";
  647. NSString *fakeCode = @"fakeCode";
  648. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  649. .andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
  650. FIRResetPasswordCallback callback) {
  651. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  652. XCTAssertEqualObjects(request.oobCode, fakeCode);
  653. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  654. id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
  655. OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
  656. OCMStubRecorder *stub =
  657. OCMStub([(FIRResetPasswordResponse *) mockResetPasswordResponse requestType]);
  658. stub.andReturn(passwordResetRequestType);
  659. callback(mockResetPasswordResponse, nil);
  660. });
  661. });
  662. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  663. [[FIRAuth auth] verifyPasswordResetCode:fakeCode completion:^(NSString *_Nullable email,
  664. NSError *_Nullable error) {
  665. XCTAssertTrue([NSThread isMainThread]);
  666. XCTAssertNil(error);
  667. XCTAssertEqual(email, fakeEmail);
  668. [expectation fulfill];
  669. }];
  670. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  671. OCMVerifyAll(_mockBackend);
  672. }
  673. /** @fn testVerifyPasswordResetCodeFailure
  674. @brief Tests the flow of a failed @c verifyPasswordResetCode:completion call.
  675. */
  676. - (void)testVeridyPasswordResetCodeFailure {
  677. NSString *fakeCode = @"fakeCode";
  678. OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
  679. ._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
  680. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  681. [[FIRAuth auth] signOut:NULL];
  682. [[FIRAuth auth] verifyPasswordResetCode:fakeCode completion:^(NSString *_Nullable email,
  683. NSError *_Nullable error) {
  684. XCTAssertTrue([NSThread isMainThread]);
  685. XCTAssertNotNil(error);
  686. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
  687. [expectation fulfill];
  688. }];
  689. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  690. OCMVerifyAll(_mockBackend);
  691. }
  692. /** @fn testSignInWithEmailCredentialSuccess
  693. @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
  694. email-password credential.
  695. */
  696. - (void)testSignInWithEmailCredentialSuccess {
  697. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  698. .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
  699. FIRVerifyPasswordResponseCallback callback) {
  700. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  701. XCTAssertEqualObjects(request.email, kEmail);
  702. XCTAssertEqualObjects(request.password, kFakePassword);
  703. XCTAssertTrue(request.returnSecureToken);
  704. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  705. id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
  706. [self stubTokensWithMockResponse:mockVeriyPasswordResponse];
  707. callback(mockVeriyPasswordResponse, nil);
  708. });
  709. });
  710. [self expectGetAccountInfo];
  711. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  712. [[FIRAuth auth] signOut:NULL];
  713. FIRAuthCredential *emailCredential =
  714. [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
  715. [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
  716. NSError *_Nullable error) {
  717. XCTAssertTrue([NSThread isMainThread]);
  718. [self assertUser:user];
  719. XCTAssertNil(error);
  720. [expectation fulfill];
  721. }];
  722. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  723. [self assertUser:[FIRAuth auth].currentUser];
  724. OCMVerifyAll(_mockBackend);
  725. }
  726. /** @fn testSignInWithEmailCredentialSuccess
  727. @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
  728. email-password credential using the deprecated FIREmailPasswordAuthProvider.
  729. */
  730. - (void)testSignInWithEmailCredentialSuccessWithDepricatedProvider {
  731. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  732. .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
  733. FIRVerifyPasswordResponseCallback callback) {
  734. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  735. XCTAssertEqualObjects(request.email, kEmail);
  736. XCTAssertEqualObjects(request.password, kFakePassword);
  737. XCTAssertTrue(request.returnSecureToken);
  738. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  739. id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
  740. [self stubTokensWithMockResponse:mockVeriyPasswordResponse];
  741. callback(mockVeriyPasswordResponse, nil);
  742. });
  743. });
  744. [self expectGetAccountInfo];
  745. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  746. [[FIRAuth auth] signOut:NULL];
  747. #pragma clang diagnostic push
  748. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  749. FIRAuthCredential *emailCredential =
  750. [FIREmailPasswordAuthProvider credentialWithEmail:kEmail password:kFakePassword];
  751. #pragma clang diagnostic pop
  752. [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
  753. NSError *_Nullable error) {
  754. XCTAssertTrue([NSThread isMainThread]);
  755. [self assertUser:user];
  756. XCTAssertNil(error);
  757. [expectation fulfill];
  758. }];
  759. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  760. [self assertUser:[FIRAuth auth].currentUser];
  761. OCMVerifyAll(_mockBackend);
  762. }
  763. /** @fn testSignInWithEmailCredentialFailure
  764. @brief Tests the flow of a failed @c signInWithCredential:completion: call with an
  765. email-password credential.
  766. */
  767. - (void)testSignInWithEmailCredentialFailure {
  768. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  769. .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
  770. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  771. [[FIRAuth auth] signOut:NULL];
  772. FIRAuthCredential *emailCredential =
  773. [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
  774. [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
  775. NSError *_Nullable error) {
  776. XCTAssertTrue([NSThread isMainThread]);
  777. XCTAssertNil(user);
  778. XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
  779. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  780. [expectation fulfill];
  781. }];
  782. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  783. XCTAssertNil([FIRAuth auth].currentUser);
  784. OCMVerifyAll(_mockBackend);
  785. }
  786. /** @fn testSignInWithEmailCredentialEmptyPassword
  787. @brief Tests the flow of a failed @c signInWithCredential:completion: call with an
  788. email-password credential using an empty password. This error occurs on the client side,
  789. so there is no need to fake an RPC response.
  790. */
  791. - (void)testSignInWithEmailCredentialEmptyPassword {
  792. NSString *emptyString = @"";
  793. [self expectGetAccountInfo];
  794. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  795. [[FIRAuth auth] signOut:NULL];
  796. FIRAuthCredential *emailCredential =
  797. [FIREmailAuthProvider credentialWithEmail:kEmail password:emptyString];
  798. [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
  799. NSError *_Nullable error) {
  800. XCTAssertTrue([NSThread isMainThread]);
  801. XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
  802. [expectation fulfill];
  803. }];
  804. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  805. }
  806. /** @fn testSignInWithGoogleAccountExistsError
  807. @brief Tests the flow of a failed @c signInWithCredential:completion: with a Google credential
  808. where the backend returns a needs @needConfirmation equal to true. An
  809. FIRAuthErrorCodeAccountExistsWithDifferentCredential error should be thrown.
  810. */
  811. - (void)testSignInWithGoogleAccountExistsError {
  812. OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
  813. .andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
  814. FIRVerifyAssertionResponseCallback callback) {
  815. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  816. XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
  817. XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
  818. XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
  819. XCTAssertTrue(request.returnSecureToken);
  820. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  821. id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
  822. OCMStub([mockVeriyAssertionResponse needConfirmation]).andReturn(YES);
  823. OCMStub([mockVeriyAssertionResponse email]).andReturn(kEmail);
  824. [self stubTokensWithMockResponse:mockVeriyAssertionResponse];
  825. callback(mockVeriyAssertionResponse, nil);
  826. });
  827. });
  828. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  829. [[FIRAuth auth] signOut:NULL];
  830. FIRAuthCredential *googleCredential =
  831. [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
  832. [[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
  833. NSError *_Nullable error) {
  834. XCTAssertTrue([NSThread isMainThread]);
  835. XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential);
  836. XCTAssertEqualObjects(error.userInfo[FIRAuthErrorUserInfoEmailKey], kEmail);
  837. [expectation fulfill];
  838. }];
  839. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  840. OCMVerifyAll(_mockBackend);
  841. }
  842. /** @fn testSignInWithGoogleCredentialSuccess
  843. @brief Tests the flow of a successful @c signInWithCredential:completion: call with an
  844. Google Sign-In credential.
  845. */
  846. - (void)testSignInWithGoogleCredentialSuccess {
  847. OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
  848. .andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
  849. FIRVerifyAssertionResponseCallback callback) {
  850. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  851. XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
  852. XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
  853. XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
  854. XCTAssertTrue(request.returnSecureToken);
  855. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  856. id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
  857. OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
  858. OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
  859. OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
  860. OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
  861. [self stubTokensWithMockResponse:mockVeriyAssertionResponse];
  862. callback(mockVeriyAssertionResponse, nil);
  863. });
  864. });
  865. [self expectGetAccountInfoGoogle];
  866. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  867. [[FIRAuth auth] signOut:NULL];
  868. FIRAuthCredential *googleCredential =
  869. [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
  870. [[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
  871. NSError *_Nullable error) {
  872. XCTAssertTrue([NSThread isMainThread]);
  873. [self assertUserGoogle:user];
  874. XCTAssertNil(error);
  875. [expectation fulfill];
  876. }];
  877. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  878. [self assertUserGoogle:[FIRAuth auth].currentUser];
  879. OCMVerifyAll(_mockBackend);
  880. }
  881. /** @fn testSignInAndRetrieveDataWithCredentialSuccess
  882. @brief Tests the flow of a successful @c signInAndRetrieveDataWithCredential:completion: call
  883. with an Google Sign-In credential.
  884. */
  885. - (void)testSignInAndRetrieveDataWithCredentialSuccess {
  886. OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
  887. .andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
  888. FIRVerifyAssertionResponseCallback callback) {
  889. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  890. XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
  891. XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
  892. XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
  893. XCTAssertTrue(request.returnSecureToken);
  894. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  895. id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
  896. OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
  897. OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
  898. OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
  899. OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
  900. OCMStub([mockVeriyAssertionResponse profile]).andReturn([[self class] googleProfile]);
  901. OCMStub([mockVeriyAssertionResponse username]).andReturn(kDisplayName);
  902. [self stubTokensWithMockResponse:mockVeriyAssertionResponse];
  903. callback(mockVeriyAssertionResponse, nil);
  904. });
  905. });
  906. [self expectGetAccountInfoGoogle];
  907. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  908. [[FIRAuth auth] signOut:NULL];
  909. FIRAuthCredential *googleCredential =
  910. [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
  911. [[FIRAuth auth] signInAndRetrieveDataWithCredential:googleCredential
  912. completion:^(FIRAuthDataResult *_Nullable authResult,
  913. NSError *_Nullable error) {
  914. XCTAssertTrue([NSThread isMainThread]);
  915. [self assertUserGoogle:authResult.user];
  916. XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]);
  917. XCTAssertEqualObjects(authResult.additionalUserInfo.username, kDisplayName);
  918. XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRGoogleAuthProviderID);
  919. XCTAssertNil(error);
  920. [expectation fulfill];
  921. }];
  922. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  923. [self assertUserGoogle:[FIRAuth auth].currentUser];
  924. OCMVerifyAll(_mockBackend);
  925. }
  926. /** @fn testSignInWithGoogleCredentialFailure
  927. @brief Tests the flow of a failed @c signInWithCredential:completion: call with an
  928. Google Sign-In credential.
  929. */
  930. - (void)testSignInWithGoogleCredentialFailure {
  931. OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
  932. .andDispatchError2([FIRAuthErrorUtils emailAlreadyInUseErrorWithEmail:kGoogleEmail]);
  933. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  934. [[FIRAuth auth] signOut:NULL];
  935. FIRAuthCredential *googleCredential =
  936. [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
  937. [[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
  938. NSError *_Nullable error) {
  939. XCTAssertTrue([NSThread isMainThread]);
  940. XCTAssertNil(user);
  941. XCTAssertEqual(error.code, FIRAuthErrorCodeEmailAlreadyInUse);
  942. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  943. [expectation fulfill];
  944. }];
  945. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  946. XCTAssertNil([FIRAuth auth].currentUser);
  947. OCMVerifyAll(_mockBackend);
  948. }
  949. /** @fn testSignInAnonymouslySuccess
  950. @brief Tests the flow of a successful @c signInAnonymously:completion: call.
  951. */
  952. - (void)testSignInAnonymouslySuccess {
  953. OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
  954. .andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
  955. FIRSignupNewUserCallback callback) {
  956. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  957. XCTAssertNil(request.email);
  958. XCTAssertNil(request.password);
  959. XCTAssertTrue(request.returnSecureToken);
  960. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  961. id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
  962. [self stubTokensWithMockResponse:mockSignUpNewUserResponse];
  963. callback(mockSignUpNewUserResponse, nil);
  964. });
  965. });
  966. [self expectGetAccountInfoAnonymous];
  967. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  968. [[FIRAuth auth] signOut:NULL];
  969. [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
  970. NSError *_Nullable error) {
  971. XCTAssertTrue([NSThread isMainThread]);
  972. [self assertUserAnonymous:user];
  973. XCTAssertNil(error);
  974. [expectation fulfill];
  975. }];
  976. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  977. [self assertUserAnonymous:[FIRAuth auth].currentUser];
  978. OCMVerifyAll(_mockBackend);
  979. }
  980. /** @fn testSignInAnonymouslyFailure
  981. @brief Tests the flow of a failed @c signInAnonymously:completion: call.
  982. */
  983. - (void)testSignInAnonymouslyFailure {
  984. OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
  985. .andDispatchError2([FIRAuthErrorUtils operationNotAllowedErrorWithMessage:nil]);
  986. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  987. [[FIRAuth auth] signOut:NULL];
  988. [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
  989. NSError *_Nullable error) {
  990. XCTAssertTrue([NSThread isMainThread]);
  991. XCTAssertNil(user);
  992. XCTAssertEqual(error.code, FIRAuthErrorCodeOperationNotAllowed);
  993. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  994. [expectation fulfill];
  995. }];
  996. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  997. XCTAssertNil([FIRAuth auth].currentUser);
  998. OCMVerifyAll(_mockBackend);
  999. }
  1000. /** @fn testSignInWithCustomTokenSuccess
  1001. @brief Tests the flow of a successful @c signInWithCustomToken:completion: call.
  1002. */
  1003. - (void)testSignInWithCustomTokenSuccess {
  1004. OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
  1005. .andCallBlock2(^(FIRVerifyCustomTokenRequest *_Nullable request,
  1006. FIRVerifyCustomTokenResponseCallback callback) {
  1007. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1008. XCTAssertEqualObjects(request.token, kCustomToken);
  1009. XCTAssertTrue(request.returnSecureToken);
  1010. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1011. id mockVeriyCustomTokenResponse = OCMClassMock([FIRVerifyCustomTokenResponse class]);
  1012. [self stubTokensWithMockResponse:mockVeriyCustomTokenResponse];
  1013. callback(mockVeriyCustomTokenResponse, nil);
  1014. });
  1015. });
  1016. [self expectGetAccountInfo];
  1017. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1018. [[FIRAuth auth] signOut:NULL];
  1019. [[FIRAuth auth] signInWithCustomToken:kCustomToken completion:^(FIRUser *_Nullable user,
  1020. NSError *_Nullable error) {
  1021. XCTAssertTrue([NSThread isMainThread]);
  1022. [self assertUser:user];
  1023. XCTAssertNil(error);
  1024. [expectation fulfill];
  1025. }];
  1026. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1027. [self assertUser:[FIRAuth auth].currentUser];
  1028. OCMVerifyAll(_mockBackend);
  1029. }
  1030. /** @fn testSignInWithCustomTokenFailure
  1031. @brief Tests the flow of a failed @c signInWithCustomToken:completion: call.
  1032. */
  1033. - (void)testSignInWithCustomTokenFailure {
  1034. OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
  1035. .andDispatchError2([FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:nil]);
  1036. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1037. [[FIRAuth auth] signOut:NULL];
  1038. [[FIRAuth auth] signInWithCustomToken:kCustomToken completion:^(FIRUser *_Nullable user,
  1039. NSError *_Nullable error) {
  1040. XCTAssertTrue([NSThread isMainThread]);
  1041. XCTAssertNil(user);
  1042. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidCustomToken);
  1043. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  1044. [expectation fulfill];
  1045. }];
  1046. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1047. XCTAssertNil([FIRAuth auth].currentUser);
  1048. OCMVerifyAll(_mockBackend);
  1049. }
  1050. /** @fn testCreateUserWithEmailPasswordSuccess
  1051. @brief Tests the flow of a successful @c createUserWithEmail:password:completion: call.
  1052. */
  1053. - (void)testCreateUserWithEmailPasswordSuccess {
  1054. OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
  1055. .andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
  1056. FIRSignupNewUserCallback callback) {
  1057. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1058. XCTAssertEqualObjects(request.email, kEmail);
  1059. XCTAssertEqualObjects(request.password, kFakePassword);
  1060. XCTAssertTrue(request.returnSecureToken);
  1061. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1062. id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
  1063. [self stubTokensWithMockResponse:mockSignUpNewUserResponse];
  1064. callback(mockSignUpNewUserResponse, nil);
  1065. });
  1066. });
  1067. [self expectGetAccountInfo];
  1068. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1069. [[FIRAuth auth] signOut:NULL];
  1070. [[FIRAuth auth] createUserWithEmail:kEmail
  1071. password:kFakePassword
  1072. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1073. XCTAssertTrue([NSThread isMainThread]);
  1074. [self assertUser:user];
  1075. XCTAssertNil(error);
  1076. [expectation fulfill];
  1077. }];
  1078. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1079. [self assertUser:[FIRAuth auth].currentUser];
  1080. OCMVerifyAll(_mockBackend);
  1081. }
  1082. /** @fn testCreateUserWithEmailPasswordFailure
  1083. @brief Tests the flow of a failed @c createUserWithEmail:password:completion: call.
  1084. */
  1085. - (void)testCreateUserWithEmailPasswordFailure {
  1086. NSString *reason = @"Password shouldn't be a common word.";
  1087. OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
  1088. .andDispatchError2([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:reason]);
  1089. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1090. [[FIRAuth auth] signOut:NULL];
  1091. [[FIRAuth auth] createUserWithEmail:kEmail
  1092. password:kFakePassword
  1093. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1094. XCTAssertTrue([NSThread isMainThread]);
  1095. XCTAssertNil(user);
  1096. XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
  1097. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  1098. XCTAssertEqualObjects(error.userInfo[NSLocalizedFailureReasonErrorKey], reason);
  1099. [expectation fulfill];
  1100. }];
  1101. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1102. XCTAssertNil([FIRAuth auth].currentUser);
  1103. OCMVerifyAll(_mockBackend);
  1104. }
  1105. /** @fn testCreateUserEmptyPasswordFailure
  1106. @brief Tests the flow of a failed @c createUserWithEmail:password:completion: call due to an
  1107. empty password. This error occurs on the client side, so there is no need to fake an RPC
  1108. response.
  1109. */
  1110. - (void)testCreateUserEmptyPasswordFailure {
  1111. [self expectGetAccountInfo];
  1112. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1113. [[FIRAuth auth] signOut:NULL];
  1114. [[FIRAuth auth] createUserWithEmail:kEmail
  1115. password:@""
  1116. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1117. XCTAssertTrue([NSThread isMainThread]);
  1118. XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
  1119. [expectation fulfill];
  1120. }];
  1121. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1122. }
  1123. /** @fn testCreateUserEmptyEmailFailure
  1124. @brief Tests the flow of a failed @c createUserWithEmail:password:completion: call due to an
  1125. empty email adress. This error occurs on the client side, so there is no need to fake an RPC
  1126. response.
  1127. */
  1128. - (void)testCreateUserEmptyEmailFailure {
  1129. [self expectGetAccountInfo];
  1130. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1131. [[FIRAuth auth] signOut:NULL];
  1132. [[FIRAuth auth] createUserWithEmail:@""
  1133. password:kFakePassword
  1134. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1135. XCTAssertTrue([NSThread isMainThread]);
  1136. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingEmail);
  1137. [expectation fulfill];
  1138. }];
  1139. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1140. }
  1141. /** @fn testSendPasswordResetEmailSuccess
  1142. @brief Tests the flow of a successful @c sendPasswordResetWithEmail:completion: call.
  1143. */
  1144. - (void)testSendPasswordResetEmailSuccess {
  1145. OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
  1146. .andCallBlock2(^(FIRGetOOBConfirmationCodeRequest *_Nullable request,
  1147. FIRGetOOBConfirmationCodeResponseCallback callback) {
  1148. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1149. XCTAssertEqualObjects(request.email, kEmail);
  1150. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1151. callback([[FIRGetOOBConfirmationCodeResponse alloc] init], nil);
  1152. });
  1153. });
  1154. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1155. [[FIRAuth auth] sendPasswordResetWithEmail:kEmail completion:^(NSError *_Nullable error) {
  1156. XCTAssertTrue([NSThread isMainThread]);
  1157. XCTAssertNil(error);
  1158. [expectation fulfill];
  1159. }];
  1160. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1161. OCMVerifyAll(_mockBackend);
  1162. }
  1163. /** @fn testSendPasswordResetEmailFailure
  1164. @brief Tests the flow of a failed @c sendPasswordResetWithEmail:completion: call.
  1165. */
  1166. - (void)testSendPasswordResetEmailFailure {
  1167. OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
  1168. .andDispatchError2([FIRAuthErrorUtils appNotAuthorizedError]);
  1169. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1170. [[FIRAuth auth] sendPasswordResetWithEmail:kEmail completion:^(NSError *_Nullable error) {
  1171. XCTAssertTrue([NSThread isMainThread]);
  1172. XCTAssertEqual(error.code, FIRAuthErrorCodeAppNotAuthorized);
  1173. XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
  1174. [expectation fulfill];
  1175. }];
  1176. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1177. OCMVerifyAll(_mockBackend);
  1178. }
  1179. /** @fn testSignOut
  1180. @brief Tests the @c signOut: method.
  1181. */
  1182. - (void)testSignOut {
  1183. [self waitForSignIn];
  1184. // Verify signing out succeeds and clears the current user.
  1185. NSError *error;
  1186. XCTAssertTrue([[FIRAuth auth] signOut:&error]);
  1187. XCTAssertNil([FIRAuth auth].currentUser);
  1188. }
  1189. /** @fn testAuthStateChanges
  1190. @brief Tests @c addAuthStateDidChangeListener: and @c removeAuthStateDidChangeListener: methods.
  1191. */
  1192. - (void)testAuthStateChanges {
  1193. // Set up listener.
  1194. __block XCTestExpectation *expectation;
  1195. __block BOOL shouldHaveUser;
  1196. FIRAuthStateDidChangeListenerBlock listener = ^(FIRAuth *auth, FIRUser *_Nullable user) {
  1197. XCTAssertTrue([NSThread isMainThread]);
  1198. XCTAssertEqual(auth, [FIRAuth auth]);
  1199. XCTAssertEqual(user, [FIRAuth auth].currentUser);
  1200. if (shouldHaveUser) {
  1201. XCTAssertNotNil(user);
  1202. } else {
  1203. XCTAssertNil(user);
  1204. }
  1205. // `expectation` being nil means the listener is not expected to be fired at this moment.
  1206. XCTAssertNotNil(expectation);
  1207. [expectation fulfill];
  1208. };
  1209. [[FIRAuth auth] signOut:NULL];
  1210. [self waitForTimeIntervel:kWaitInterval]; // Wait until dust settled from previous tests.
  1211. // Listener should fire immediately when attached.
  1212. expectation = [self expectationWithDescription:@"initial"];
  1213. shouldHaveUser = NO;
  1214. FIRAuthStateDidChangeListenerHandle handle =
  1215. [[FIRAuth auth] addAuthStateDidChangeListener:listener];
  1216. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1217. // Listener should fire for signing in.
  1218. expectation = [self expectationWithDescription:@"sign-in"];
  1219. shouldHaveUser = YES;
  1220. [self waitForSignIn];
  1221. // Listener should not fire for signing in again.
  1222. shouldHaveUser = YES;
  1223. [self waitForSignIn];
  1224. [self waitForTimeIntervel:kWaitInterval]; // make sure listener is not called
  1225. // Listener should fire for signing out.
  1226. expectation = [self expectationWithDescription:@"sign-out"];
  1227. shouldHaveUser = NO;
  1228. [[FIRAuth auth] signOut:NULL];
  1229. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1230. // Listener should no longer fire once detached.
  1231. expectation = nil;
  1232. [[FIRAuth auth] removeAuthStateDidChangeListener:handle];
  1233. [self waitForSignIn];
  1234. [self waitForTimeIntervel:kWaitInterval]; // make sure listener is no longer called
  1235. }
  1236. /** @fn testIDTokenChanges
  1237. @brief Tests @c addIDTokenDidChangeListener: and @c removeIDTokenDidChangeListener: methods.
  1238. */
  1239. - (void)testIDTokenChanges {
  1240. // Set up listener.
  1241. __block XCTestExpectation *expectation;
  1242. __block BOOL shouldHaveUser;
  1243. FIRIDTokenDidChangeListenerBlock listener = ^(FIRAuth *auth, FIRUser *_Nullable user) {
  1244. XCTAssertTrue([NSThread isMainThread]);
  1245. XCTAssertEqual(auth, [FIRAuth auth]);
  1246. XCTAssertEqual(user, [FIRAuth auth].currentUser);
  1247. if (shouldHaveUser) {
  1248. XCTAssertNotNil(user);
  1249. } else {
  1250. XCTAssertNil(user);
  1251. }
  1252. // `expectation` being nil means the listener is not expected to be fired at this moment.
  1253. XCTAssertNotNil(expectation);
  1254. [expectation fulfill];
  1255. };
  1256. [[FIRAuth auth] signOut:NULL];
  1257. [self waitForTimeIntervel:kWaitInterval]; // Wait until dust settled from previous tests.
  1258. // Listener should fire immediately when attached.
  1259. expectation = [self expectationWithDescription:@"initial"];
  1260. shouldHaveUser = NO;
  1261. FIRIDTokenDidChangeListenerHandle handle =
  1262. [[FIRAuth auth] addIDTokenDidChangeListener:listener];
  1263. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1264. // Listener should fire for signing in.
  1265. expectation = [self expectationWithDescription:@"sign-in"];
  1266. shouldHaveUser = YES;
  1267. [self waitForSignIn];
  1268. // Listener should fire for signing in again as the same user.
  1269. expectation = [self expectationWithDescription:@"sign-in again"];
  1270. shouldHaveUser = YES;
  1271. [self waitForSignIn];
  1272. // Listener should fire for signing out.
  1273. expectation = [self expectationWithDescription:@"sign-out"];
  1274. shouldHaveUser = NO;
  1275. [[FIRAuth auth] signOut:NULL];
  1276. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1277. // Listener should no longer fire once detached.
  1278. expectation = nil;
  1279. [[FIRAuth auth] removeIDTokenDidChangeListener:handle];
  1280. [self waitForSignIn];
  1281. [self waitForTimeIntervel:kWaitInterval]; // make sure listener is no longer called
  1282. }
  1283. #pragma mark - Automatic Token Refresh Tests.
  1284. /** @fn testAutomaticTokenRefresh
  1285. @brief Tests a successful flow to automatically refresh tokens for a signed in user.
  1286. */
  1287. - (void)testAutomaticTokenRefresh {
  1288. [[FIRAuth auth] signOut:NULL];
  1289. // Enable auto refresh
  1290. [self enableAutoTokenRefresh];
  1291. // Sign in a user.
  1292. [self waitForSignIn];
  1293. // Set up expectation for secureToken RPC made by token refresh task.
  1294. [self mockSecureTokenResponseWithError:nil];
  1295. // Verify that the current user's access token is the "old" access token before automatic token
  1296. // refresh.
  1297. XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1298. // Execute saved token refresh task.
  1299. XCTestExpectation *dispatchAfterExpectation =
  1300. [self expectationWithDescription:@"dispatchAfterExpectation"];
  1301. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1302. XCTAssertNotNil(_FIRAuthDispatcherCallback);
  1303. _FIRAuthDispatcherCallback();
  1304. [dispatchAfterExpectation fulfill];
  1305. });
  1306. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1307. // Verify that current user's access token is the "new" access token provided in the mock secure
  1308. // token response during automatic token refresh.
  1309. XCTAssertEqualObjects(kNewAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1310. OCMVerifyAll(_mockBackend);
  1311. }
  1312. /** @fn testAutomaticTokenRefreshInvalidTokenFailure
  1313. @brief Tests an unsuccessful flow to auto refresh tokens with an "invalid token" error.
  1314. This error should cause the user to be signed out.
  1315. */
  1316. - (void)testAutomaticTokenRefreshInvalidTokenFailure {
  1317. [[FIRAuth auth] signOut:NULL];
  1318. // Enable auto refresh
  1319. [self enableAutoTokenRefresh];
  1320. // Sign in a user.
  1321. [self waitForSignIn];
  1322. // Set up expectation for secureToken RPC made by a failed attempt to refresh tokens.
  1323. [self mockSecureTokenResponseWithError:[FIRAuthErrorUtils invalidUserTokenErrorWithMessage:nil]];
  1324. // Verify that current user is still valid.
  1325. XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1326. // Execute saved token refresh task.
  1327. XCTestExpectation *dispatchAfterExpectation =
  1328. [self expectationWithDescription:@"dispatchAfterExpectation"];
  1329. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1330. XCTAssertNotNil(_FIRAuthDispatcherCallback);
  1331. _FIRAuthDispatcherCallback();
  1332. [dispatchAfterExpectation fulfill];
  1333. });
  1334. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1335. //Verify that the user is nil after failed attempt to refresh tokens caused signed out.
  1336. XCTAssertNil([FIRAuth auth].currentUser);
  1337. OCMVerifyAll(_mockBackend);
  1338. }
  1339. /** @fn testAutomaticTokenRefreshRetry
  1340. @brief Tests that a retry is attempted for a automatic token refresh task (which is not due to
  1341. invalid tokens). The initial attempt to refresh the access token fails, but the second
  1342. attempt is successful.
  1343. */
  1344. - (void)testAutomaticTokenRefreshRetry {
  1345. [[FIRAuth auth] signOut:NULL];
  1346. // Enable auto refresh
  1347. [self enableAutoTokenRefresh];
  1348. // Sign in a user.
  1349. [self waitForSignIn];
  1350. // Set up expectation for secureToken RPC made by a failed attempt to refresh tokens.
  1351. [self mockSecureTokenResponseWithError:[NSError errorWithDomain:@"ERROR" code:-1 userInfo:nil]];
  1352. // Execute saved token refresh task.
  1353. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1354. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1355. XCTAssertNotNil(_FIRAuthDispatcherCallback);
  1356. _FIRAuthDispatcherCallback();
  1357. _FIRAuthDispatcherCallback = nil;
  1358. [expectation fulfill];
  1359. });
  1360. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1361. // The old access token should still be the current user's access token and not the new access
  1362. // token (kNewAccessToken).
  1363. XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1364. // Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
  1365. [self mockSecureTokenResponseWithError:nil];
  1366. // Execute saved token refresh task.
  1367. XCTestExpectation *dispatchAfterExpectation =
  1368. [self expectationWithDescription:@"dispatchAfterExpectation"];
  1369. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1370. XCTAssertNotNil(_FIRAuthDispatcherCallback);
  1371. _FIRAuthDispatcherCallback();
  1372. [dispatchAfterExpectation fulfill];
  1373. });
  1374. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1375. // Verify that current user's access token is the "new" access token provided in the mock secure
  1376. // token response during automatic token refresh.
  1377. XCTAssertEqualObjects([FIRAuth auth].currentUser.rawAccessToken, kNewAccessToken);
  1378. OCMVerifyAll(_mockBackend);
  1379. }
  1380. #if TARGET_OS_IOS
  1381. /** @fn testAutomaticTokenRefreshInvalidTokenFailure
  1382. @brief Tests that app foreground notification triggers the scheduling of an automatic token
  1383. refresh task.
  1384. */
  1385. - (void)testAutoRefreshAppForegroundedNotification {
  1386. [[FIRAuth auth] signOut:NULL];
  1387. // Enable auto refresh
  1388. [self enableAutoTokenRefresh];
  1389. // Sign in a user.
  1390. [self waitForSignIn];
  1391. // Post "UIApplicationDidBecomeActiveNotification" to trigger scheduling token refresh task.
  1392. [[NSNotificationCenter defaultCenter]
  1393. postNotificationName:UIApplicationDidBecomeActiveNotification object:nil];
  1394. // Verify that current user is still valid with old access token.
  1395. XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1396. // Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
  1397. [self mockSecureTokenResponseWithError:nil];
  1398. // Execute saved token refresh task.
  1399. XCTestExpectation *dispatchAfterExpectation =
  1400. [self expectationWithDescription:@"dispatchAfterExpectation"];
  1401. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1402. XCTAssertNotNil(_FIRAuthDispatcherCallback);
  1403. _FIRAuthDispatcherCallback();
  1404. [dispatchAfterExpectation fulfill];
  1405. });
  1406. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1407. // Verify that current user is still valid with new access token.
  1408. XCTAssertEqualObjects(kNewAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
  1409. OCMVerifyAll(_mockBackend);
  1410. }
  1411. #endif
  1412. #pragma mark - Helpers
  1413. /** @fn mockSecureTokenResponseWithError:
  1414. @brief Set up expectation for secureToken RPC.
  1415. @param error The error that the mock should return if any.
  1416. */
  1417. - (void)mockSecureTokenResponseWithError:(nullable NSError *)error {
  1418. // Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
  1419. XCTestExpectation *secureTokenResponseExpectation =
  1420. [self expectationWithDescription:@"secureTokenResponseExpectation"];
  1421. OCMExpect([_mockBackend secureToken:[OCMArg any] callback:[OCMArg any]])
  1422. .andCallBlock2(^(FIRSecureTokenRequest *_Nullable request,
  1423. FIRSecureTokenResponseCallback callback) {
  1424. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1425. XCTAssertEqualObjects(request.refreshToken, kRefreshToken);
  1426. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1427. if (error) {
  1428. callback(nil, error);
  1429. [secureTokenResponseExpectation fulfill];
  1430. return;
  1431. }
  1432. id mockSecureTokenResponse = OCMClassMock([FIRSecureTokenResponse class]);
  1433. OCMStub([mockSecureTokenResponse accessToken]).andReturn(kNewAccessToken);
  1434. NSDate *futureDate =
  1435. [[NSDate date] dateByAddingTimeInterval:kTestTokenExpirationTimeInterval];
  1436. OCMStub([mockSecureTokenResponse approximateExpirationDate]).andReturn(futureDate);
  1437. callback(mockSecureTokenResponse, nil);
  1438. [secureTokenResponseExpectation fulfill];
  1439. });
  1440. });
  1441. }
  1442. /** @fn enableAutoTokenRefresh
  1443. @brief Enables automatic token refresh by invoking FIRAuth's implementation of FIRApp's
  1444. |getTokenWithImplementation|.
  1445. */
  1446. - (void)enableAutoTokenRefresh {
  1447. XCTestExpectation *expectation = [self expectationWithDescription:@"autoTokenRefreshcallback"];
  1448. [[FIRAuth auth].app getTokenForcingRefresh:NO withCallback:^(NSString *_Nullable token,
  1449. NSError *_Nullable error) {
  1450. [expectation fulfill];
  1451. }];
  1452. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1453. }
  1454. /** @fn app1
  1455. @brief Creates a Firebase app.
  1456. @return A @c FIRApp with some name.
  1457. */
  1458. - (FIRApp *)app1 {
  1459. return [FIRApp appForAuthUnitTestsWithName:kFirebaseAppName1];
  1460. }
  1461. /** @fn app2
  1462. @brief Creates another Firebase app.
  1463. @return A @c FIRApp with some other name.
  1464. */
  1465. - (FIRApp *)app2 {
  1466. return [FIRApp appForAuthUnitTestsWithName:kFirebaseAppName2];
  1467. }
  1468. /** @fn stubSecureTokensWithMockResponse
  1469. @brief Creates stubs on the mock response object with access and refresh tokens
  1470. @param mockResponse The mock response object.
  1471. */
  1472. - (void)stubTokensWithMockResponse:(id)mockResponse {
  1473. OCMStub([mockResponse IDToken]).andReturn(kAccessToken);
  1474. OCMStub([mockResponse approximateExpirationDate])
  1475. .andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
  1476. OCMStub([mockResponse refreshToken]).andReturn(kRefreshToken);
  1477. }
  1478. /** @fn expectGetAccountInfo
  1479. @brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
  1480. data.
  1481. */
  1482. - (void)expectGetAccountInfo {
  1483. OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
  1484. .andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
  1485. FIRGetAccountInfoResponseCallback callback) {
  1486. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1487. XCTAssertEqualObjects(request.accessToken, kAccessToken);
  1488. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1489. id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
  1490. OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
  1491. OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kDisplayName);
  1492. OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
  1493. OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
  1494. id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
  1495. OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
  1496. callback(mockGetAccountInfoResponse, nil);
  1497. });
  1498. });
  1499. }
  1500. /** @fn assertUser
  1501. @brief Asserts the given FIRUser matching the fake data returned by @c expectGetAccountInfo.
  1502. @param user The user object to be verified.
  1503. */
  1504. - (void)assertUser:(FIRUser *)user {
  1505. XCTAssertNotNil(user);
  1506. XCTAssertEqualObjects(user.uid, kLocalID);
  1507. XCTAssertEqualObjects(user.displayName, kDisplayName);
  1508. XCTAssertEqualObjects(user.email, kEmail);
  1509. XCTAssertFalse(user.anonymous);
  1510. XCTAssertEqual(user.providerData.count, 0u);
  1511. }
  1512. /** @fn expectGetAccountInfoGoogle
  1513. @brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
  1514. data for a Google Sign-In user.
  1515. */
  1516. - (void)expectGetAccountInfoGoogle {
  1517. OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
  1518. .andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
  1519. FIRGetAccountInfoResponseCallback callback) {
  1520. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1521. XCTAssertEqualObjects(request.accessToken, kAccessToken);
  1522. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1523. id mockGoogleUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
  1524. OCMStub([mockGoogleUserInfo providerID]).andReturn(FIRGoogleAuthProviderID);
  1525. OCMStub([mockGoogleUserInfo displayName]).andReturn(kGoogleDisplayName);
  1526. OCMStub([mockGoogleUserInfo federatedID]).andReturn(kGoogleID);
  1527. OCMStub([mockGoogleUserInfo email]).andReturn(kGoogleEmail);
  1528. id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
  1529. OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
  1530. OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kDisplayName);
  1531. OCMStub([mockGetAccountInfoResponseUser providerUserInfo])
  1532. .andReturn((@[ mockGoogleUserInfo ]));
  1533. id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
  1534. OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
  1535. callback(mockGetAccountInfoResponse, nil);
  1536. });
  1537. });
  1538. }
  1539. /** @fn assertUserGoogle
  1540. @brief Asserts the given FIRUser matching the fake data returned by
  1541. @c expectGetAccountInfoGoogle.
  1542. @param user The user object to be verified.
  1543. */
  1544. - (void)assertUserGoogle:(FIRUser *)user {
  1545. XCTAssertNotNil(user);
  1546. XCTAssertEqualObjects(user.uid, kLocalID);
  1547. XCTAssertEqualObjects(user.displayName, kDisplayName);
  1548. XCTAssertEqual(user.providerData.count, 1u);
  1549. id<FIRUserInfo> googleUserInfo = user.providerData[0];
  1550. XCTAssertEqualObjects(googleUserInfo.providerID, FIRGoogleAuthProviderID);
  1551. XCTAssertEqualObjects(googleUserInfo.uid, kGoogleID);
  1552. XCTAssertEqualObjects(googleUserInfo.displayName, kGoogleDisplayName);
  1553. XCTAssertEqualObjects(googleUserInfo.email, kGoogleEmail);
  1554. }
  1555. /** @fn expectGetAccountInfoAnonymous
  1556. @brief Expects a GetAccountInfo request on the mock backend and calls back with fake anonymous
  1557. account data.
  1558. */
  1559. - (void)expectGetAccountInfoAnonymous {
  1560. OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
  1561. .andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
  1562. FIRGetAccountInfoResponseCallback callback) {
  1563. XCTAssertEqualObjects(request.APIKey, kAPIKey);
  1564. XCTAssertEqualObjects(request.accessToken, kAccessToken);
  1565. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1566. id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
  1567. OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
  1568. id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
  1569. OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
  1570. callback(mockGetAccountInfoResponse, nil);
  1571. });
  1572. });
  1573. }
  1574. /** @fn assertUserAnonymous
  1575. @brief Asserts the given FIRUser matching the fake data returned by
  1576. @c expectGetAccountInfoAnonymous.
  1577. @param user The user object to be verified.
  1578. */
  1579. - (void)assertUserAnonymous:(FIRUser *)user {
  1580. XCTAssertNotNil(user);
  1581. XCTAssertEqualObjects(user.uid, kLocalID);
  1582. XCTAssertNil(user.displayName);
  1583. XCTAssertTrue(user.anonymous);
  1584. XCTAssertEqual(user.providerData.count, 0u);
  1585. }
  1586. /** @fn waitForSignIn
  1587. @brief Signs in a user to prepare for tests.
  1588. @remarks This method also waits for all other pending @c XCTestExpectation instances.
  1589. */
  1590. - (void)waitForSignIn {
  1591. OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
  1592. .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
  1593. FIRVerifyPasswordResponseCallback callback) {
  1594. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  1595. id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
  1596. [self stubTokensWithMockResponse:mockVeriyPasswordResponse];
  1597. callback(mockVeriyPasswordResponse, nil);
  1598. });
  1599. });
  1600. [self expectGetAccountInfo];
  1601. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  1602. [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
  1603. NSError *_Nullable error) {
  1604. [expectation fulfill];
  1605. }];
  1606. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  1607. OCMVerifyAll(_mockBackend);
  1608. XCTAssertNotNil([FIRAuth auth].currentUser);
  1609. }
  1610. /** @fn waitForTimeInterval:
  1611. @brief Wait for a particular time interval.
  1612. @remarks This method also waits for all other pending @c XCTestExpectation instances.
  1613. */
  1614. - (void)waitForTimeIntervel:(NSTimeInterval)timeInterval {
  1615. static dispatch_queue_t queue;
  1616. static dispatch_once_t onceToken;
  1617. XCTestExpectation *expectation = [self expectationWithDescription:@"waitForTimeIntervel:"];
  1618. dispatch_once(&onceToken, ^{
  1619. queue = dispatch_queue_create("com.google.FIRAuthUnitTests.waitForTimeIntervel", NULL);
  1620. });
  1621. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeInterval * NSEC_PER_SEC), queue, ^() {
  1622. [expectation fulfill];
  1623. });
  1624. [self waitForExpectationsWithTimeout:timeInterval + kExpectationTimeout handler:nil];
  1625. }
  1626. @end