| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- /*
- * Copyright 2019 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 <XCTest/XCTest.h>
- #import "OCMock.h"
- #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
- #import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
- #import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
- #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
- #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
- #import "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h"
- #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
- #import <GoogleUtilities/GULNSData+zlib.h>
- #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
- #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
- @interface RCNConfigFetch (ForTest)
- - (instancetype)initWithContent:(RCNConfigContent *)content
- DBManager:(RCNConfigDBManager *)DBManager
- settings:(RCNConfigSettings *)settings
- experiment:(RCNConfigExperiment *)experiment
- queue:(dispatch_queue_t)queue
- namespace:firebaseNamespace
- app:firebaseApp;
- @end
- @interface RCNConfigDBManager (Test)
- - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path;
- @end
- @interface RCNUserDefaultsManager (Test)
- + (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier;
- @end
- @interface FIRInstallationsAuthTokenResult (Test)
- - (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate;
- @end
- typedef NS_ENUM(NSInteger, RCNTestRCInstance) {
- RCNTestRCInstanceDefault,
- RCNTestRCNumTotalInstances, // TODO(mandard): Remove once OCMock issue is resolved (#4877).
- RCNTestRCInstanceSecondNamespace,
- RCNTestRCInstanceSecondApp,
- };
- @class FIRInstallationsIDController;
- @interface FIRInstallations (Tests)
- - (instancetype)initWithAppOptions:(FIROptions *)appOptions
- appName:(NSString *)appName
- installationsIDController:(FIRInstallationsIDController *)installationsIDController
- prefetchAuthToken:(BOOL)prefetchAuthToken;
- @end
- @interface RCNInstallationsTests : XCTestCase {
- NSTimeInterval _expectationTimeout;
- NSTimeInterval _checkCompletionTimeout;
- NSMutableArray<FIRRemoteConfig *> *_configInstances;
- NSMutableArray<NSDictionary<NSString *, NSString *> *> *_entries;
- NSMutableArray<NSDictionary<NSString *, id> *> *_response;
- NSMutableArray<NSData *> *_responseData;
- NSMutableArray<NSURLResponse *> *_URLResponse;
- NSMutableArray<RCNConfigFetch *> *_configFetch;
- RCNConfigDBManager *_DBManager;
- NSUserDefaults *_userDefaults;
- NSString *_userDefaultsSuiteName;
- NSString *_DBPath;
- id _installationsMock;
- }
- @end
- @implementation RCNInstallationsTests
- - (void)setUpConfigMock {
- FIRSetLoggerLevel(FIRLoggerLevelMax);
- _expectationTimeout = 5;
- _checkCompletionTimeout = 1.0;
- [FIRApp configureWithOptions:[self firstAppOptions]];
- // Always remove the database at the start of testing.
- _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
- id classMock = OCMClassMock([RCNConfigDBManager class]);
- OCMStub([classMock remoteConfigPathForDatabase]).andReturn(_DBPath);
- _DBManager = [[RCNConfigDBManager alloc] init];
- _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
- _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
- id userDefaultsClassMock = OCMClassMock([RCNUserDefaultsManager class]);
- OCMStub([userDefaultsClassMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
- .andReturn(_userDefaults);
- RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
- _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
- _entries = [[NSMutableArray alloc] initWithCapacity:3];
- _response = [[NSMutableArray alloc] initWithCapacity:3];
- _responseData = [[NSMutableArray alloc] initWithCapacity:3];
- _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
- _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
- // Populate the default, second app, second namespace instances.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- // Fake a response for default instance.
- NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
- for (int count = 1; count <= 100; count++) {
- NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
- NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
- valuesDict[key] = value;
- }
- _entries[i] = valuesDict;
- NSString *currentAppName = nil;
- FIROptions *currentOptions = nil;
- NSString *currentNamespace = nil;
- switch (i) {
- case RCNTestRCInstanceSecondNamespace:
- currentAppName = RCNTestsDefaultFIRAppName;
- currentOptions = [self firstAppOptions];
- currentNamespace = RCNTestsPerfNamespace;
- break;
- case RCNTestRCInstanceSecondApp:
- currentAppName = RCNTestsSecondFIRAppName;
- currentOptions = [self secondAppOptions];
- currentNamespace = FIRNamespaceGoogleMobilePlatform;
- break;
- case RCNTestRCInstanceDefault:
- default:
- currentAppName = RCNTestsDefaultFIRAppName;
- currentOptions = [self firstAppOptions];
- currentNamespace = RCNTestsFIRNamespace;
- break;
- }
- NSString *fullyQualifiedNamespace =
- [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
- FIRRemoteConfig *config =
- OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
- FIROptions:currentOptions
- namespace:currentNamespace
- DBManager:_DBManager
- configContent:configContent
- analytics:nil]);
- _configInstances[i] = config;
- RCNConfigSettings *settings =
- [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
- namespace:fullyQualifiedNamespace
- firebaseAppName:currentAppName
- googleAppID:currentOptions.googleAppID];
- dispatch_queue_t queue = dispatch_queue_create(
- [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
- DISPATCH_QUEUE_SERIAL);
- _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
- DBManager:_DBManager
- settings:settings
- analytics:nil
- experiment:nil
- queue:queue
- namespace:fullyQualifiedNamespace
- options:currentOptions]);
- }
- }
- // Mock instance ID methods.
- - (void)mockInstanceIDMethodForTokenAndIdentity:(nullable NSString *)token
- tokenError:(nullable NSError *)tokenError
- identity:(nullable NSString *)identity
- identityError:(nullable NSError *)identityError {
- // Mock the installations retreival method.
- _installationsMock = OCMClassMock([FIRInstallations class]);
- id installationIDCompletionArg =
- [OCMArg checkWithBlock:^BOOL(FIRInstallationsIDHandler completion) {
- if (completion) {
- completion(identity, identityError);
- }
- return YES;
- }];
- OCMStub([_installationsMock installationIDWithCompletion:installationIDCompletionArg]);
- FIRInstallationsAuthTokenResult *tokenResult;
- if (token) {
- tokenResult = [[FIRInstallationsAuthTokenResult alloc] initWithToken:token
- expirationDate:[NSDate distantFuture]];
- }
- id authTokenCompletionArg =
- [OCMArg checkWithBlock:^BOOL(FIRInstallationsTokenHandler completion) {
- if (completion) {
- completion(tokenResult, tokenError);
- }
- return YES;
- }];
- OCMStub([_installationsMock authTokenWithCompletion:authTokenCompletionArg]);
- OCMStub([_installationsMock installationsWithApp:[OCMArg any]]).andReturn(_installationsMock);
- [self setUpConfigMock];
- }
- - (void)tearDown {
- [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
- [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
- [FIRApp resetApps];
- [_installationsMock stopMocking];
- _installationsMock = nil;
- [super tearDown];
- }
- // Instance ID token is nil. Error is not nil. Verify fetch fails.
- - (void)testNilInstallationsAuthTokenAndError {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Set the token as nil.
- [self
- mockInstanceIDMethodForTokenAndIdentity:nil
- tokenError:[NSError
- errorWithDomain:kFirebaseInstallationsErrorDomain
- code:FIRInstallationsErrorCodeUnknown
- userInfo:nil]
- identity:nil
- identityError:nil];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- [expectations[i] fulfill];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- // Test IID error. Subsequent request also fails with same error (b/148975341).
- - (void)testMultipleFetchCallsFailing {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Set the token as nil.
- NSError *tokenError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
- code:FIRInstallationsErrorCodeUnknown
- userInfo:nil];
- [self mockInstanceIDMethodForTokenAndIdentity:nil
- tokenError:tokenError
- identity:nil
- identityError:nil];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- XCTAssert([[error.userInfo objectForKey:@"NSLocalizedDescription"]
- containsString:@"Failed to get installations token"]);
- // Make a second fetch call.
- [self->_configInstances[i]
- fetchWithExpirationDuration:43200
- completionHandler:^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- XCTAssert([[error.userInfo objectForKey:@"NSLocalizedDescription"]
- containsString:@"Failed to get installations token"]);
- [expectations[i] fulfill];
- }];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- // Instance ID token is not nil. Error is not nil. Verify fetch fails.
- - (void)testValidInstanceIDTokenAndValidError {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- // Set the token as nil.
- NSError *tokenError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
- code:FIRInstallationsErrorCodeUnknown
- userInfo:nil];
- [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
- tokenError:tokenError
- identity:nil
- identityError:nil];
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- [expectations[i] fulfill];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- // Instance ID token is nil. Error is nil. Verify fetch fails.
- - (void)testNilInstanceIDTokenAndNilError {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- // Set the token as nil.
- [self mockInstanceIDMethodForTokenAndIdentity:nil
- tokenError:nil
- identity:nil
- identityError:nil];
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- [expectations[i] fulfill];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- // Instance ID token is valid. InstanceID is nil with no error. Verify fetch fails.
- - (void)testNilInstanceIDWithValidInstanceIDToken {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- // Set the token as nil.
- [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
- tokenError:nil
- identity:nil
- identityError:nil];
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- [expectations[i] fulfill];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- // Instance ID is not nil, but IID SDK returns an error. Also token is valid.
- - (void)testValidInstanceIDAndError {
- NSMutableArray<XCTestExpectation *> *expectations =
- [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
- // Test for each RC FIRApp, namespace instance.
- for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
- // Set the token as nil.
- NSError *identityError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
- code:FIRInstallationsErrorCodeUnknown
- userInfo:nil];
- [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
- tokenError:nil
- identity:@"test-id"
- identityError:identityError];
- expectations[i] =
- [self expectationWithDescription:
- [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
- XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
- FIRRemoteConfigFetchCompletion fetchCompletion =
- ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
- XCTAssertNotNil(error);
- [expectations[i] fulfill];
- };
- [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
- }
- [self waitForExpectationsWithTimeout:_expectationTimeout
- handler:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }
- #pragma mark - Test Helpers
- - (FIROptions *)firstAppOptions {
- // TODO: Evaluate if we want to hardcode things here instead.
- FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
- GCMSenderID:@"correct_gcm_sender_id"];
- options.APIKey = @"AIzaSy-ApiKeyWithValidFormat_0123456789";
- options.projectID = @"abc-xyz-123";
- return options;
- }
- - (FIROptions *)secondAppOptions {
- FIROptions *options =
- [[FIROptions alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]]
- pathForResource:@"SecondApp-GoogleService-Info"
- ofType:@"plist"]];
- XCTAssertNotNil(options);
- return options;
- }
- @end
|