diff --git a/ForageSDK.podspec b/ForageSDK.podspec index 2756d514..711a1b68 100644 --- a/ForageSDK.podspec +++ b/ForageSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = "ForageSDK" - spec.version = "4.4.3" + spec.version = "4.4.4" spec.summary = "ForageSDK" spec.description = "The ForageSDK process Electronic Benefit Transfer (EBT) payments in your e-commerce application." spec.homepage = "https://github.com/teamforage/forage-ios-sdk" @@ -9,7 +9,7 @@ Pod::Spec.new do |spec| spec.author = { "Rob Gormisky" => "rob@joinforage.com" } spec.platform = :ios, "13.0" spec.readme = "https://raw.githubusercontent.com/teamforage/forage-ios-sdk/main/README.md" - spec.source = { :git => "https://github.com/teamforage/forage-ios-sdk.git", :tag => "4.4.3" } + spec.source = { :git => "https://github.com/teamforage/forage-ios-sdk.git", :tag => "4.4.4" } spec.source_files = ["Sources/ForageSDK/**/*.swift", "DatadogPrivate-Objc/**/*.{h,m}"] spec.dependency 'VGSCollectSDK', '1.15.3' spec.dependency 'LaunchDarkly', '9.6.0' diff --git a/README.md b/README.md index 7cbaac89..d4be336d 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ pod init 3. Add the following line to your `Podfile`: ```swift -pod 'ForageSDK', '~> 4.4.3' +pod 'ForageSDK', '~> 4.4.4' ``` 4. Run the following command diff --git a/Sources/ForageSDK/ForageSDK.swift b/Sources/ForageSDK/ForageSDK.swift index 6d4bb813..adf974cd 100644 --- a/Sources/ForageSDK/ForageSDK.swift +++ b/Sources/ForageSDK/ForageSDK.swift @@ -18,7 +18,7 @@ public class ForageSDK { public var environment: Environment = .sandbox // Don't update! Only updated when releasing. - public static let version = "4.4.3" + public static let version = "4.4.4" public static let shared = ForageSDK() // MARK: Init diff --git a/Sources/ForageSDK/Foundation/Network/Collector.swift b/Sources/ForageSDK/Foundation/Network/Collector.swift index fcbcbe8c..42d49bea 100644 --- a/Sources/ForageSDK/Foundation/Network/Collector.swift +++ b/Sources/ForageSDK/Foundation/Network/Collector.swift @@ -68,11 +68,11 @@ class VGSCollectWrapper: VaultCollector { mutableHeaders["X-KEY"] = xKey["vgsXKey"] vgsCollect.customHeaders = mutableHeaders } - - internal func handleResponse(code: Int, data: Data?, error: Error?, measurement: NetworkMonitor, completion: (T?, ForageError?) -> Void) { + + func handleResponse(code: Int, data: Data?, error: Error?, measurement: NetworkMonitor, completion: (T?, ForageError?) -> Void) { measurement.end() measurement.setHttpStatusCode(code).logResult() - + // If an error is explicitly returned from VGS, log the error and return if let error = error { logger?.critical( @@ -92,7 +92,7 @@ class VGSCollectWrapper: VaultCollector { ) return completion(nil, CommonErrors.UNKNOWN_SERVER_ERROR) } - + // If the response was a Forage error (ex. 429 throttled), catch it here and return if let forageServiceError = try? JSONDecoder().decode(ForageServiceError.self, from: data) { let forageCode = forageServiceError.errors[0].code @@ -103,13 +103,13 @@ class VGSCollectWrapper: VaultCollector { message: message )) } - + // If the code is a 204, we got a successful response from the deferred capture flow. // In this scenario, we should just return - if (code == 204) { + if code == 204 { return completion(nil, nil) } - + // Try to decode the response and return the expected object do { let decoder = JSONDecoder() @@ -238,8 +238,8 @@ class BasisTheoryWrapper: VaultCollector { } } } - - internal func handleResponse(response: URLResponse?, data: JSON?, error: Error?, measurement: NetworkMonitor, completion: (T?, ForageError?) -> Void) { + + func handleResponse(response: URLResponse?, data: JSON?, error: Error?, measurement: NetworkMonitor, completion: (T?, ForageError?) -> Void) { measurement.end() let httpStatusCode = (response as? HTTPURLResponse)?.statusCode @@ -253,20 +253,20 @@ class BasisTheoryWrapper: VaultCollector { ]) return completion(nil, CommonErrors.UNKNOWN_SERVER_ERROR) } - + guard let data = data else { logger?.critical("Basis Theory failed to respond with a data object", error: nil, attributes: [ "http_status": httpStatusCode ]) return completion(nil, CommonErrors.UNKNOWN_SERVER_ERROR) } - + // If the code is a 204, we got a successful response from the deferred capture flow. // In this scenario, we should just return - if (httpStatusCode == 204) { + if httpStatusCode == 204 { return completion(nil, nil) } - + // If we found `proxy_error` in the response, we know there was an issue with the BT proxy. // We can't currently access any of the information returned from BT in this state. if data["proxy_error"] != nil { @@ -275,7 +275,7 @@ class BasisTheoryWrapper: VaultCollector { ]) return completion(nil, CommonErrors.UNKNOWN_SERVER_ERROR) } - + let dataObject: Data // Try to decode the response and return the expected object do { @@ -290,7 +290,7 @@ class BasisTheoryWrapper: VaultCollector { ) return completion(nil, CommonErrors.UNKNOWN_SERVER_ERROR) } - + // If the response was a Forage error (ex. 429 throttled), catch it here and return if let forageServiceError = try? JSONDecoder().decode(ForageServiceError.self, from: dataObject) { let forageCode = forageServiceError.errors[0].code @@ -301,7 +301,7 @@ class BasisTheoryWrapper: VaultCollector { message: message )) } - + // Try to decode the response and return the expected object do { let decoder = JSONDecoder() diff --git a/Sources/ForageSDK/Foundation/Network/ForageAPI.swift b/Sources/ForageSDK/Foundation/Network/ForageAPI.swift index 17d52e7c..296bd3d8 100644 --- a/Sources/ForageSDK/Foundation/Network/ForageAPI.swift +++ b/Sources/ForageSDK/Foundation/Network/ForageAPI.swift @@ -44,7 +44,7 @@ extension ForageAPI: ServiceProtocol { "content-type": "application/json", "accept": "application/json", "x-datadog-trace-id": ForageSDK.shared.traceId, - "API-VERSION": "default" + "API-VERSION": "default", ]) switch self { case let .tokenizeNumber( diff --git a/Sources/ForageSDK/Foundation/Network/LiveForageService.swift b/Sources/ForageSDK/Foundation/Network/LiveForageService.swift index 3a4886b7..d10a6e69 100644 --- a/Sources/ForageSDK/Foundation/Network/LiveForageService.swift +++ b/Sources/ForageSDK/Foundation/Network/LiveForageService.swift @@ -44,7 +44,7 @@ class LiveForageService: ForageService { do { // If any of the preamble requests fail, return back a generic response to the user let balanceRequest = try await createRequestModel(using: getTokenFromPaymentMethod, tokenRef: paymentMethodReference) - + // If the vault request fails for some unforeseen reason, return back a generic response to the user rawBalanceModel = try await submitPinToVault( pinCollector: pinCollector, @@ -56,7 +56,7 @@ class LiveForageService: ForageService { } catch { throw error } - + guard let rawBalanceModel = rawBalanceModel else { logger?.critical( "Received malformed Vault response", @@ -65,12 +65,12 @@ class LiveForageService: ForageService { ) throw CommonErrors.UNKNOWN_SERVER_ERROR } - + // Return the balance back to the user if let balance = rawBalanceModel.balance { return balance } - + // ELSE return back the expected EBT Network error to the user if let vaultError = rawBalanceModel.error { let forageError = ForageError.create( @@ -110,7 +110,7 @@ class LiveForageService: ForageService { } catch { throw error } - + guard let paymentResponse = paymentResponse else { logger?.critical( "Received malformed Vault response", @@ -119,7 +119,7 @@ class LiveForageService: ForageService { ) throw CommonErrors.UNKNOWN_SERVER_ERROR } - + // Return back the expected EBT Network error to the user if let vaultError = paymentResponse.error { let forageError = ForageError.create( @@ -129,18 +129,18 @@ class LiveForageService: ForageService { ) throw forageError } - + return paymentResponse } - + typealias CollectTokenFunc = (_ sessionToken: String, _ merchantID: String, _ reference: String) async throws -> String // MARK: Collect PIN - + func collectPinForDeferredCapture( pinCollector: VaultCollector, paymentReference: String - ) async throws -> Void { + ) async throws { do { let _: Empty? = try await collectPinForPayment( pinCollector: pinCollector, @@ -151,14 +151,13 @@ class LiveForageService: ForageService { } catch { throw error } - } // MARK: Private structs - + /// `Empty` used to signify a generic, decodable type that returns nothing private struct Empty: Decodable {} - + /// `ForageRequestModel` used for compose ForageSDK requests private struct ForageRequestModel: Codable { let authorization: String @@ -168,7 +167,7 @@ class LiveForageService: ForageService { } // MARK: Private helper methods - + /// Common logic required for all requests to the proxy. private func createRequestModel( using collectTokenFunc: CollectTokenFunc, @@ -182,9 +181,9 @@ class LiveForageService: ForageService { let xKeyModel = try await awaitResult { completion in self.getXKey(sessionToken: sessionToken, merchantID: merchantID, completion: completion) } - + let token = try await collectTokenFunc(sessionToken, merchantID, tokenRef) - + return ForageRequestModel( authorization: sessionToken, cardNumberToken: token, @@ -200,7 +199,7 @@ class LiveForageService: ForageService { throw error } } - + /// Common Payment-related prologue across capturePayment and collectPin. /// Both `deferPaymentCapture` and `capturePayment` involve the same /// preliminerary data retrieval and a trip to the Vault (VGS or Basis Theory) Proxy @@ -212,7 +211,7 @@ class LiveForageService: ForageService { ) async throws -> T? { do { let collectPinRequest = try await createRequestModel(using: getTokenFromPayment, tokenRef: paymentReference) - + let basePath = "/api/payments/\(paymentReference)" return try await submitPinToVault( @@ -225,7 +224,6 @@ class LiveForageService: ForageService { } catch { throw error } - } /// Submit PIN to the Vault Proxy (Basis Theory or VGS) @@ -263,15 +261,15 @@ class LiveForageService: ForageService { continuation.resume(returning: (result, error)) } } - + if let error = error { throw error } - + return result } } - + private func getTokenFromPayment(sessionToken: String, merchantID: String, paymentRef: String) async throws -> String { do { /// We only decode what we need here using `ThinPaymentModel` @@ -286,13 +284,13 @@ class LiveForageService: ForageService { completion: completion ) } - + return try await getTokenFromPaymentMethod(sessionToken: sessionToken, merchantID: merchantID, paymentMethodRef: payment.paymentMethodRef) } catch { throw error } } - + private func getTokenFromPaymentMethod(sessionToken: String, merchantID: String, paymentMethodRef: String) async throws -> String { do { let paymentMethod = try await awaitResult { completion in @@ -303,18 +301,18 @@ class LiveForageService: ForageService { completion: completion ) } - + return paymentMethod.card.token } catch { throw error } } - - internal func getPayment(sessionToken: String, merchantID: String, paymentRef: String, completion: @escaping (Result) -> Void) { + + func getPayment(sessionToken: String, merchantID: String, paymentRef: String, completion: @escaping (Result) -> Void) { do { try provider.execute(model: T.self, endpoint: ForageAPI.getPayment(sessionToken: sessionToken, merchantID: merchantID, paymentRef: paymentRef), completion: completion) } catch { completion(.failure(error)) } } - - internal func getPaymentMethod( + + func getPaymentMethod( sessionToken: String, merchantID: String, paymentMethodRef: String, @@ -322,8 +320,8 @@ class LiveForageService: ForageService { ) { do { try provider.execute(model: PaymentMethodModel.self, endpoint: ForageAPI.getPaymentMethod(sessionToken: sessionToken, merchantID: merchantID, paymentMethodRef: paymentMethodRef), completion: completion) } catch { completion(.failure(error)) } } - - internal func getXKey(sessionToken: String, merchantID: String, completion: @escaping (Result) -> Void) { + + func getXKey(sessionToken: String, merchantID: String, completion: @escaping (Result) -> Void) { do { try provider.execute(model: ForageXKeyModel.self, endpoint: ForageAPI.xKey(sessionToken: sessionToken, merchantID: merchantID), completion: completion) } catch { completion(.failure(error)) } } } diff --git a/Sources/ForageSDK/Foundation/Network/Model/ForageErrorModel.swift b/Sources/ForageSDK/Foundation/Network/Model/ForageErrorModel.swift index b051e56f..b09a38b0 100644 --- a/Sources/ForageSDK/Foundation/Network/Model/ForageErrorModel.swift +++ b/Sources/ForageSDK/Foundation/Network/Model/ForageErrorModel.swift @@ -26,10 +26,10 @@ struct ForageErrorSource: Codable { /// Represents an error that occurs when a request to submit a `ForageElement` to the Forage API fails. public struct ForageError: Error, Codable, Equatable { - public static func == (lhs: ForageError, rhs: ForageError) -> Bool { - return lhs.code == rhs.code && lhs.httpStatusCode == rhs.httpStatusCode && lhs.message == rhs.message && lhs.details == rhs.details + public static func ==(lhs: ForageError, rhs: ForageError) -> Bool { + lhs.code == rhs.code && lhs.httpStatusCode == rhs.httpStatusCode && lhs.message == rhs.message && lhs.details == rhs.details } - + /// A short string that helps identify the cause of the error. /// [Learn more about SDK error codes](https://docs.joinforage.app/reference/errors#code-and-message-pairs-1) /// diff --git a/Sources/ForageSDK/Foundation/Network/Model/PaymentModel.swift b/Sources/ForageSDK/Foundation/Network/Model/PaymentModel.swift index ad802f5b..9667f7c8 100644 --- a/Sources/ForageSDK/Foundation/Network/Model/PaymentModel.swift +++ b/Sources/ForageSDK/Foundation/Network/Model/PaymentModel.swift @@ -80,7 +80,7 @@ public struct PaymentModel: Codable { public let merchantFixedSettlement: String? public let platformFixedSettlement: String? public let refunds: [String] - internal let error: VaultError? + let error: VaultError? private enum CodingKeys: String, CodingKey { case paymentRef = "ref" @@ -112,9 +112,9 @@ public struct PaymentModel: Codable { /// payment is updated and captured on the server-side. /// In turn, we only grab what we need from `ThinPaymentModel` for /// intermediate internal SDK requests to `GET /payments/` -internal struct ThinPaymentModel: Codable { - internal let paymentMethodRef: String - +struct ThinPaymentModel: Codable { + let paymentMethodRef: String + private enum CodingKeys: String, CodingKey { case paymentMethodRef = "payment_method" } diff --git a/Sources/ForageSDK/Foundation/Network/Protocol/ForageService.swift b/Sources/ForageSDK/Foundation/Network/Protocol/ForageService.swift index 12a34747..03a74b2a 100644 --- a/Sources/ForageSDK/Foundation/Network/Protocol/ForageService.swift +++ b/Sources/ForageSDK/Foundation/Network/Protocol/ForageService.swift @@ -49,7 +49,7 @@ protocol ForageService: AnyObject { /// - merchantID: The unique ID of the Merchant. /// - paymentRef: The reference hash of the Payment. /// - completion: The closure returns a `Result` containing either a `PaymentModel` or an `Error`. [Read more](https://docs.joinforage.app/reference/get-payment-details) - func getPayment( + func getPayment( sessionToken: String, merchantID: String, paymentRef: String, @@ -112,5 +112,5 @@ protocol ForageService: AnyObject { func collectPinForDeferredCapture( pinCollector: VaultCollector, paymentReference: String - ) async throws -> Void + ) async throws } diff --git a/Tests/ForageSDKTests/CollectorTests.swift b/Tests/ForageSDKTests/CollectorTests.swift index 3412a1ca..d4768e0a 100644 --- a/Tests/ForageSDKTests/CollectorTests.swift +++ b/Tests/ForageSDKTests/CollectorTests.swift @@ -36,78 +36,77 @@ class VaultCollectorTests: XCTestCase { XCTAssertEqual(vgsWrapper.vgsCollect.customHeaders?["X-KEY"], "VgsXKeyValue") } - + func testVGSCollectWrapper_handleResponse_vgsError() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + let vgsError = CommonErrors.UNKNOWN_SERVER_ERROR - + vgsWrapper.handleResponse(code: 500, data: nil, error: vgsError, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "VGS proxy failed with an error") } } - + func testVGSCollectWrapper_handleResponse_noData() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + vgsWrapper.handleResponse(code: 500, data: nil, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "VGS failed to respond with a data object") } } - + func testVGSCollectWrapper_handleResponse_forageServiceError() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + let forageErrorData = """ + { + "path": "test/path", + "errors": [ { - "path": "test/path", - "errors": [ - { - "code": "too_many_requests", - "message": "Request was throttled, please try again later." - } - ] + "code": "too_many_requests", + "message": "Request was throttled, please try again later." } - """.data(using: .utf8)! - - + ] + } + """.data(using: .utf8)! + vgsWrapper.handleResponse(code: 429, data: forageErrorData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, THROTTLE_ERROR) } } - + func testVGSCollectWrapper_handleResponse_204Success() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + vgsWrapper.handleResponse(code: 204, data: "".data(using: .utf8)!, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertNil(error) } } - + func testVGSCollectWrapper_handleResponse_success() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + let validData = """ - { - "id": "12345" - } - """.data(using: .utf8)! + { + "id": "12345" + } + """.data(using: .utf8)! let expectedModel = MockDecodableModel(id: "12345") vgsWrapper.handleResponse(code: 200, data: validData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in @@ -115,18 +114,18 @@ class VaultCollectorTests: XCTestCase { XCTAssertEqual(result, expectedModel) } } - + func testVGSCollectWrapper_handleResponse_invalidResponseStructure() { let config = VGSCollectConfig(id: "identifier", environment: .sandbox) let logger = MockLogger() let vgsWrapper = VGSCollectWrapper(config: config, logger: logger) - + let invalidData = """ - { - "this": "is invalid" - } - """.data(using: .utf8)! - + { + "this": "is invalid" + } + """.data(using: .utf8)! + vgsWrapper.handleResponse(code: 200, data: invalidData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) @@ -164,48 +163,48 @@ class VaultCollectorTests: XCTestCase { let resultToken = try basisTheoryWrapper.getPaymentMethodToken(paymentMethodToken: token) XCTAssertEqual(resultToken, "789012") } - - func testBasisTheoryWrapper_handleResponse_btError () { + + func testBasisTheoryWrapper_handleResponse_btError() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 500, httpVersion: nil, headerFields: nil) - + let testBTError = CommonErrors.UNKNOWN_SERVER_ERROR - + basisTheoryWrapper.handleResponse(response: response, data: nil, error: testBTError, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "Basis Theory proxy failed with an error") } } - - func testBasisTheoryWrapper_handleResponse_noData () { + + func testBasisTheoryWrapper_handleResponse_noData() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 500, httpVersion: nil, headerFields: nil) - + basisTheoryWrapper.handleResponse(response: response, data: nil, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "Basis Theory failed to respond with a data object") } } - - func testBasisTheoryWrapper_handleResponse_proxyError () { + + func testBasisTheoryWrapper_handleResponse_proxyError() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 500, httpVersion: nil, headerFields: nil) - + // THIS IS NOT THE CORRECT FORMAT. Swap all `rawValue` types for `elementValueReference`, an internal BT type that we can't replicate or access. // This mock is just for testing purposes let mockData = JSON.dictionaryValue([ - "proxy_error" : JSON.dictionaryValue([ + "proxy_error": JSON.dictionaryValue([ "errors": JSON.dictionaryValue([ "error": JSON.arrayValue([ JSON.rawValue("") @@ -216,39 +215,39 @@ class VaultCollectorTests: XCTestCase { "status": JSON.rawValue(""), "detail": JSON.rawValue("") ]) - + basisTheoryWrapper.handleResponse(response: response, data: mockData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "Basis Theory proxy script failed") } } - - func testBasisTheoryWrapper_handleResponse_204Success () { + + func testBasisTheoryWrapper_handleResponse_204Success() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 204, httpVersion: nil, headerFields: nil) - + // BT returns an empty dict let mockData = JSON.dictionaryValue([:]) - + basisTheoryWrapper.handleResponse(response: response, data: mockData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertNil(error) } } - - func testBasisTheoryWrapper_handleResponse_429Error () { + + func testBasisTheoryWrapper_handleResponse_429Error() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 429, httpVersion: nil, headerFields: nil) - + let mockData = JSON.dictionaryValue([ - "path" : JSON.rawValue("test/path"), + "path": JSON.rawValue("test/path"), "errors": JSON.arrayValue([ JSON.dictionaryValue([ "code": JSON.rawValue("too_many_requests"), @@ -256,43 +255,42 @@ class VaultCollectorTests: XCTestCase { ]) ]) ]) - + basisTheoryWrapper.handleResponse(response: response, data: mockData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, THROTTLE_ERROR) } } - - func testBasisTheoryWrapper_handleResponse_invalidData () { + + func testBasisTheoryWrapper_handleResponse_invalidData() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 429, httpVersion: nil, headerFields: nil) - + let mockData = JSON.dictionaryValue([ "this": JSON.rawValue("Is an invalid structure!") ]) - - + basisTheoryWrapper.handleResponse(response: response, data: mockData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(result) XCTAssertEqual(error, CommonErrors.UNKNOWN_SERVER_ERROR) XCTAssertEqual(logger.lastCriticalMessage, "Received an unknown response structure from Basis Theory") } } - - func testBasisTheoryWrapper_handleResponse_validData () { + + func testBasisTheoryWrapper_handleResponse_validData() { let textElement = TextElementUITextField() let config = BasisTheoryConfig(publicKey: "key1", proxyKey: "key2") let logger = MockLogger() let basisTheoryWrapper = BasisTheoryWrapper(textElement: textElement, basisTheoryconfig: config, logger: logger) let response = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 429, httpVersion: nil, headerFields: nil) - + let mockData = JSON.dictionaryValue([ "id": JSON.rawValue("12345") ]) - + basisTheoryWrapper.handleResponse(response: response, data: mockData, error: nil, measurement: TestableResponseMonitor(metricsLogger: MockLogger())) { (result: MockDecodableModel?, error: ForageError?) in XCTAssertNil(error) XCTAssertEqual(result, MockDecodableModel(id: "12345")) diff --git a/Tests/ForageSDKTests/ForagePublicSubmitMethodTests.swift b/Tests/ForageSDKTests/ForagePublicSubmitMethodTests.swift index 0df61f42..952c6ae9 100644 --- a/Tests/ForageSDKTests/ForagePublicSubmitMethodTests.swift +++ b/Tests/ForageSDKTests/ForagePublicSubmitMethodTests.swift @@ -66,7 +66,7 @@ class MockForageService: LiveForageService { override func collectPinForDeferredCapture( pinCollector: VaultCollector, paymentReference: String - ) async throws -> Void { + ) async throws { if doesCollectPinThrow { throw ForageError.create( code: "too_many_requests", diff --git a/Tests/ForageSDKTests/ForageServiceTests.swift b/Tests/ForageSDKTests/ForageServiceTests.swift index 00badfa0..4083321b 100644 --- a/Tests/ForageSDKTests/ForageServiceTests.swift +++ b/Tests/ForageSDKTests/ForageServiceTests.swift @@ -199,7 +199,7 @@ final class ForageServiceTests: XCTestCase { } wait(for: [expectation], timeout: 1.0) } - + func test_getThinPayment_onSuccess_checkExpectedPayload() { let mockSession = URLSessionMock() mockSession.data = forageMocks.capturePaymentSuccess @@ -219,7 +219,6 @@ final class ForageServiceTests: XCTestCase { wait(for: [expectation], timeout: 1.0) } - func test_getPayment_onFailure_shouldReturnFailure() { let mockSession = URLSessionMock() mockSession.error = forageMocks.getPaymentError