Files
firebase-ios-sdk/FirebaseAppDistribution/Tests/Unit/FIRFADApiServiceTests.m
T
16e062c355 Switch App Distribution SDK from AppAuth to FirebaseInstallations (#6126)
* Adding error handling and returning correct error codes

* * Handling auth cancellation a few other error cases

* Adding some logging for EAP (Using NSLogs temporarily. DONOT MERGE)

* Add in a setter for clientID to allow overrides (#5514)

* Add in a setter for clientID to allow overrides

* Update styling

* Adding some result parsing logs

* Fixing code hash logging

* Add FirebaseAppDistribution to ZipBuilder

* * Making sure keychain is accesible after first unlock even if app is backgrounded (#5563)
* Fix auth persistence logging and invalid type casting (#5631)
* Logging app id requesting releases (#5655)

* AppDistribution keychain access fixes (#5683)

* Using the GULKeychainUtils instead of custom keychain utility. Also added better logging

* Updating keychain name and fixing tests

* Code review feedback updates

* Making serviceID unique per app

* Adding gmp app id to the keychain along with the bundle id

* Moving Keychain Utility functions in the AuthPersistence class and deleting the utility class

* Lazy initialize auth state (#5755)

* Lazy initialize auth state

* Making sure we dont log an error is auth state is not persisted in the keychain

* Switch from NSLog to FIRFADLogger (#5756)

* Add FIRLogger
* Switch from NSLog to FIRFADLogger

* * Use FIS Auth for authenticating the tester
* Remove AppAuth and start wiring up interceptor to do sign in

* Extracting registration flow UI in interceptor

* Adding support for iOS 9 and 10

* Create app distribution api service to handle tester api calls. (#6060)

* Pull out calls to the FAD API to make them reuseable

* Fix tests and run styl updates

* Uncomment FIRFADLog Statements

* Use [] accessor rather than . accessor

* Re-running style updates

* Trying to get these checks to pass the first time around

* App distribution signin persist (#6063)

* Pull out calls to the FAD API to make them reuseable

* Fix tests and run styl updates

* Add in UserDefaults storage after a tester has signed in

* Add testing for the main AppDistribution file

* Ran style.sh

* Remove FIRFADLocalStorage and call GULUserDefaults directly. Update tests to stop mocking on teardown.

* Add support for dynamic url schemes and fix two crashes (#6081)

* Add support for dynamic url schemes and fix two crashes

  * Add support for dynamic url schemes in the format appdistribution-<app id without colons>
  * Fix crash when launching an app directly via a URL scheme
  * Fix a crash on iOS 10 simulator caused by calling NSLogv twice
  * Remove uses of NSLog
  * Fix warnings

* Switch encodedAppId to be a class method

* Fix styles

* Create changelog and readme in preparation for release. (#6095)

Just changing markdown files so will not wait for gha checks.

* Change slices property from copy to strong (#6097)

Fix lint warning

* Change error handlers to return bool values instead of void (#6101)

* Mapping sign in flow errors to app distribution errors. Also renaming AppDelegateInterceptor to FIRAppDistributionUIService (#6099)

* Add github action for App Distribution (#6094)

* Updating app distribution deployment target to iOS 9 (#6118)

* Making sure we return granular errors during sign in persistence. Also fixing some naming and comment issues (#6117)

* Encoding colons to dashes in the app id (#6119)

* Move Internal headers to Sources (#6106)

* Move Internal headers to Sources/ folder

* PR feedback

* Remove appDistribution from zip builder

* Respond to code review feedback (#6138)

* Initializing UIWindow using initWithWindowScene for ios 13 and removing UI code from main FIRAppDistribution.m (#6144)

* Initializing UIWindow using initWithWindowScene for ios 13. Also making sure we only reset window in the App delegate if a registration flow is in progress.

* Remove UI code from main class, add unit tests, and clean up UIService

* stylish

* Remove defines in favor of solely 'if (@available(iOS 13.0, *))'

* Respond to code review feedback

Co-authored-by: Cleo Schneider <cleoschneider@google.com>

Co-authored-by: Pranav Rajgopal <pranavrajgopal@google.com>
Co-authored-by: Cleo Schneider <cleoschneider@google.com>
2020-07-28 16:39:29 -04:00

488 lines
20 KiB
Objective-C

//
// Copyright 2020 Google LLC
//
// 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 <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "FirebaseAppDistribution/Sources/FIRFADApiService.h"
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
#import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
NSString *const kFakeErrorDomain = @"test.failure.domain";
@interface FIRFADApiServiceTests : XCTestCase
@end
@implementation FIRFADApiServiceTests {
id _mockFIRAppClass;
id _mockURLSession;
id _mockFIRInstallations;
id _mockInstallationToken;
NSString *_mockAuthToken;
NSString *_mockInstallationId;
NSDictionary *_mockReleases;
}
- (void)setUp {
[super setUp];
_mockFIRAppClass = OCMClassMock([FIRApp class]);
_mockURLSession = OCMClassMock([NSURLSession class]);
_mockFIRInstallations = OCMClassMock([FIRInstallations class]);
_mockInstallationToken = OCMClassMock([FIRInstallationsAuthTokenResult class]);
_mockAuthToken = @"this-is-an-auth-token";
OCMStub([_mockFIRAppClass defaultApp]).andReturn(_mockFIRAppClass);
OCMStub([_mockURLSession sharedSession]).andReturn(_mockURLSession);
OCMStub([_mockFIRInstallations installations]).andReturn(_mockFIRInstallations);
OCMStub([_mockInstallationToken authToken]).andReturn(_mockAuthToken);
_mockInstallationId = @"this-id-is-fake-ccccc";
_mockReleases = @{
@"releases" : @[
@{
@"displayVersion" : @"1.0.0",
@"buildVersion" : @"111",
@"releaseNotes" : @"This is a release",
@"downloadURL" : @"http://faketyfakefake.download"
},
@{
@"latest" : @YES,
@"displayVersion" : @"1.0.1",
@"buildVersion" : @"112",
@"releaseNotes" : @"This is a release too",
@"downloadURL" : @"http://faketyfakefake.download"
}
]
};
}
- (void)tearDown {
[super tearDown];
[_mockFIRAppClass stopMocking];
[_mockFIRInstallations stopMocking];
[_mockInstallationToken stopMocking];
[_mockURLSession stopMocking];
}
- (void)mockInstallationAuthCompletion:(FIRInstallationsAuthTokenResult *_Nullable)token
error:(NSError *_Nullable)error {
[OCMStub([_mockFIRInstallations authTokenWithCompletion:OCMOCK_ANY])
andDo:^(NSInvocation *invocation) {
void (^handler)(FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
NSError *_Nullable error);
[invocation getArgument:&handler atIndex:2];
handler(token, error);
}];
}
- (void)verifyInstallationAuthCompletion {
OCMVerify([_mockFIRInstallations authTokenWithCompletion:[OCMArg isNotNil]]);
}
- (void)rejectInstallationAuthCompletion {
OCMReject([_mockFIRInstallations authTokenWithCompletion:[OCMArg isNotNil]]);
}
- (void)mockInstallationIdCompletion:(NSString *_Nullable)identifier
error:(NSError *_Nullable)error {
[OCMStub([_mockFIRInstallations installationIDWithCompletion:OCMOCK_ANY])
andDo:^(NSInvocation *invocation) {
void (^handler)(NSString *identifier, NSError *_Nullable error);
[invocation getArgument:&handler atIndex:2];
handler(identifier, error);
}];
}
- (void)verifyInstallationIdCompletion {
OCMVerify([_mockFIRInstallations installationIDWithCompletion:[OCMArg isNotNil]]);
}
- (void)rejectInstallationIdCompletion {
OCMReject([_mockFIRInstallations installationIDWithCompletion:[OCMArg isNotNil]]);
}
- (void)mockUrlSessionResponse:(NSDictionary *)dictionary
response:(NSURLResponse *)response
error:(NSError *)error {
NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error];
[self mockUrlSessionResponseWithData:data response:response error:error];
}
- (void)mockUrlSessionResponseWithData:(NSData *)data
response:(NSURLResponse *)response
error:(NSError *)error {
[OCMStub([_mockURLSession dataTaskWithRequest:[OCMArg any]
completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
void (^handler)(NSData *data, NSURLResponse *response, NSError *error);
[invocation getArgument:&handler atIndex:3];
handler(data, response, error);
}];
}
- (void)verifyUrlSessionResponseWithData {
OCMVerify([_mockURLSession dataTaskWithRequest:[OCMArg isNotNil]
completionHandler:[OCMArg isNotNil]]);
}
- (void)rejectUrlSessionResponseWithData {
OCMReject([_mockURLSession dataTaskWithRequest:[OCMArg isNotNil]
completionHandler:[OCMArg isNotNil]]);
}
- (void)testGenerateAuthTokenWithCompletionSuccess {
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Generate auth token succeeds."];
[FIRFADApiService
generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
NSError *_Nullable error) {
XCTAssertNotNil(authTokenResult);
XCTAssertNotNil(identifier);
XCTAssertNil(error);
XCTAssertEqual(identifier, self->_mockInstallationId);
XCTAssertEqual([authTokenResult authToken], self -> _mockAuthToken);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
}
- (void)testGenerateAuthTokenWithCompletionAuthTokenFailure {
[self mockInstallationAuthCompletion:nil
error:[NSError errorWithDomain:kFakeErrorDomain
code:1
userInfo:@{}]];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Generate auth token fails to generate auth token."];
[FIRFADApiService
generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
NSError *_Nullable error) {
XCTAssertNil(identifier);
XCTAssertNil(authTokenResult);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRFADApiTokenGenerationFailure);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self rejectInstallationIdCompletion];
}
- (void)testGenerateAuthTokenWithCompletionIDFailure {
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:nil
error:[NSError errorWithDomain:kFakeErrorDomain
code:1
userInfo:@{}]];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Generate auth token fails to find ID."];
[FIRFADApiService
generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
NSError *_Nullable error) {
XCTAssertNil(identifier);
XCTAssertNil(authTokenResult);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiInstallationIdentifierError);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
}
- (void)testFetchReleasesWithCompletionSuccess {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(200);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases succeeds with two releases."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertNotNil(releases);
XCTAssertEqual([releases count], 2);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnknownFailure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(200);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases
response:fakeResponse
error:[NSError errorWithDomain:kFakeErrorDomain code:1 userInfo:@{}]];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases fails with unknown error."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRApiErrorUnknownFailure);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnauthenticatedFailure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(401);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases fails with unknown error."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRFADApiErrorUnauthenticated);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnauthorized400Failure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(400);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a 400."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnauthorized403Failure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(403);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a 403."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnauthorized404Failure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(404);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a 404."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionTimeout408Failure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(408);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with 408."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiErrorTimeout);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionTimeout504Failure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(504);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a 504."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRFADApiErrorTimeout);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionUnknownStatusCodeFailure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(500);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a 500."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRApiErrorUnknownFailure);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionNoReleasesFoundSuccess {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(200);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self mockUrlSessionResponse:@{@"releases" : @[]} response:fakeResponse error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a not found exception."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNotNil(releases);
XCTAssertNil(error);
XCTAssertEqual([releases count], 0);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
- (void)testFetchReleasesWithCompletionParsingFailure {
NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
OCMStub([fakeResponse statusCode]).andReturn(200);
[self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
[self mockInstallationIdCompletion:_mockInstallationId error:nil];
[self
mockUrlSessionResponseWithData:[@"malformed{json[data" dataUsingEncoding:NSUTF8StringEncoding]
response:fakeResponse
error:nil];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Fetch releases rejects with a parsing failure."];
[FIRFADApiService
fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
XCTAssertNil(releases);
XCTAssertNotNil(error);
XCTAssertEqual([error code], FIRApiErrorParseFailure);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5.0];
[self verifyInstallationAuthCompletion];
[self verifyInstallationIdCompletion];
[self verifyUrlSessionResponseWithData];
OCMVerify([fakeResponse statusCode]);
}
@end