Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ad-hoc): additional integration tests #131

Merged
merged 12 commits into from
Jul 11, 2023
12 changes: 5 additions & 7 deletions Example/Example/Sources/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Private Methods

private func configureProcessOut() {
let configuration = ProcessOutConfiguration.test(
projectId: Constants.projectId,
privateKey: Constants.projectPrivateKey,
// swiftlint:disable force_unwrapping
apiBaseUrl: URL(string: Constants.apiBaseUrl)!,
checkoutBaseUrl: URL(string: Constants.checkoutBaseUrl)!
// swiftlint:enable force_unwrapping
// Please note that implementation is using factory method (part of private interface) that creates
// configuration with private key. It is only done for demonstration/testing purposes to avoid setting
// up test server and shouldn't be shipped with production code.
let configuration = ProcessOutConfiguration.production(
projectId: Constants.projectId, privateKey: Constants.projectPrivateKey
)
ProcessOut.configure(configuration: configuration)
}
Expand Down
2 changes: 0 additions & 2 deletions Example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ Project demonstrates multiple flows that ProcessOut framework is capable of.
projectId: test-proj_K3Ur9LQzcKtm4zttWJ7oAKHgqdiwboAw
projectPrivateKey: key_test_RE14RLcNikkP5ZXMn84BFYApwotD05Kc
customerId: cust_dCFEWBwqWrBFYAtkRIpILCynNqfhLQWX
apiBaseUrl: https://api.processout.com
checkoutBaseUrl: https://checkout.processout.com
```

2. Run `./Scripts/BootstrapProject.sh` script.
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ Before going further please make sure that you have installed all dependencies s
> ```yml
> projectId: test-proj_K3Ur9LQzcKtm4zttWJ7oAKHgqdiwboAw
> projectPrivateKey: key_test_RE14RLcNikkP5ZXMn84BFYApwotD05Kc
> apiBaseUrl: https://api.processout.com
> checkoutBaseUrl: https://checkout.processout.com
> customerId: cust_dCFEWBwqWrBFYAtkRIpILCynNqfhLQWX
> ```

### Running tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,22 @@ public struct ProcessOutConfiguration {
public let privateKey: String?

/// Api base URL.
let apiBaseUrl: URL
let apiBaseUrl = URL(string: "https://api.processout.com")! // swiftlint:disable:this force_unwrapping

/// Checkout base URL.
let checkoutBaseUrl: URL
let checkoutBaseUrl = URL(string: "https://checkout.processout.com")! // swiftlint:disable:this force_unwrapping
}

extension ProcessOutConfiguration {

/// Creates production configuration.
public static func production(projectId: String, appVersion: String? = nil, isDebug: Bool = false) -> Self {
// swiftlint:disable force_unwrapping
let apiBaseUrl = URL(string: "https://api.processout.com")!
let checkoutBaseUrl = URL(string: "https://checkout.processout.com")!
// swiftlint:enable force_unwrapping
return ProcessOutConfiguration(
projectId: projectId,
appVersion: appVersion,
isDebug: isDebug,
privateKey: nil,
apiBaseUrl: apiBaseUrl,
checkoutBaseUrl: checkoutBaseUrl
)
ProcessOutConfiguration(projectId: projectId, appVersion: appVersion, isDebug: isDebug, privateKey: nil)
}

/// Creates test configuration.
/// Creates debug production configuration with optional private key.
@_spi(PO)
public static func test(projectId: String, privateKey: String?, apiBaseUrl: URL, checkoutBaseUrl: URL) -> Self {
ProcessOutConfiguration(
projectId: projectId,
appVersion: nil,
isDebug: true,
privateKey: privateKey,
apiBaseUrl: apiBaseUrl,
checkoutBaseUrl: checkoutBaseUrl
)
public static func production(projectId: String, privateKey: String? = nil) -> Self {
ProcessOutConfiguration(projectId: projectId, appVersion: nil, isDebug: true, privateKey: privateKey)
}
}
8 changes: 4 additions & 4 deletions Sources/ProcessOut/Sources/Api/ProcessOut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ public final class ProcessOut {
/// Event emitter to use for events exchange.
private(set) lazy var eventEmitter: EventEmitter = LocalEventEmitter()

init(configuration: ProcessOutConfiguration) {
self.configuration = configuration
}

// MARK: - Private Nested Types

private enum Constants {
Expand Down Expand Up @@ -155,10 +159,6 @@ public final class ProcessOut {

// MARK: - Private Methods

private init(configuration: ProcessOutConfiguration) {
self.configuration = configuration
}

private func createLogger(for category: String, includeRemoteDestination: Bool = true) -> POLogger {
let destinations: [LoggerDestination] = [
SystemLoggerDestination(subsystem: Constants.bundleIdentifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ final class DefaultHttpConnectorRequestMapper: HttpConnectorRequestMapper {
if let privateKey = configuration.privateKey {
value += privateKey
} else {
preconditionFailure("Private key is required by '\(request.id)' request but not set")
preconditionFailure("Private key is required by '\(request)' request but not set")
}
}
return "Basic " + Data(value.utf8).base64EncodedString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ final class HttpCustomerTokensRepository: CustomerTokensRepository {
}
let httpRequest = HttpConnectorRequest<Response>.post(
path: "/customers/\(request.customerId)/tokens",
body: request,
includesDeviceMetadata: true,
requiresPrivateKey: true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
import Foundation

@_spi(PO)
public struct POCreateCustomerTokenRequest {
public struct POCreateCustomerTokenRequest: Encodable {

/// Customer id to associate created token with.
public let customerId: String
@POImmutableExcludedCodable
public var customerId: String

public init(customerId: String) {
self.customerId = customerId
/// Flag if you wish to verify the customer token by making zero value transaction. Applicable for cards only.
public let verify: Bool

public init(customerId: String, verify: Bool = false) {
self._customerId = .init(value: customerId)
self.verify = verify
}
}
Empty file.
35 changes: 0 additions & 35 deletions Tests/ProcessOutTests/Resources/CardsRepositoryTokenize200.json

This file was deleted.

This file was deleted.

29 changes: 29 additions & 0 deletions Tests/ProcessOutTests/Sources/Core/Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Utils.swift
// ProcessOut
//
// Created by Andrii Vysotskyi on 07.07.2023.
//

import XCTest

/// Asserts that an expression throws an error.
/// - Parameters:
/// - expression: An expression that can throw an error.
/// - message: An optional description of a failure.
/// - errorHandler: An optional handler for errors that expression throws.
func assertThrowsError<T>(
_ expression: @autoclosure () async throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line,
_ errorHandler: (_ error: Error) -> Void = { _ in }
) async {
do {
_ = try await expression()
} catch {
errorHandler(error)
return
}
XCTFail(message(), file: file, line: line)
}
69 changes: 46 additions & 23 deletions Tests/ProcessOutTests/Sources/Integration/CardsServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@

import Foundation
import XCTest
@_spi(PO) import ProcessOut
@testable import ProcessOut

final class CardsServiceTests: XCTestCase {
@MainActor final class CardsServiceTests: XCTestCase {

override func setUp() {
super.setUp()
let configuration = ProcessOutConfiguration.test(
projectId: Constants.projectId,
privateKey: Constants.projectPrivateKey,
apiBaseUrl: URL(string: Constants.apiBaseUrl)!,
checkoutBaseUrl: URL(string: Constants.checkoutBaseUrl)!
)
ProcessOut.configure(configuration: configuration)
sut = ProcessOut.shared.cards
let configuration = ProcessOutConfiguration.production(projectId: Constants.projectId)
sut = ProcessOut(configuration: configuration).cards
}

// MARK: - Tests

func test_issuerInformation() async throws {
// When
let information = try await sut.issuerInformation(iin: "400012")
Expand All @@ -35,32 +31,59 @@ final class CardsServiceTests: XCTestCase {
XCTAssertEqual(information.type, "debit")
}

func test_tokenizeRequest_returnsCard() async throws {
// Given
let request = POCardTokenizationRequest(
number: "4242424242424242", expMonth: 12, expYear: 40, cvc: "737"
)

// When
let card = try await sut.tokenize(request: request)

// Then
XCTAssertEqual(card.last4Digits, "4242")
XCTAssertEqual(card.expMonth, 12)
XCTAssertEqual(card.expYear, 2040)
}

func test_issuerInformation_whenIinIsTooShort_throws() async {
do {
// When
_ = try await sut.issuerInformation(iin: "4")
} catch {
return
// Given
let iin = "4"

// When
let issuerInformation = {
try await self.sut.issuerInformation(iin: iin)
}

// Then
XCTFail("IIN with length less than 6 symbols should be invalid")
await assertThrowsError(try await issuerInformation(), "IIN with length less than 6 symbols should be invalid")
}

func test_tokenizeRequest_returnsCard() async throws {
func test_tokenizeRequest_whenNumberIsInvalid_throwsError() async {
// Given
let request = POCardTokenizationRequest(number: "4242424242424242", expMonth: 12, expYear: 40, cvc: "737")
let request = POCardTokenizationRequest(number: "", expMonth: 12, expYear: 40, cvc: "737")

// When
let card = try await sut.tokenize(request: request)
let card = {
try await self.sut.tokenize(request: request)
}

// Then
XCTAssertEqual(card.last4Digits, "4242")
XCTAssertEqual(card.expMonth, 12)
XCTAssertEqual(card.expYear, 2040)
await assertThrowsError(try await card(), "Unexpected success, card number is invalid")
}

// MARK: - Tests
func test_updateCard() async throws {
// Given
let card = try await sut.tokenize(
request: .init(number: "4242424242424242", expMonth: 12, expYear: 40, cvc: "737")
)
let cardUpdateRequest = POCardUpdateRequest(cardId: card.id, cvc: "123")

// When
_ = try await sut.updateCard(request: cardUpdateRequest)
}

// MARK: - Private Properties

private var sut: POCardsService!
}
Loading