From 7dfbf2401873feff1320107abfba749bae29bdbb Mon Sep 17 00:00:00 2001 From: Gustavo Meyer <107439697+grmeyer-hw-dev@users.noreply.github.com> Date: Tue, 30 Aug 2022 14:59:24 -0700 Subject: [PATCH] DTSERWONE-821 - Handle HTTP 401 (#132) * DTSERWONE-821 - Bubble up HTTP_UNAUTHORIZED error Map HTTP UNAUTHORIZED responses as Authentication error * DTSERWONE-821 - Handle HTTP 401 The Hyperwallet SDK can receive unauthorized (HTTP 401) response from the Hyperwallet API due the client change the password from the consumer UI, the core is bubble up HTTP error as Unexpected error. This PR will bubble up HTTP 401 error as Authentication error. Enhanced Hyperwallet Error to handle 401 Introduce test by performing Rest and GraphQL call to handle 401 * Update .travis.yml * Upate ios version to 15.5 * Update .travis.yml * Update .travis.yml * Update .travis.yml * Update swiftlint deprecated param * Bump up SDK Version * Bump up version * Update Carthage build folder --- .travis.yml | 14 +- CHANGELOG.md | 4 + HyperwalletSDK.podspec | 2 +- HyperwalletSDK.xcodeproj/project.pbxproj | 40 ++--- README.md | 6 +- Sources/HyperwalletError.swift | 16 +- Sources/Info.plist | 2 +- Tests/HTTPTransactionTests.swift | 182 ++++++++++++++++++++++- Tests/HyperwalletErrorTests.swift | 11 +- Tests/Responses/JWTTokenExpired.json | 8 + 10 files changed, 251 insertions(+), 34 deletions(-) create mode 100644 Tests/Responses/JWTTokenExpired.json diff --git a/.travis.yml b/.travis.yml index 6d2069eb..45a59650 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: swift -osx_image: xcode13.1 +osx_image: xcode13.4 os: osx branches: only: @@ -15,14 +15,14 @@ env: - IOS_FRAMEWORK_SCHEME="HyperwalletSDK" matrix: - - ios_version='15.0' ios_device='iPhone 11' scheme="$IOS_FRAMEWORK_SCHEME" platform='iOS Simulator' + - ios_version='15.5' ios_device='iPhone 13' scheme="$IOS_FRAMEWORK_SCHEME" platform='iOS Simulator' before_install: # Boot the emulator by ID - - | - ios_uid=$(xcrun xctrace list devices | grep Simulator | grep "$ios_device Simulator ($ios_version)*" | grep -o "[0-9A-F]\{8\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{12\}") - xcrun simctl boot $ios_uid - # List all emulator available - - xcrun simctl list + - xcrun xctrace list devices + - ios_uid=$(xcrun xctrace list devices | grep Simulator | grep "$ios_device Simulator ($ios_version) ([0-9A-F]\{8\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{12\})" | grep -o "[0-9A-F]\{8\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{4\}-[0-9A-F]\{12\}") + - echo $ios_uid + - xcrun simctl boot $ios_uid + # Update the brew and build dependencies tools - brew update && brew upgrade carthage - carthage update --platform ios --cache-builds --use-xcframeworks --no-use-binaries diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f9cfcc..a5f3e10d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ Changelog ========= +[1.0.0-beta17](https://github.com/hyperwallet/hyperwallet-ios-sdk/releases/tag/1.0.0-beta17) + ------------------- + - Handle HTTP 401 + [1.0.0-beta16](https://github.com/hyperwallet/hyperwallet-ios-sdk/releases/tag/1.0.0-beta16) ------------------- - iOS upgrade to version 13 diff --git a/HyperwalletSDK.podspec b/HyperwalletSDK.podspec index 734e48ff..2b63babc 100644 --- a/HyperwalletSDK.podspec +++ b/HyperwalletSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'HyperwalletSDK' - spec.version = '1.0.0-beta16' + spec.version = '1.0.0-beta17' spec.summary = 'Hyperwallet Core SDK for iOS to integrate with Hyperwallet Platform' spec.homepage = 'https://github.com/hyperwallet/hyperwallet-ios-sdk' spec.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/HyperwalletSDK.xcodeproj/project.pbxproj b/HyperwalletSDK.xcodeproj/project.pbxproj index e6e282e3..a64a9196 100644 --- a/HyperwalletSDK.xcodeproj/project.pbxproj +++ b/HyperwalletSDK.xcodeproj/project.pbxproj @@ -101,6 +101,8 @@ DB1A0422225D571B0080C8D6 /* AuthenticationTokenGeneratorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1A0420225D571A0080C8D6 /* AuthenticationTokenGeneratorMock.swift */; }; DB1A0423225D571B0080C8D6 /* AuthenticationProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1A0421225D571B0080C8D6 /* AuthenticationProviderMock.swift */; }; DB2A3B092261D2DC0049F891 /* HyperwalletSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = DB2A3B082261D2DC0049F891 /* HyperwalletSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DB2BBC2928B99806005AACE0 /* JWTTokenExpired.json in Resources */ = {isa = PBXBuildFile; fileRef = DB2BBC2828B99806005AACE0 /* JWTTokenExpired.json */; }; + DB2BBC2A28B99806005AACE0 /* JWTTokenExpired.json in Resources */ = {isa = PBXBuildFile; fileRef = DB2BBC2828B99806005AACE0 /* JWTTokenExpired.json */; }; DB3151CF22780AA300FC9F8E /* UserIndividualResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = DB3151CE22780AA300FC9F8E /* UserIndividualResponse.json */; }; DB45190F22DD00A00022BD1F /* HyperwalletTransferMethodConfigurationFieldQueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45190E22DD00A00022BD1F /* HyperwalletTransferMethodConfigurationFieldQueryTests.swift */; }; DB764E91227833E10053BB91 /* UserBusinessResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = DB764E8F22782D890053BB91 /* UserBusinessResponse.json */; }; @@ -257,6 +259,7 @@ DB1A0420225D571A0080C8D6 /* AuthenticationTokenGeneratorMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTokenGeneratorMock.swift; sourceTree = ""; }; DB1A0421225D571B0080C8D6 /* AuthenticationProviderMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationProviderMock.swift; sourceTree = ""; }; DB2A3B082261D2DC0049F891 /* HyperwalletSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HyperwalletSDK.h; sourceTree = ""; }; + DB2BBC2828B99806005AACE0 /* JWTTokenExpired.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = JWTTokenExpired.json; sourceTree = ""; }; DB3151CE22780AA300FC9F8E /* UserIndividualResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = UserIndividualResponse.json; sourceTree = ""; }; DB45190E22DD00A00022BD1F /* HyperwalletTransferMethodConfigurationFieldQueryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperwalletTransferMethodConfigurationFieldQueryTests.swift; sourceTree = ""; }; DB764E8F22782D890053BB91 /* UserBusinessResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = UserBusinessResponse.json; sourceTree = ""; }; @@ -652,23 +655,21 @@ DB99FF2E225D600000F86181 /* Responses */ = { isa = PBXGroup; children = ( - 2E6A04172577162E00244BE6 /* PaperCheck */, - 641324DE22954E2500512816 /* BankAccount */, 641EBE992292DA3300D718F4 /* AuthenticationTokens */, - 64D2493D22BD023600561C14 /* Transfer */, - DB764E8F22782D890053BB91 /* UserBusinessResponse.json */, - DB3151CE22780AA300FC9F8E /* UserIndividualResponse.json */, + 641324DE22954E2500512816 /* BankAccount */, DBCA37AA225D6D1700CD4137 /* BankAccountErrorResponseWithMissingBankId.json */, DBCA37AC225D6D1700CD4137 /* BankCardErrorResponseWithInvalidBranchId.json */, DBCA37A5225D6D1700CD4137 /* BankCardErrorResponseWithInvalidCardNumber.json */, DBCA37BB225D6DD900CD4137 /* BankCardErrorResponseWithMissingCardNumber.json */, DBCA37BD225D6E1900CD4137 /* BankCardResponse.json */, + DB2BBC2828B99806005AACE0 /* JWTTokenExpired.json */, DBCA37A2225D6D1600CD4137 /* ListBankAccountResponse.json */, DBCA37A6225D6D1700CD4137 /* ListBankCardResponse.json */, 07C00B542278EFB200E0930C /* ListPayPalAccountResponse.json */, + 4A41069B22B10FAF00A930AC /* ListPrepaidCardReceiptResponse.json */, 07B1FE6322C2C56800F461D0 /* ListPrepaidCardResponse.json */, 2B5433F8229EDDED00F900D2 /* ListUserReceiptResponse.json */, - 4A41069B22B10FAF00A930AC /* ListPrepaidCardReceiptResponse.json */, + 2E6A04172577162E00244BE6 /* PaperCheck */, 07C00B062277D30000E0930C /* PayPalAccountResponse.json */, 07C00B57227A11CB00E0930C /* PayPalAccountResponseNotProfileEmail.json */, 07C00B502278EC3500E0930C /* PayPalAccountResponseWithInvalidEmail.json */, @@ -676,20 +677,23 @@ 980B9AE5251C052600653D1C /* PrepaidCardResponse.json */, DBCA37A7225D6D1700CD4137 /* StatusTransitionMockedResponseInvalidTransition.json */, DBCA37A3225D6D1600CD4137 /* StatusTransitionMockedResponseSuccess.json */, + 64D2493D22BD023600561C14 /* Transfer */, + FD2B6ECA2670D6980021078B /* TransferMethodConfigurationFeeAndProcessingTimeResponse.json */, DBCA37A9225D6D1700CD4137 /* TransferMethodConfigurationFieldsResponse.json */, DBCA37A4225D6D1600CD4137 /* TransferMethodConfigurationGraphQlResponse.json */, DBCA37AD225D6D1700CD4137 /* TransferMethodConfigurationKeysResponse.json */, - FD2B6ECA2670D6980021078B /* TransferMethodConfigurationFeeAndProcessingTimeResponse.json */, - FDF2E7A5257E06D600589076 /* TransferMethodUpdateConfigurationFieldsResponse.json */, + DBCA37AB225D6D1700CD4137 /* TransferMethodConfigurationKeysWithoutFeeResponse.json */, + DBCA37BF225D6E3A00CD4137 /* TransferMethodMockedSuccessResponse.json */, FDD2F4D02580B1D0008E4CFC /* TransferMethodUpdateConfigurationFieldsBankCardResponse.json */, FDD2F4CE2580AFB0008E4CFC /* TransferMethodUpdateConfigurationFieldsPaypalResponse.json */, + FDF2E7A5257E06D600589076 /* TransferMethodUpdateConfigurationFieldsResponse.json */, FD1208CB2581FE3200A8D8B2 /* TransferMethodUpdateConfigurationFieldsVenmoResponse.json */, - DBCA37AB225D6D1700CD4137 /* TransferMethodConfigurationKeysWithoutFeeResponse.json */, - DBCA37BF225D6E3A00CD4137 /* TransferMethodMockedSuccessResponse.json */, - E843856BD0D7E5295973780D /* VenmoAccountResponse.json */, + DB764E8F22782D890053BB91 /* UserBusinessResponse.json */, + DB3151CE22780AA300FC9F8E /* UserIndividualResponse.json */, + E8438B2F7AA2659EA914914B /* VenmoAccountList.json */, E8438A5AF96BD0809F4B8287 /* VenmoAccountMissingAccountId.json */, + E843856BD0D7E5295973780D /* VenmoAccountResponse.json */, E84381CE41DDB491C2F2B63D /* VenmoAccountWrongFormatAccountId.json */, - E8438B2F7AA2659EA914914B /* VenmoAccountList.json */, ); path = Responses; sourceTree = ""; @@ -804,6 +808,7 @@ DB866F2A2261EE28003C41F6 /* README.md in Resources */, DB866F292261EE28003C41F6 /* LICENSE in Resources */, DB866F262261EE28003C41F6 /* Cartfile.resolved in Resources */, + DB2BBC2928B99806005AACE0 /* JWTTokenExpired.json in Resources */, E8438CA7F43C3CB6FD545187 /* VenmoAccountResponse.json in Resources */, E84382073237BBFB9B03C00C /* VenmoAccountMissingAccountId.json in Resources */, E8438DB98656EB3F69DE0776 /* VenmoAccountWrongFormatAccountId.json in Resources */, @@ -851,6 +856,7 @@ DBCA37AE225D6D1700CD4137 /* BankAccountIndividualResponse.json in Resources */, 644A69C122B0F7640058E77E /* WireAccountBusinessResponse.json in Resources */, DBCA37B2225D6D1700CD4137 /* BankCardErrorResponseWithInvalidCardNumber.json in Resources */, + DB2BBC2A28B99806005AACE0 /* JWTTokenExpired.json in Resources */, 64278CB922C3C8EF00B8736B /* ListTransferResponse.json in Resources */, 641EBE9E2292E2B600D718F4 /* BankAccountBusinessResponse.json in Resources */, DBCA37B1225D6D1700CD4137 /* TransferMethodConfigurationGraphQlResponse.json in Resources */, @@ -889,7 +895,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\nset -x\n\nif ! which swiftlint > /dev/null; then\necho \"warning: SwiftLint is not installed. To install run `brew install SwiftLint`\"\nelse\nswiftlint autocorrect\nswiftlint\nfi\n"; + shellScript = "set -e\nset -x\n\nif ! which swiftlint > /dev/null; then\necho \"warning: SwiftLint is not installed. To install run `brew install SwiftLint`\"\nelse\nswiftlint --fix\nswiftlint\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1147,7 +1153,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TAG_VERSION = "1.0.0-beta16"; + TAG_VERSION = "1.0.0-beta17"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1177,7 +1183,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; - TAG_VERSION = "1.0.0-beta16"; + TAG_VERSION = "1.0.0-beta17"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -1192,7 +1198,7 @@ DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", + "$(PROJECT_DIR)/Carthage/Build", ); INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -1219,7 +1225,7 @@ DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", + "$(PROJECT_DIR)/Carthage/Build", ); INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; diff --git a/README.md b/README.md index d78e8eb4..62497014 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ Use [Carthage](https://github.com/Carthage/Carthage) or [CocoaPods](https://coco ### Carthage Specify it in your Cartfile: ```ogdl -github "hyperwallet/hyperwallet-ios-sdk" "1.0.0-beta16" +github "hyperwallet/hyperwallet-ios-sdk" "1.0.0-beta17" ``` ### CocoaPods Specify it in your Podfile: ```ruby -pod 'HyperwalletSDK', '~> 1.0.0-beta16' +pod 'HyperwalletSDK', '~> 1.0.0-beta17' ``` ## Initialization @@ -701,7 +701,7 @@ Hyperwallet } guard let result = result else { return } - + // Get transfer method types based on the first country code and its first currency code transferMethodTypes = result.transferMethodTypes(countryCode: country, currencyCode: currency) print(transferMethodTypes) diff --git a/Sources/HyperwalletError.swift b/Sources/HyperwalletError.swift index 9dd00ad3..9cf89a13 100644 --- a/Sources/HyperwalletError.swift +++ b/Sources/HyperwalletError.swift @@ -106,9 +106,7 @@ public enum HyperwalletErrorType: Error, LocalizedError { public var group: HyperwalletErrorGroup { switch self { case .http(_, let httpCode): - return httpCode == 400 - ? HyperwalletErrorGroup.business - : HyperwalletErrorGroup.unexpected + return mapErrorGroupFor(httpCode) case .parseError, .notInitialized, .invalidUrl, @@ -172,6 +170,18 @@ public enum HyperwalletErrorType: Error, LocalizedError { return nil } } + + /// Maps Hyperwallet Error Group for HTTP Code + /// + /// - Parameter httpCode: the reponse HTTP code + /// - Returns the HyperwalletErrorGroup + private func mapErrorGroupFor(_ httpCode: Int) -> HyperwalletErrorGroup { + switch httpCode { + case 400: return HyperwalletErrorGroup.business + case 401: return HyperwalletErrorGroup.authentication + default: return HyperwalletErrorGroup.unexpected + } + } } /// The `HyperwalletAuthenticationErrorType` is the authentication error type returned By Hyperwallet SDK. diff --git a/Sources/Info.plist b/Sources/Info.plist index 9e5bf896..3d0773d0 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -21,6 +21,6 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) TAG_VERSION - 1.0.0-beta16 + 1.0.0-beta17 diff --git a/Tests/HTTPTransactionTests.swift b/Tests/HTTPTransactionTests.swift index f2dbfc82..07be2320 100644 --- a/Tests/HTTPTransactionTests.swift +++ b/Tests/HTTPTransactionTests.swift @@ -30,7 +30,7 @@ class HTTPTransactionTests: XCTestCase { } func testPerformRest_newConfiguration() { - // Given + // Given var hasRequestTransactionPerformed = false // When - an API call request is made let completionHandler = {(_: [String: String]?, _: HyperwalletErrorType?) -> Void in @@ -208,6 +208,129 @@ class HTTPTransactionTests: XCTestCase { XCTAssertNil(hyperwalletError) } + func testPerformGraphQl_HTTP401_returnAuthenticationErrorGroup() { + // Given - SDK is initialized + var response: Connection? + var hyperwalletError: HyperwalletErrorType? + + let request = HyperwalletTransferMethodConfigurationFieldQuery(country: "AR", + currency: "ARS", + transferMethodType: "BANK_ACCOUNT", + profile: "INDIVIDUAL") + + httpClientMock.data = HyperwalletTestHelper.getDataFromJson("JWTTokenExpired") + httpClientMock.urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 401, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + // When - an API call request is made + let handler = { (data: Connection?, error: HyperwalletErrorType?) + -> Void in + response = data + hyperwalletError = error + } + + transaction.performGraphQl(request, completionHandler: handler) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(hyperwalletError, "The hyperwalletError should not be null") + XCTAssertEqual(hyperwalletError?.getHttpCode(), 401) + XCTAssertEqual(hyperwalletError?.group, HyperwalletErrorGroup.authentication) + XCTAssertEqual(hyperwalletError?.getHyperwalletErrors()?.errorList?.first?.code, "JWT_EXPIRED") + XCTAssertEqual(hyperwalletError?.getHyperwalletErrors()?.errorList?.first?.message, + "JWT expired") + } + + func testPerformGraphQl_HTTP403_returnUnexpectedErrorGroup() { + // Given - SDK is initialized + + var response: Connection? + var hyperwalletError: HyperwalletErrorType? + let request = HyperwalletTransferMethodConfigurationFieldQuery(country: "AR", + currency: "ARS", + transferMethodType: "BANK_ACCOUNT", + profile: "INDIVIDUAL") + httpClientMock.data = "{}".data(using: .utf8) + httpClientMock.urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 403, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + // When - an API call request is made + let handler = { (data: Connection?, error: HyperwalletErrorType?) + -> Void in + response = data + hyperwalletError = error + } + + transaction.performGraphQl(request, completionHandler: handler) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(hyperwalletError, "The hyperwalletError should not be null") + XCTAssertEqual(hyperwalletError?.getHttpCode(), 403) + XCTAssertEqual(hyperwalletError?.group, HyperwalletErrorGroup.unexpected) + } + + func testPerformRest_HTTP401_returnAuthenticationErrorGroup() { + // Given - SDK is initialized + + var response: [String: String]? + var hyperwalletError: HyperwalletErrorType? + + httpClientMock.data = HyperwalletTestHelper.getDataFromJson("JWTTokenExpired") + httpClientMock.urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 401, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + // When - an API call request is made + let completionHandler = { (data: [String: String]?, error: HyperwalletErrorType?) -> Void in + response = data + hyperwalletError = error + } + + transaction.performRest(httpMethod: .post, urlPath: "", payload: "", completionHandler: completionHandler) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(hyperwalletError, "The hyperwalletError should not be null") + XCTAssertEqual(hyperwalletError?.getHttpCode(), 401) + XCTAssertEqual(hyperwalletError?.group, HyperwalletErrorGroup.authentication) + XCTAssertEqual(hyperwalletError?.getHyperwalletErrors()?.errorList?.first?.code, "JWT_EXPIRED") + XCTAssertEqual(hyperwalletError?.getHyperwalletErrors()?.errorList?.first?.message, + "JWT expired") + } + + func testPerformRest_HTTP403_returnUnexpectedErrorGroup() { + // Given - SDK is initialized + + var response: [String: String]? + var hyperwalletError: HyperwalletErrorType? + + httpClientMock.data = "{}".data(using: .utf8) + httpClientMock.urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 403, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + // When - an API call request is made + let completionHandler = { (data: [String: String]?, error: HyperwalletErrorType?) -> Void in + response = data + hyperwalletError = error + } + + transaction.performRest(httpMethod: .post, urlPath: "", payload: "", completionHandler: completionHandler) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(hyperwalletError, "The hyperwalletError should not be null") + XCTAssertEqual(hyperwalletError?.getHttpCode(), 403) + XCTAssertEqual(hyperwalletError?.group, HyperwalletErrorGroup.unexpected) + } + func testPerformRest_HTTP204_returnEmptyResponseAndError() { // Given - SDK is initialized @@ -466,6 +589,63 @@ class HTTPTransactionTests: XCTestCase { "Invalid field length for cardNumber") } + func testRequestHandler_httpCodeUnauthorized_hyperwalletError() { + // Given + var response: [String: String]? + var errorType: HyperwalletErrorType? + let urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 401, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + let data = HyperwalletTestHelper.getDataFromJson("JWTTokenExpired") + + // When + let completionHandler = {(data: [String: String]?, error: HyperwalletErrorType?) -> Void in + response = data + errorType = error + } + + let requestHandler = HTTPTransaction.requestHandler(completionHandler) + + requestHandler(data, urlResponse, nil) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(errorType, "The hyperwalletError should not be null") + XCTAssertEqual(errorType?.getHttpCode(), 401) + XCTAssertEqual(errorType?.group, HyperwalletErrorGroup.authentication) + XCTAssertEqual(errorType?.getHyperwalletErrors()?.errorList?.first?.code, "JWT_EXPIRED") + XCTAssertEqual(errorType?.getHyperwalletErrors()?.errorList?.first?.message, + "JWT expired") + } + + func testRequestHandler_httpCodeForbiddenAccess_hyperwalletError() { + // Given + var response: [String: String]? + var errorType: HyperwalletErrorType? + let urlResponse = HTTPURLResponse(url: URL(string: "http://localhost")!, + statusCode: 403, + httpVersion: "post", + headerFields: ["Content-type": "application/json"]) + + // When + let completionHandler = {(data: [String: String]?, error: HyperwalletErrorType?) -> Void in + response = data + errorType = error + } + + let requestHandler = HTTPTransaction.requestHandler(completionHandler) + + requestHandler("{}".data(using: .utf8)!, urlResponse, nil) + + // Then + XCTAssertNil(response, "The response should be null") + XCTAssertNotNil(errorType, "The hyperwalletError should not be null") + XCTAssertEqual(errorType?.getHttpCode(), 403) + XCTAssertEqual(errorType?.group, HyperwalletErrorGroup.unexpected) + } + func testRequestHandler_httpCodeSuccessWithJSONFormatPayload_parseData() { var response: [String: String]? var errorType: HyperwalletErrorType? diff --git a/Tests/HyperwalletErrorTests.swift b/Tests/HyperwalletErrorTests.swift index 7e35cc08..a5b30df6 100644 --- a/Tests/HyperwalletErrorTests.swift +++ b/Tests/HyperwalletErrorTests.swift @@ -78,11 +78,20 @@ class HyperwalletErrorTests: XCTestCase { "Error message should be 'Error message'") } - func testHyperwalletError_generalHttpError() { + func testHyperwalletError_unauthorizedHttpError() { let hyperwalletError = HyperwalletError(message: "Please check your login credentials and try again", code: "INCORRECT_LOGIN_CREDENTIALS") let testErrorTypeHttp = HyperwalletErrorType.http(HyperwalletErrors(errorList: [hyperwalletError]), 401) + XCTAssertNotNil(testErrorTypeHttp) + XCTAssertEqual(testErrorTypeHttp.group, HyperwalletErrorGroup.authentication) + } + + func testHyperwalletError_forbiddenAccessHttpError() { + let hyperwalletError = HyperwalletError(message: "Please check your login credentials and try again", + code: "FORBIDDEN_ACCESS") + let testErrorTypeHttp = HyperwalletErrorType.http(HyperwalletErrors(errorList: [hyperwalletError]), 403) + XCTAssertNotNil(testErrorTypeHttp) XCTAssertEqual(testErrorTypeHttp.group, HyperwalletErrorGroup.unexpected) } diff --git a/Tests/Responses/JWTTokenExpired.json b/Tests/Responses/JWTTokenExpired.json new file mode 100644 index 00000000..a9cfa9c0 --- /dev/null +++ b/Tests/Responses/JWTTokenExpired.json @@ -0,0 +1,8 @@ +{ + "errors":[ + { + "message":"JWT expired", + "code":"JWT_EXPIRED" + } + ] +}