FIRPhoneAuthProviderTests.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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 "FIRPhoneAuthProvider.h"
  18. #import "Phone/FIRPhoneAuthCredential_Internal.h"
  19. #import "Phone/NSString+FIRAuth.h"
  20. #import "FIRAuthAPNSToken.h"
  21. #import "FIRAuthAPNSTokenManager.h"
  22. #import "FIRAuthAppCredential.h"
  23. #import "FIRAuthAppCredentialManager.h"
  24. #import "FIRAuthNotificationManager.h"
  25. #import "FIRAuth_Internal.h"
  26. #import "FIRAuthCredential_Internal.h"
  27. #import "FIRAuthErrorUtils.h"
  28. #import "FIRAuthGlobalWorkQueue.h"
  29. #import "FIRAuthBackend.h"
  30. #import "FIRSendVerificationCodeRequest.h"
  31. #import "FIRSendVerificationCodeResponse.h"
  32. #import "FIRVerifyClientRequest.h"
  33. #import "FIRVerifyClientResponse.h"
  34. #import "FIRApp+FIRAuthUnitTests.h"
  35. #import "OCMStubRecorder+FIRAuthUnitTests.h"
  36. #import <OCMock/OCMock.h>
  37. NS_ASSUME_NONNULL_BEGIN
  38. /** @var kTestPhoneNumber
  39. @brief A testing phone number.
  40. */
  41. static NSString *const kTestPhoneNumber = @"55555555";
  42. /** @var kTestInvalidPhoneNumber
  43. @brief An invalid testing phone number.
  44. */
  45. static NSString *const kTestInvalidPhoneNumber = @"555+!*55555";
  46. /** @var kTestVerificationID
  47. @brief A testing verfication ID.
  48. */
  49. static NSString *const kTestVerificationID = @"verificationID";
  50. /** @var kTestReceipt
  51. @brief A fake receipt for testing.
  52. */
  53. static NSString *const kTestReceipt = @"receipt";
  54. /** @var kTestSecret
  55. @brief A fake secret for testing.
  56. */
  57. static NSString *const kTestSecret = @"secret";
  58. /** @var kTestOldReceipt
  59. @brief A fake old receipt for testing.
  60. */
  61. static NSString *const kTestOldReceipt = @"old_receipt";
  62. /** @var kTestOldSecret
  63. @brief A fake old secret for testing.
  64. */
  65. static NSString *const kTestOldSecret = @"old_secret";
  66. /** @var kTestVerificationCode
  67. @brief A fake verfication code.
  68. */
  69. static NSString *const kTestVerificationCode = @"verificationCode";
  70. /** @var kTestTimeout
  71. @brief A fake timeout value for waiting for push notification.
  72. */
  73. static const NSTimeInterval kTestTimeout = 5;
  74. /** @var kAPIKey
  75. @brief The fake API key.
  76. */
  77. static NSString *const kAPIKey = @"FAKE_API_KEY";
  78. /** @var kExpectationTimeout
  79. @brief The maximum time waiting for expectations to fulfill.
  80. */
  81. static const NSTimeInterval kExpectationTimeout = 1;
  82. /** @class FIRPhoneAuthProviderTests
  83. @brief Tests for @c FIRPhoneAuthProvider
  84. */
  85. @interface FIRPhoneAuthProviderTests : XCTestCase
  86. @end
  87. @implementation FIRPhoneAuthProviderTests {
  88. /** @var _mockBackend
  89. @brief The mock @c FIRAuthBackendImplementation .
  90. */
  91. id _mockBackend;
  92. /** @var _provider
  93. @brief The @c FIRPhoneAuthProvider instance under test.
  94. */
  95. FIRPhoneAuthProvider *_provider;
  96. /** @var _mockAuth
  97. @brief The mock @c FIRAuth instance associated with @c _provider .
  98. */
  99. id _mockAuth;
  100. /** @var _mockAPNSTokenManager
  101. @brief The mock @c FIRAuthAPNSTokenManager instance associated with @c _mockAuth .
  102. */
  103. id _mockAPNSTokenManager;
  104. /** @var _mockAppCredentialManager
  105. @brief The mock @c FIRAuthAppCredentialManager instance associated with @c _mockAuth .
  106. */
  107. id _mockAppCredentialManager;
  108. /** @var _mockNotificationManager
  109. @brief The mock @c FIRAuthNotificationManager instance associated with @c _mockAuth .
  110. */
  111. id _mockNotificationManager;
  112. }
  113. - (void)setUp {
  114. [super setUp];
  115. _mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
  116. [FIRAuthBackend setBackendImplementation:_mockBackend];
  117. _mockAuth = OCMClassMock([FIRAuth class]);
  118. _mockAPNSTokenManager = OCMClassMock([FIRAuthAPNSTokenManager class]);
  119. OCMStub([_mockAuth tokenManager]).andReturn(_mockAPNSTokenManager);
  120. _mockAppCredentialManager = OCMClassMock([FIRAuthAppCredentialManager class]);
  121. OCMStub([_mockAuth appCredentialManager]).andReturn(_mockAppCredentialManager);
  122. _mockNotificationManager = OCMClassMock([FIRAuthNotificationManager class]);
  123. OCMStub([_mockAuth notificationManager]).andReturn(_mockNotificationManager);
  124. _provider = [FIRPhoneAuthProvider providerWithAuth:_mockAuth];
  125. }
  126. - (void)tearDown {
  127. [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
  128. [super tearDown];
  129. }
  130. /** @fn testCredentialWithVerificationID
  131. @brief Tests the @c credentialWithToken method to make sure that it returns a valid
  132. FIRAuthCredential instance.
  133. */
  134. - (void)testCredentialWithVerificationID {
  135. FIRPhoneAuthCredential *credential =
  136. [_provider credentialWithVerificationID:kTestVerificationID
  137. verificationCode:kTestVerificationCode];
  138. XCTAssertEqualObjects(credential.verificationID, kTestVerificationID);
  139. XCTAssertEqualObjects(credential.verificationCode, kTestVerificationCode);
  140. XCTAssertNil(credential.temporaryProof);
  141. XCTAssertNil(credential.phoneNumber);
  142. }
  143. /** @fn testVerifyEmptyPhoneNumber
  144. @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an empty phone
  145. number was provided.
  146. */
  147. - (void)testVerifyEmptyPhoneNumber {
  148. // Empty phone number is checked on the client side so no backend RPC is mocked.
  149. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  150. [_provider verifyPhoneNumber:@""
  151. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  152. XCTAssertNotNil(error);
  153. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingPhoneNumber);
  154. [expectation fulfill];
  155. }];
  156. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  157. }
  158. /** @fn testVerifyInvalidPhoneNumber
  159. @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an invalid phone
  160. number was provided.
  161. */
  162. - (void)testVerifyInvalidPhoneNumber {
  163. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  164. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  165. OCMStub([_mockAppCredentialManager credential])
  166. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  167. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  168. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  169. FIRSendVerificationCodeResponseCallback callback) {
  170. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  171. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  172. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  173. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  174. callback(nil, [FIRAuthErrorUtils invalidPhoneNumberErrorWithMessage:nil]);
  175. });
  176. });
  177. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  178. [_provider verifyPhoneNumber:kTestPhoneNumber
  179. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  180. XCTAssertTrue([NSThread isMainThread]);
  181. XCTAssertNil(verificationID);
  182. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidPhoneNumber);
  183. [expectation fulfill];
  184. }];
  185. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  186. OCMVerifyAll(_mockBackend);
  187. OCMVerifyAll(_mockNotificationManager);
  188. OCMVerifyAll(_mockAppCredentialManager);
  189. }
  190. /** @fn testVerifyPhoneNumber
  191. @brief Tests a successful invocation of @c verifyPhoneNumber:completion:.
  192. */
  193. - (void)testVerifyPhoneNumber {
  194. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  195. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  196. OCMStub([_mockAppCredentialManager credential])
  197. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  198. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  199. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  200. FIRSendVerificationCodeResponseCallback callback) {
  201. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  202. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  203. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  204. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  205. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  206. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  207. callback(mockSendVerificationCodeResponse, nil);
  208. });
  209. });
  210. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  211. [_provider verifyPhoneNumber:kTestPhoneNumber
  212. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  213. XCTAssertTrue([NSThread isMainThread]);
  214. XCTAssertNil(error);
  215. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  216. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  217. [expectation fulfill];
  218. }];
  219. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  220. OCMVerifyAll(_mockBackend);
  221. OCMVerifyAll(_mockNotificationManager);
  222. OCMVerifyAll(_mockAppCredentialManager);
  223. }
  224. /** @fn testNotForwardingNotification
  225. @brief Tests returning an error for the app failing to forward notification.
  226. */
  227. - (void)testNotForwardingNotification {
  228. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  229. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(NO); });
  230. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  231. [_provider verifyPhoneNumber:kTestPhoneNumber
  232. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  233. XCTAssertTrue([NSThread isMainThread]);
  234. XCTAssertNil(verificationID);
  235. XCTAssertEqual(error.code, FIRAuthErrorCodeNotificationNotForwarded);
  236. [expectation fulfill];
  237. }];
  238. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  239. OCMVerifyAll(_mockNotificationManager);
  240. }
  241. /** @fn testMissingAPNSToken
  242. @brief Tests returning an error for the app failing to provide an APNS device token.
  243. */
  244. - (void)testMissingAPNSToken {
  245. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  246. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  247. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  248. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  249. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(nil); });
  250. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  251. [_provider verifyPhoneNumber:kTestPhoneNumber
  252. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  253. XCTAssertTrue([NSThread isMainThread]);
  254. XCTAssertNil(verificationID);
  255. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingAppToken);
  256. [expectation fulfill];
  257. }];
  258. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  259. OCMVerifyAll(_mockNotificationManager);
  260. OCMVerifyAll(_mockAppCredentialManager);
  261. OCMVerifyAll(_mockAPNSTokenManager);
  262. }
  263. /** @fn testVerifyClient
  264. @brief Tests verifying client before sending verification code.
  265. */
  266. - (void)testVerifyClient {
  267. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  268. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  269. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  270. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  271. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  272. type:FIRAuthAPNSTokenTypeProd];
  273. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  274. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); });
  275. // Expect verify client request to the backend.
  276. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  277. .andCallBlock2(^(FIRVerifyClientRequest *request,
  278. FIRVerifyClientResponseCallback callback) {
  279. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  280. XCTAssertFalse(request.isSandbox);
  281. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  282. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  283. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  284. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  285. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  286. callback(mockVerifyClientResponse, nil);
  287. });
  288. });
  289. // Mock receiving of push notification.
  290. OCMExpect([[_mockAppCredentialManager ignoringNonObjectArgs]
  291. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  292. .andCallIdDoubleIdBlock(^(NSString *receipt,
  293. NSTimeInterval timeout,
  294. FIRAuthAppCredentialCallback callback) {
  295. XCTAssertEqualObjects(receipt, kTestReceipt);
  296. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  297. // into the block either, so we can't verify it here.
  298. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  299. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  300. });
  301. });
  302. // Expect send verification code request to the backend.
  303. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  304. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  305. FIRSendVerificationCodeResponseCallback callback) {
  306. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  307. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  308. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  309. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  310. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  311. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  312. callback(mockSendVerificationCodeResponse, nil);
  313. });
  314. });
  315. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  316. [_provider verifyPhoneNumber:kTestPhoneNumber
  317. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  318. XCTAssertTrue([NSThread isMainThread]);
  319. XCTAssertNil(error);
  320. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  321. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  322. [expectation fulfill];
  323. }];
  324. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  325. OCMVerifyAll(_mockBackend);
  326. OCMVerifyAll(_mockNotificationManager);
  327. OCMVerifyAll(_mockAppCredentialManager);
  328. OCMVerifyAll(_mockAPNSTokenManager);
  329. }
  330. /** @fn testSendVerificationCodeFailedRetry
  331. @brief Tests failed retry after failing to send verification code.
  332. */
  333. - (void)testSendVerificationCodeFailedRetry {
  334. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  335. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  336. // Expect twice due to null check consumes one expectation.
  337. OCMExpect([_mockAppCredentialManager credential])
  338. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  339. secret:kTestOldSecret]);
  340. OCMExpect([_mockAppCredentialManager credential])
  341. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  342. secret:kTestOldSecret]);
  343. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  344. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  345. type:FIRAuthAPNSTokenTypeProd];
  346. // Expect first sendVerificationCode request to the backend, with request containing old app
  347. // credential.
  348. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  349. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  350. FIRSendVerificationCodeResponseCallback callback) {
  351. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  352. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  353. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  354. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  355. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  356. });
  357. });
  358. // Expect send verification code request to the backend, with request containing new app
  359. // credential data.
  360. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  361. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  362. FIRSendVerificationCodeResponseCallback callback) {
  363. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  364. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  365. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  366. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  367. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  368. });
  369. });
  370. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  371. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); });
  372. // Expect verify client request to the backend.
  373. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  374. .andCallBlock2(^(FIRVerifyClientRequest *request,
  375. FIRVerifyClientResponseCallback callback) {
  376. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  377. XCTAssertFalse(request.isSandbox);
  378. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  379. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  380. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  381. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  382. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  383. callback(mockVerifyClientResponse, nil);
  384. });
  385. });
  386. // Mock receiving of push notification.
  387. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  388. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  389. .andCallIdDoubleIdBlock(^(NSString *receipt,
  390. NSTimeInterval timeout,
  391. FIRAuthAppCredentialCallback callback) {
  392. XCTAssertEqualObjects(receipt, kTestReceipt);
  393. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  394. // into the block either, so we can't verify it here.
  395. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  396. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  397. });
  398. });
  399. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  400. [_provider verifyPhoneNumber:kTestPhoneNumber
  401. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  402. XCTAssertTrue([NSThread isMainThread]);
  403. XCTAssertNil(verificationID);
  404. XCTAssertEqual(error.code, FIRAuthErrorCodeInternalError);
  405. [expectation fulfill];
  406. }];
  407. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  408. OCMVerifyAll(_mockBackend);
  409. OCMVerifyAll(_mockNotificationManager);
  410. OCMVerifyAll(_mockAppCredentialManager);
  411. OCMVerifyAll(_mockAPNSTokenManager);
  412. }
  413. /** @fn testSendVerificationCodeSuccessFulRetry
  414. @brief Tests successful retry after failing to send verification code.
  415. */
  416. - (void)testSendVerificationCodeSuccessFulRetry {
  417. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  418. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  419. // Expect twice due to null check consumes one expectation.
  420. OCMExpect([_mockAppCredentialManager credential])
  421. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  422. secret:kTestOldSecret]);
  423. OCMExpect([_mockAppCredentialManager credential])
  424. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  425. secret:kTestOldSecret]);
  426. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  427. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  428. type:FIRAuthAPNSTokenTypeProd];
  429. // Expect first sendVerificationCode request to the backend, with request containing old app
  430. // credential.
  431. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  432. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  433. FIRSendVerificationCodeResponseCallback callback) {
  434. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  435. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  436. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  437. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  438. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  439. });
  440. });
  441. // Expect send verification code request to the backend, with request containing new app
  442. // credential data.
  443. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  444. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  445. FIRSendVerificationCodeResponseCallback callback) {
  446. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  447. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  448. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  449. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  450. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  451. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  452. callback(mockSendVerificationCodeResponse, nil);
  453. });
  454. });
  455. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  456. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); });
  457. // Expect verify client request to the backend.
  458. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  459. .andCallBlock2(^(FIRVerifyClientRequest *request,
  460. FIRVerifyClientResponseCallback callback) {
  461. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  462. XCTAssertFalse(request.isSandbox);
  463. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  464. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  465. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  466. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  467. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  468. callback(mockVerifyClientResponse, nil);
  469. });
  470. });
  471. // Mock receiving of push notification.
  472. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  473. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  474. .andCallIdDoubleIdBlock(^(NSString *receipt,
  475. NSTimeInterval timeout,
  476. FIRAuthAppCredentialCallback callback) {
  477. XCTAssertEqualObjects(receipt, kTestReceipt);
  478. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  479. // into the block either, so we can't verify it here.
  480. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  481. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  482. });
  483. });
  484. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  485. [_provider verifyPhoneNumber:kTestPhoneNumber
  486. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  487. XCTAssertNil(error);
  488. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  489. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  490. [expectation fulfill];
  491. }];
  492. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  493. OCMVerifyAll(_mockBackend);
  494. OCMVerifyAll(_mockNotificationManager);
  495. OCMVerifyAll(_mockAppCredentialManager);
  496. OCMVerifyAll(_mockAPNSTokenManager);
  497. }
  498. @end
  499. NS_ASSUME_NONNULL_END