Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Commit

Permalink
Fixes tests for #687.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashfurrow committed Mar 6, 2018
1 parent 4b3c408 commit 43d0b1d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 50 deletions.
27 changes: 19 additions & 8 deletions Kiosk/App/CardHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,26 @@ import CardFlight
class CardHandler: NSObject, CFTTransactionDelegate {

private let _cardStatus = PublishSubject<String>()
private var transaction: CFTTransaction?
private let _userMessages = PublishSubject<String>()
private var cardReader: CFTCardReaderInfo?

var transaction: CFTTransaction?

var cardStatus: Observable<String> {
return _cardStatus.asObservable()
}
// TODO: Surface these messages to the user in the Kiosk UI.
var userMessages: Observable<String> {
// User messages are things like "Swipe card", "processing", or "Swipe card again". Due to a problem with the
// CardFlight SDK, the user is prompted to access processing for card tokenization, which is provides a
// unfriendly user experience. So we auto-accept these requests and filter out confirmation messages, which
// don't apply to tokenization flows, until this issue is fixed: https://github.com/CardFlight/cardflight-v4-ios/issues/4
return _userMessages
.asObservable()
.filter { message -> Bool in
!message.hasSuffix("?")
}
}

var cardFlightCredentials: CFTCredentials {
let credentials = CFTCredentials()
Expand Down Expand Up @@ -95,19 +109,16 @@ class CardHandler: NSObject, CFTTransactionDelegate {
func transaction(_ transaction: CFTTransaction, didRequestProcessOption cardInfo: CFTCardInfo) {
logger.log("Received request for processing option, will process transaction.")
_cardStatus.onNext("Request for process option, automatically processing...")
// We auto-accept the process option on the user's behalf because the prompt doesn't make sense in a
// tokenization flow. See comments in `userMessages` property above.
transaction.select(processOption: .process)
// TODO: CardFlight docs says we're supposed to confirm with the user before proceeding with the transaction,
// but that's silly because we're only tokenizing and not not making an authorization or a charge.
}

func transaction(_ transaction: CFTTransaction, didRequestDisplay message: CFTMessage) {
let message = message.primary ?? message.secondary ?? "Unknown message"
let message = message.primary ?? message.secondary ?? ""
_userMessages.onNext(message)
logger.log("Received request to display message: \(message)")
_cardStatus.onNext("Received message for user: \(message)")
// TODO: Present message to user somehow
// TODO: We want to show the user the custom messages but not messages confirming the transaction (see above,
// it's not appropriate for tokenization). So we need to show the user _most_ messages but not messages
// confirming transactions (that don't exist).
}
}

Expand Down
108 changes: 66 additions & 42 deletions KioskTests/CardHandlerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,94 +3,118 @@ import Nimble
@testable
import Kiosk
import RxSwift
import CardFlight
import RxBlocking

class CardHandlerTests: QuickSpec {
var handler: CardHandler!
var reader: LocalCardReader!

override func spec() {
let apiKey = "jhfbsdhbfsd"
let accountToken = "dxcvxfvdfgvxcv"

var handler: CardHandler!
var transaction: LocalTransaction!
var disposeBag: DisposeBag!

beforeEach {
let manager = CFTSessionManager()
self.reader = LocalCardReader()

self.handler = CardHandler(apiKey: apiKey, accountToken: accountToken)

self.handler.reader = self.reader!
self.handler.sessionManager = manager
handler = CardHandler(apiKey: apiKey, accountToken: accountToken)
transaction = LocalTransaction(delegate: handler)
handler.transaction = transaction

disposeBag = DisposeBag()
}

pending("sets up the Cardflight API + Token") {
expect(self.handler.sessionManager.apiToken()) == apiKey
expect(self.handler.sessionManager.accountToken()) == accountToken
it("sets up the Cardflight API + Token") {
handler.startSearching()
expect(transaction.receivedTokenizationParameters?.credentials.apiKey) == apiKey
expect(transaction.receivedTokenizationParameters?.credentials.accountToken) == accountToken
}

xit("sends an observable with a card if successful") {
it("sends an observable with a card if successful") {
var success = false
self.handler
handler
.cardStatus
.subscribe(onCompleted: {
success = true
})
.disposed(by: disposeBag)

self.handler.startSearching()
handler.startSearching()
expect(success) == true
expect(handler.card).toNot( beNil() )
}

xit("sends an observable with an error if failed") {
self.reader.fail = true
it("sends an observable with an error if failed") {
transaction.fail = true

var failed = false
self.handler
handler
.cardStatus
.subscribe(onError: { _ in
failed = true
.subscribe(onNext: { message in
failed = failed || message.contains("Error")
})
.disposed(by: disposeBag)

self.handler!.startSearching()
handler!.startSearching()
expect(failed) == true
}

xit("passes messages along the card observable as things are moving") {
var messageCount = 0

self.handler!
.cardStatus
.subscribe(onNext: { (message) in
messageCount = messageCount + 1
})
.disposed(by: disposeBag)
it("passes user messages through an observable") {
var messages = Array<String>()
handler.userMessages.subscribe(onNext: { (message) in
messages.append(message)
}).disposed(by: disposeBag)

self.handler!.readerIsAttached()
self.handler!.readerIsConnecting()
self.handler!.readerIsDisconnected()
self.handler!.readerSwipeDidCancel()
self.handler!.readerGenericResponse("string")
let message = CFTMessage()
message.setValue("Hello there", forKey: "primary")
handler.transaction(transaction, didRequestDisplay: message)

expect(messageCount) == 5
expect(messages) == ["Hello there"]
}

it("filters user messages that end in a question mark") {
var messages = Array<String>()
handler.userMessages.subscribe(onNext: { (message) in
messages.append(message)
}).disposed(by: disposeBag)

let message = CFTMessage()
message.setValue("Process ARTSYCARD 1234?", forKey: "primary")
handler.transaction(transaction, didRequestDisplay: message)

expect(messages).to( beEmpty() )
}
}
}

class LocalCardReader: CFTReader {
class LocalTransaction: CFTTransaction {
enum TestError: Swift.Error {
case error
}

var fail = false
var callCount = 0

override func beginSwipe() {
if fail {
let error = NSError(domain: "eidolon", code: 111, userInfo: nil)
self.delegate?.readerCardResponse!(nil, withError: error)
var receivedTokenizationParameters: CFTTokenizationParameters?

// This is a stub method for the _real_ beginTokenizing(tokenizationParameters:). It does not call super.
override func beginTokenizing(tokenizationParameters: CFTTokenizationParameters) {
// We only want our tests to handle this call once, since they are synchronous and we will get into an infinite
// loop otherwise.
guard callCount < 1 else { return }
callCount += 1

self.receivedTokenizationParameters = tokenizationParameters

// This object has only read-only properties, so we need to be sneaky and use ObjC.
let historicalTransaction = CFTHistoricalTransaction()
if fail {
historicalTransaction.setValue(TestError.error, forKey: "error")
} else {
self.delegate?.readerCardResponse!(CFTCard(), withError: nil)
let cardInfo = CFTCardInfo()
historicalTransaction.setValue(cardInfo, forKey: "cardInfo")
historicalTransaction.setValue("some-token", forKey: "cardToken")
}
delegate?.transaction(self, didComplete: historicalTransaction)
}
}

0 comments on commit 43d0b1d

Please sign in to comment.