/* * Copyright 2017 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #import #import "FIRAuthAppCredential.h" #import "FIRAuthAppCredentialManager.h" #import "FIRAuthNotificationManager.h" #import NS_ASSUME_NONNULL_BEGIN /** @var kReceipt @brief A fake receipt used for testing. */ static NSString *const kReceipt = @"FAKE_RECEIPT"; /** @var kSecret @brief A fake secret used for testing. */ static NSString *const kSecret = @"FAKE_SECRET"; /** @class FIRAuthFakeForwardingDelegate @brief The base class for a fake UIApplicationDelegate that forwards remote notifications. */ @interface FIRAuthFakeForwardingDelegate : NSObject /** @property notificationManager @brief The notification manager to forward. */ @property(nonatomic, strong) FIRAuthNotificationManager *notificationManager; /** @property forwardsNotification @brief Whether notifications are being forwarded. */ @property(nonatomic, assign) BOOL forwardsNotification; /** @property notificationReceived @brief Whether a notification has been received. */ @property(nonatomic, assign) BOOL notificationReceived; /** @property notificationhandled @brief Whether a notification has been handled. */ @property(nonatomic, assign) BOOL notificationhandled; @end @implementation FIRAuthFakeForwardingDelegate @end /** @class FIRAuthFakeForwardingDelegate @brief A fake UIApplicationDelegate that implements the modern deegate method to receive notification. */ @interface FIRAuthModernForwardingDelegate : FIRAuthFakeForwardingDelegate @end @implementation FIRAuthModernForwardingDelegate - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { self.notificationReceived = YES; if (self.forwardsNotification) { self.notificationhandled = [self.notificationManager canHandleNotification:userInfo]; } } @end /** @class FIRAuthLegacyForwardingDelegate @brief A fake UIApplicationDelegate that implements the legacy deegate method to receive notification. */ @interface FIRAuthLegacyForwardingDelegate : FIRAuthFakeForwardingDelegate @end @implementation FIRAuthLegacyForwardingDelegate - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { self.notificationReceived = YES; if (self.forwardsNotification) { self.notificationhandled = [self.notificationManager canHandleNotification:userInfo]; } } @end /** @class FIRAuthNotificationManagerTests @brief Unit tests for @c FIRAuthNotificationManager . */ @interface FIRAuthNotificationManagerTests : XCTestCase @end @implementation FIRAuthNotificationManagerTests { /** @var _mockApplication @brief The mock UIApplication for testing. */ id _mockApplication; /** @var _mockAppCredentialManager @brief The mock FIRAuthAppCredentialManager for testing. */ id _mockAppCredentialManager; /** @var _notificationManager @brief The FIRAuthNotificationManager to be tested. */ FIRAuthNotificationManager *_notificationManager; /** @var _modernDelegate @brief The modern fake UIApplicationDelegate for testing. */ FIRAuthModernForwardingDelegate *_modernDelegate; /** @var _legacyDelegate @brief The legacy fake UIApplicationDelegate for testing. */ FIRAuthLegacyForwardingDelegate *_legacyDelegate; } - (void)setUp { _mockApplication = OCMClassMock([UIApplication class]); _mockAppCredentialManager = OCMClassMock([FIRAuthAppCredentialManager class]); _notificationManager = [[FIRAuthNotificationManager alloc] initWithApplication:_mockApplication appCredentialManager:_mockAppCredentialManager]; _modernDelegate = [[FIRAuthModernForwardingDelegate alloc] init]; _modernDelegate.notificationManager = _notificationManager; _legacyDelegate = [[FIRAuthLegacyForwardingDelegate alloc] init]; _legacyDelegate.notificationManager = _notificationManager; } /** @fn testForwardingModernDelegate @brief Tests checking notification forwarding on modern fake delegate. */ - (void)testForwardingModernDelegate { [self verifyForwarding:YES delegate:_modernDelegate]; } /** @fn testForwardingLegacyDelegate @brief Tests checking notification forwarding on legacy fake delegate. */ - (void)testForwardingLegacyDelegate { [self verifyForwarding:YES delegate:_legacyDelegate]; } /** @fn testNotForwardingModernDelegate @brief Tests checking notification not forwarding on modern fake delegate. */ - (void)testNotForwardingModernDelegate { [self verifyForwarding:NO delegate:_modernDelegate]; } /** @fn testNotForwardingLegacyDelegate @brief Tests checking notification not forwarding on legacy fake delegate. */ - (void)testNotForwardingLegacyDelegate { [self verifyForwarding:NO delegate:_legacyDelegate]; } /** @fn verifyForwarding:delegate: @brief Tests checking notification forwarding on a particular delegate. @param forwarding Whether the notification is being forwarded or not. @param delegate The fake UIApplicationDelegate used for testing. */ - (void)verifyForwarding:(BOOL)forwarding delegate:(FIRAuthFakeForwardingDelegate *)delegate { delegate.forwardsNotification = forwarding; OCMStub([_mockApplication delegate]).andReturn(delegate); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [_notificationManager checkNotificationForwardingWithCallback:^(BOOL isNotificationBeingForwarded) { XCTAssertEqual(isNotificationBeingForwarded, forwarding); [expectation fulfill]; }]; XCTAssertFalse(delegate.notificationReceived); NSTimeInterval timeout = _notificationManager.timeout * (1.5 - forwarding); [self waitForExpectationsWithTimeout:timeout handler:nil]; XCTAssertTrue(delegate.notificationReceived); XCTAssertEqual(delegate.notificationhandled, forwarding); } /** @fn testCachedResult @brief Test notification forwarding is only checked once. */ - (void)testCachedResult { FIRAuthFakeForwardingDelegate *delegate = _modernDelegate; [self verifyForwarding:NO delegate:delegate]; delegate.notificationReceived = NO; __block BOOL calledBack = NO; [_notificationManager checkNotificationForwardingWithCallback:^(BOOL isNotificationBeingForwarded) { XCTAssertFalse(isNotificationBeingForwarded); calledBack = YES; }]; XCTAssertTrue(calledBack); XCTAssertFalse(delegate.notificationReceived); } /** @fn testMultipleCallbacks @brief Test multiple callbacks are handled correctly. */ - (void)testMultipleCallbacks { FIRAuthFakeForwardingDelegate *delegate = _legacyDelegate; delegate.forwardsNotification = YES; OCMStub([_mockApplication delegate]).andReturn(delegate); XCTestExpectation *expectation1 = [self expectationWithDescription:@"callback1"]; [_notificationManager checkNotificationForwardingWithCallback:^(BOOL isNotificationBeingForwarded) { XCTAssertTrue(isNotificationBeingForwarded); [expectation1 fulfill]; }]; XCTestExpectation *expectation2 = [self expectationWithDescription:@"callback2"]; [_notificationManager checkNotificationForwardingWithCallback:^(BOOL isNotificationBeingForwarded) { XCTAssertTrue(isNotificationBeingForwarded); [expectation2 fulfill]; }]; XCTAssertFalse(delegate.notificationReceived); [self waitForExpectationsWithTimeout:_notificationManager.timeout * .5 handler:nil]; XCTAssertTrue(delegate.notificationReceived); XCTAssertTrue(delegate.notificationhandled); } /** @fn testPassingToCredentialManager @brief Test notification with the right structure is passed to credential manager. */ - (void)testPassingToCredentialManager { NSDictionary *payload = @{ @"receipt" : kReceipt, @"secret" : kSecret }; NSDictionary *notification = @{ @"com.google.firebase.auth" : payload }; OCMExpect([_mockAppCredentialManager canFinishVerificationWithReceipt:kReceipt secret:kSecret]) .andReturn(YES); XCTAssertTrue([_notificationManager canHandleNotification:notification]); OCMVerifyAll(_mockAppCredentialManager); // JSON string form NSData *data = [NSJSONSerialization dataWithJSONObject:payload options:0 error:NULL]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; notification = @{ @"com.google.firebase.auth" : string }; OCMExpect([_mockAppCredentialManager canFinishVerificationWithReceipt:kReceipt secret:kSecret]) .andReturn(YES); XCTAssertTrue([_notificationManager canHandleNotification:notification]); OCMVerifyAll(_mockAppCredentialManager); } /** @fn testNotHandling @brief Test unrecognized notifications are not handled. */ - (void)testNotHandling { XCTAssertFalse([_notificationManager canHandleNotification:@{ @"random" : @"string" }]); XCTAssertFalse([_notificationManager canHandleNotification:@{ @"com.google.firebase.auth" : @"something wrong" }]); XCTAssertFalse([_notificationManager canHandleNotification:@{ @"com.google.firebase.auth" : @{ @"receipt" : kReceipt // missing secret } }]); XCTAssertFalse([_notificationManager canHandleNotification:@{ @"com.google.firebase.auth" : @{ // missing receipt @"secret" : kSecret } }]); XCTAssertFalse([_notificationManager canHandleNotification:@{ @"com.google.firebase.auth" : @{ // probing notification does not belong to this instance @"warning" : @"asdf" } }]); } @end NS_ASSUME_NONNULL_END