diff --git a/Source/Model/Card.swift b/Source/Model/Card.swift index 55efde1..284c0d2 100644 --- a/Source/Model/Card.swift +++ b/Source/Model/Card.swift @@ -90,11 +90,23 @@ public class Card: BaseModel, Mappable { - returns: A promise with the transaction. */ public func createTransaction(transactionRequest: TransactionRequest) -> Promise { + return createTransaction(false, transactionRequest: transactionRequest) + } + + /** + Creates a transaction. + + - parameter commit: A boolean to indicate if it is to commit the transaction on the creation process. + - parameter transactionRequest: The transaction request information. + + - returns: A promise with the transaction. + */ + public func createTransaction(commit: Bool, transactionRequest: TransactionRequest) -> Promise { guard let id = self.id else { return Promise(error: UnexpectedResponseError(message: "Card id should not be nil.")) } - let request = self.adapter.buildRequest(UserCardService.createTransaction(id, transactionRequest: Mapper().toJSONString(transactionRequest, prettyPrint: false)!)) + let request = self.adapter.buildRequest(UserCardService.createTransaction(id, commit: commit, transactionRequest: Mapper().toJSONString(transactionRequest, prettyPrint: false)!)) return self.adapter.buildResponse(request) } diff --git a/Source/Model/Transaction.swift b/Source/Model/Transaction.swift index f87c2ac..ed1590b 100644 --- a/Source/Model/Transaction.swift +++ b/Source/Model/Transaction.swift @@ -1,8 +1,9 @@ import Foundation +import PromiseKit import ObjectMapper /// Transaction model. -public class Transaction: Mappable { +public class Transaction: BaseModel, Mappable { /// The id of the transaction. public private(set) var id: String? @@ -78,7 +79,7 @@ public class Transaction: Mappable { Maps the JSON to the Object. - parameter map: The object to map. - */ + */ public func mapping(map: Map) { id <- map["id"] createdAt <- map["createdAt"] @@ -93,4 +94,65 @@ public class Transaction: Mappable { type <- map["type"] } + /** + Cancel a transaction. + + - returns: A promise with the transaction. + */ + public func cancel() -> Promise { + guard let id = self.id else { + return Promise(error: UnexpectedResponseError(message: "Transaction id should not be nil.")) + } + + guard let cardId = self.origin?.cardId else { + return Promise(error: UnexpectedResponseError(message: "Origin cardId is missing from this transaction.")) + } + + guard let status = self.status else { + return Promise(error: UnexpectedResponseError(message: "Transaction status should not be nil.")) + } + + switch (status) { + case "pending": + return Promise(error: LogicError(code: nil, message: "Unable to cancel uncommited transaction.")) + + case "waiting": + let request = self.adapter.buildRequest(UserCardService.cancelTransaction(cardId, transactionId: id)) + + return self.adapter.buildResponse(request) + + default: + return Promise(error: LogicError(code: nil, message: String(format: "This transaction cannot be cancelled, because the current status is %@.", status))) + } + } + + /** + Confirm a transaction. + + - parameter transactionCommit: A transactionCommitRequest with an optional transaction message. + + - returns: A promise with the transaction. + */ + public func commit(transactionCommit: TransactionCommitRequest) -> Promise { + guard let id = self.id else { + return Promise(error: UnexpectedResponseError(message: "Transaction id should not be nil.")) + } + + guard let cardId = self.origin?.cardId else { + return Promise(error: UnexpectedResponseError(message: "Origin cardId is missing from this transaction.")) + } + + guard let status = self.status else { + return Promise(error: UnexpectedResponseError(message: "Transaction status should not be nil.")) + } + + if (status != "pending") { + return Promise(error: LogicError(code: nil, message: String(format: "This transaction cannot be committed, because the current status is %@.", status))) + } + + let request = self.adapter.buildRequest(UserCardService.confirmTransaction(cardId, transactionId: id, transactionCommitRequest: Mapper().toJSONString(transactionCommit, prettyPrint: false)!)) + + return self.adapter.buildResponse(request) + } + } diff --git a/Source/Model/Transaction/Denomination.swift b/Source/Model/Transaction/Denomination.swift index 422ec76..c99bc1c 100644 --- a/Source/Model/Transaction/Denomination.swift +++ b/Source/Model/Transaction/Denomination.swift @@ -39,7 +39,11 @@ public class Denomination: Mappable { required public init?(_ map: Map) { } - /// Maps the JSON to the Object. + /** + Maps the JSON to the Object. + + - parameter map: The object to map. + */ public func mapping(map: Map) { amount <- map["amount"] currency <- map["currency"] diff --git a/Source/Model/Transaction/Destination.swift b/Source/Model/Transaction/Destination.swift index 195855f..201d1a0 100644 --- a/Source/Model/Transaction/Destination.swift +++ b/Source/Model/Transaction/Destination.swift @@ -55,7 +55,7 @@ public class Destination: Mappable { - parameter rate: The rate from the destination of the transaction. - parameter type: The type from the destination of the transaction. - parameter username: The username from the destination of the transaction. - */ + */ public init(accountId: String, cardId: String, accountType: String, amount: String, base: String, commission: String, currency: String, description: String, fee: String, rate: String, type: String, username: String) { self.accountId = accountId self.cardId = cardId diff --git a/Source/Model/Transaction/NormalizedTransaction.swift b/Source/Model/Transaction/NormalizedTransaction.swift index b51e1aa..f54c73e 100644 --- a/Source/Model/Transaction/NormalizedTransaction.swift +++ b/Source/Model/Transaction/NormalizedTransaction.swift @@ -27,7 +27,7 @@ public class NormalizedTransaction: Mappable { - parameter currency: The currency of the transaction normalized. - parameter fee: The fee of the transaction normalized. - parameter rate: The rate of the transaction normalized. - */ + */ public init(amount: String, commission: String, currency: String, fee: String, rate: String) { self.amount = amount self.commission = commission @@ -48,7 +48,7 @@ public class NormalizedTransaction: Mappable { Maps the JSON to the Object. - parameter map: The object to map. - */ + */ public func mapping(map: Map) { self.amount <- map["amount"] self.commission <- map["commission"] diff --git a/Source/Model/Transaction/Source.swift b/Source/Model/Transaction/Source.swift index e59af84..7df8570 100644 --- a/Source/Model/Transaction/Source.swift +++ b/Source/Model/Transaction/Source.swift @@ -29,7 +29,11 @@ public class Source: Mappable { required public init?(_ map: Map) { } - /// Maps the JSON to the Object. + /** + Maps the JSON to the Object. + + - parameter map: The object to map. + */ public func mapping(map: Map) { id <- map["id"] amount <- map["amount"] diff --git a/Source/Model/Transaction/TransactionCommitRequest.swift b/Source/Model/Transaction/TransactionCommitRequest.swift new file mode 100644 index 0000000..5e419c0 --- /dev/null +++ b/Source/Model/Transaction/TransactionCommitRequest.swift @@ -0,0 +1,36 @@ +import Foundation +import ObjectMapper + +/// Transaction commit request model. +public class TransactionCommitRequest: Mappable { + + /// The transaction message. + public private(set) final var message: String? + + /** + Constructor. + + - parameter message: The transaction message. + */ + public init(message: String) { + self.message = message + } + + // MARK: Required by the ObjectMapper. + + /** + Constructor. + */ + required public init?(_ map: Map) { + } + + /** + Maps the JSON to the Object. + + - parameter map: The object to map. + */ + public func mapping(map: Map) { + self.message <- map["message"] + } + +} diff --git a/Source/Service/UserCardService.swift b/Source/Service/UserCardService.swift index 2665488..8d56f5b 100644 --- a/Source/Service/UserCardService.swift +++ b/Source/Service/UserCardService.swift @@ -24,20 +24,21 @@ public class UserCardService { - returns: A request to confirm the transaction. */ - static func confirmTransaction(cardId: String, transactionId: String, message: String) -> Request { - return UpholdClient().post(String(format: "/v0/me/cards/%@/transactions/%@/commit", cardId, transactionId)).send(message) + static func confirmTransaction(cardId: String, transactionId: String, transactionCommitRequest: String) -> Request { + return UpholdClient().post(String(format: "/v0/me/cards/%@/transactions/%@/commit", cardId, transactionId)).send(transactionCommitRequest) } /** Creates a request to create a transaction. - parameter cardId: The id of the card. + - parameter commit: A boolean to indicate if it is to commit the transaction on the creation process. - parameter transactionRequest: The transaction request. - returns: A request to create a transaction. */ - static func createTransaction(cardId: String, transactionRequest: String) -> Request { - return UpholdClient().post(String(format: "/v0/me/cards/%@/transactions", cardId)).send(transactionRequest) + static func createTransaction(cardId: String, commit: Bool, transactionRequest: String) -> Request { + return UpholdClient().post(String(format: "/v0/me/cards/%@/transactions", cardId)).query(["commit": commit ? "true" : "false"]).send(transactionRequest) } /** diff --git a/Tests/Integration/Model/CardTest.swift b/Tests/Integration/Model/CardTest.swift index b2fb929..b51bbae 100644 --- a/Tests/Integration/Model/CardTest.swift +++ b/Tests/Integration/Model/CardTest.swift @@ -115,20 +115,20 @@ class CardTest: UpholdTestCase { card.createTransaction(transactionRequest).then { (transaction: Transaction) -> () in XCTAssertEqual(transaction.createdAt, "2014-08-27T00:01:11.616Z", "Failed: Wrong transaction createdAt.") - XCTAssertEqual(transaction.denomination?.amount, "0.1", "Failed: Wrong transaction denomination amount.") - XCTAssertEqual(transaction.denomination?.currency, "BTC", "Failed: Wrong transaction denomination currency.") - XCTAssertEqual(transaction.denomination?.pair, "BTCBTC", "Failed: Wrong transaction denomination pair.") - XCTAssertEqual(transaction.denomination?.rate, "1.00", "Failed: Wrong transaction denomination rate.") - XCTAssertEqual(transaction.destination?.accountId, "fuz", "Failed: Wrong transaction destination accountId.") - XCTAssertEqual(transaction.destination?.accountType, "buz", "Failed: Wrong transaction destination accountType.") - XCTAssertEqual(transaction.destination?.amount, "0.1", "Failed: Wrong transaction destination amount.") - XCTAssertEqual(transaction.destination?.base, "0.1", "Failed: Wrong transaction destination base.") - XCTAssertEqual(transaction.destination?.commission, "0.00", "Failed: Wrong transaction destination commission.") - XCTAssertEqual(transaction.destination?.currency, "BTC", "Failed: Wrong transaction destination currency.") - XCTAssertEqual(transaction.destination?.description, "foo@bar.com", "Failed: Wrong transaction destination description.") - XCTAssertEqual(transaction.destination?.fee, "0.00", "Failed: Wrong transaction destination fee.") - XCTAssertEqual(transaction.destination?.rate, "1.00", "Failed: Wrong transaction destination rate.") - XCTAssertEqual(transaction.destination?.type, "email", "Failed: Wrong transaction destination type.") + XCTAssertEqual(transaction.denomination!.amount, "0.1", "Failed: Wrong transaction denomination amount.") + XCTAssertEqual(transaction.denomination!.currency, "BTC", "Failed: Wrong transaction denomination currency.") + XCTAssertEqual(transaction.denomination!.pair, "BTCBTC", "Failed: Wrong transaction denomination pair.") + XCTAssertEqual(transaction.denomination!.rate, "1.00", "Failed: Wrong transaction denomination rate.") + XCTAssertEqual(transaction.destination!.accountId, "fuz", "Failed: Wrong transaction destination accountId.") + XCTAssertEqual(transaction.destination!.accountType, "buz", "Failed: Wrong transaction destination accountType.") + XCTAssertEqual(transaction.destination!.amount, "0.1", "Failed: Wrong transaction destination amount.") + XCTAssertEqual(transaction.destination!.base, "0.1", "Failed: Wrong transaction destination base.") + XCTAssertEqual(transaction.destination!.commission, "0.00", "Failed: Wrong transaction destination commission.") + XCTAssertEqual(transaction.destination!.currency, "BTC", "Failed: Wrong transaction destination currency.") + XCTAssertEqual(transaction.destination!.description, "foo@bar.com", "Failed: Wrong transaction destination description.") + XCTAssertEqual(transaction.destination!.fee, "0.00", "Failed: Wrong transaction destination fee.") + XCTAssertEqual(transaction.destination!.rate, "1.00", "Failed: Wrong transaction destination rate.") + XCTAssertEqual(transaction.destination!.type, "email", "Failed: Wrong transaction destination type.") XCTAssertEqual(transaction.id, "foobar", "Failed: Wrong transaction id.") XCTAssertEqual(transaction.message, "foobar", "Failed: Wrong transaction message.") XCTAssertEqual(transaction.normalized!.count, 1, "Failed: Wrong transaction normalized count.") @@ -137,25 +137,25 @@ class CardTest: UpholdTestCase { XCTAssertEqual(transaction.normalized![0].currency, "BTC", "Failed: Wrong transaction normalized currency.") XCTAssertEqual(transaction.normalized![0].fee, "1.00", "Failed: Wrong transaction normalized fee.") XCTAssertEqual(transaction.normalized![0].rate, "2.00", "Failed: Wrong transaction normalized rate.") - XCTAssertEqual(transaction.origin?.accountId, "fiz", "Failed: Wrong transaction origin accountId.") - XCTAssertEqual(transaction.origin?.cardId, "bar", "Failed: Wrong transaction origin cardId.") - XCTAssertEqual(transaction.origin?.accountType, "biz", "Failed: Wrong transaction origin accountType.") - XCTAssertEqual(transaction.origin?.amount, "0.1", "Failed: Wrong transaction origin amount.") - XCTAssertEqual(transaction.origin?.base, "0.1", "Failed: Wrong transaction origin base.") - XCTAssertEqual(transaction.origin?.commission, "0.00", "Failed: Wrong transaction origin comission.") - XCTAssertEqual(transaction.origin?.currency, "BTC", "Failed: Wrong transaction origin currency.") - XCTAssertEqual(transaction.origin?.description, "Foo Bar", "Failed: Wrong transaction origin description.") - XCTAssertEqual(transaction.origin?.fee, "0.00", "Failed: Wrong transaction origin fee.") - XCTAssertEqual(transaction.origin?.rate, "1.00", "Failed: Wrong transaction origin rate.") - XCTAssertEqual(transaction.origin?.type, "card", "Failed: Wrong transaction origin type.") - XCTAssertEqual(transaction.origin?.username, "foobar", "Failed: Wrong transaction origin username.") - XCTAssertEqual(transaction.params?.currency, "BTC", "Failed: Wrong transaction parameter currency.") - XCTAssertEqual(transaction.params?.margin, "0.00", "Failed: Wrong transaction parameter margin.") - XCTAssertEqual(transaction.params?.pair, "BTCBTC", "Failed: Wrong transaction parameter pair.") - XCTAssertEqual(transaction.params?.rate, "1.00", "Failed: Wrong transaction parameter rate.") - XCTAssertEqual(transaction.params?.refunds, "fizbiz", "Failed: Wrong transaction parameter refunds.") - XCTAssertEqual(transaction.params?.ttl, 30000, "Failed: Wrong transaction parameter ttl.") - XCTAssertEqual(transaction.params?.type, "invite", "Failed: Wrong transaction parameter type.") + XCTAssertEqual(transaction.origin!.accountId, "fiz", "Failed: Wrong transaction origin accountId.") + XCTAssertEqual(transaction.origin!.cardId, "bar", "Failed: Wrong transaction origin cardId.") + XCTAssertEqual(transaction.origin!.accountType, "biz", "Failed: Wrong transaction origin accountType.") + XCTAssertEqual(transaction.origin!.amount, "0.1", "Failed: Wrong transaction origin amount.") + XCTAssertEqual(transaction.origin!.base, "0.1", "Failed: Wrong transaction origin base.") + XCTAssertEqual(transaction.origin!.commission, "0.00", "Failed: Wrong transaction origin comission.") + XCTAssertEqual(transaction.origin!.currency, "BTC", "Failed: Wrong transaction origin currency.") + XCTAssertEqual(transaction.origin!.description, "Foo Bar", "Failed: Wrong transaction origin description.") + XCTAssertEqual(transaction.origin!.fee, "0.00", "Failed: Wrong transaction origin fee.") + XCTAssertEqual(transaction.origin!.rate, "1.00", "Failed: Wrong transaction origin rate.") + XCTAssertEqual(transaction.origin!.type, "card", "Failed: Wrong transaction origin type.") + XCTAssertEqual(transaction.origin!.username, "foobar", "Failed: Wrong transaction origin username.") + XCTAssertEqual(transaction.params!.currency, "BTC", "Failed: Wrong transaction parameter currency.") + XCTAssertEqual(transaction.params!.margin, "0.00", "Failed: Wrong transaction parameter margin.") + XCTAssertEqual(transaction.params!.pair, "BTCBTC", "Failed: Wrong transaction parameter pair.") + XCTAssertEqual(transaction.params!.rate, "1.00", "Failed: Wrong transaction parameter rate.") + XCTAssertEqual(transaction.params!.refunds, "fizbiz", "Failed: Wrong transaction parameter refunds.") + XCTAssertEqual(transaction.params!.ttl, 30000, "Failed: Wrong transaction parameter ttl.") + XCTAssertEqual(transaction.params!.type, "invite", "Failed: Wrong transaction parameter type.") XCTAssertEqual(transaction.refundedById, "foobiz", "Failed: Wrong transaction refundedById.") XCTAssertEqual(transaction.status, "pending", "Failed: Wrong transaction status.") XCTAssertEqual(transaction.type, "transfer", "Failed: Wrong transaction type.") @@ -192,6 +192,22 @@ class CardTest: UpholdTestCase { wait() } + func testCreateTransactionWithCommitShouldReturnTheTransaction() { + let card: Card = Fixtures.loadCard(nil) + card.adapter = MockRestAdapter(body: "{ \"id\": \"foobar\" }") + let expectation = expectationWithDescription("Card test: create transaction.") + let transactionDenominationRequest = TransactionDenominationRequest(amount: "foo", currency: "bar") + let transactionRequest = TransactionRequest(denomination: transactionDenominationRequest, destination: "foobar") + + card.createTransaction(true, transactionRequest: transactionRequest).then { (transaction: Transaction) -> () in + XCTAssertEqual(transaction.id, "foobar", "Failed: Wrong transaction id.") + + expectation.fulfill() + } + + wait() + } + func testGetTransactionsShouldReturnTheArrayOfTransactions() { let card: Card = Fixtures.loadCard(nil) let transactions: [Transaction] = [Fixtures.loadTransaction(["transactionId": "foobar"]), Fixtures.loadTransaction(["transactionId": "foobiz"])] diff --git a/Tests/Integration/Model/ReserveTest.swift b/Tests/Integration/Model/ReserveTest.swift index e0bcc84..76dbd38 100644 --- a/Tests/Integration/Model/ReserveTest.swift +++ b/Tests/Integration/Model/ReserveTest.swift @@ -38,16 +38,16 @@ class ReserveTest: UpholdTestCase { reserve.getLedger(0, end: 5).then { (deposits: [Deposit]) -> () in XCTAssertEqual(deposits[0].createdAt, "2015-04-20T14:57:12.398Z", "Failed: DepositMovement in currency didn't match.") - XCTAssertEqual(deposits[0].input?.amount, "foobiz", "Failed: DepositMovement in amount didn't match.") - XCTAssertEqual(deposits[0].input?.currency, "fiz", "Failed: DepositMovement in currency didn't match.") - XCTAssertEqual(deposits[0].output?.amount, "foobar", "Failed: DepositMovement out amount didn't match.") - XCTAssertEqual(deposits[0].output?.currency, "fuz", "Failed: DepositMovement out currency didn't match.") + XCTAssertEqual(deposits[0].input!.amount, "foobiz", "Failed: DepositMovement in amount didn't match.") + XCTAssertEqual(deposits[0].input!.currency, "fiz", "Failed: DepositMovement in currency didn't match.") + XCTAssertEqual(deposits[0].output!.amount, "foobar", "Failed: DepositMovement out amount didn't match.") + XCTAssertEqual(deposits[0].output!.currency, "fuz", "Failed: DepositMovement out currency didn't match.") XCTAssertEqual(deposits[0].type, "foo", "Failed: Deposit type didn't match.") XCTAssertEqual(deposits[1].createdAt, "2015-04-21T14:57:12.398Z", "Failed: CreatedAt didn't match.") - XCTAssertEqual(deposits[1].input?.amount, "foobar", "Failed: DepositMovement in amount didn't match.") - XCTAssertEqual(deposits[1].input?.currency, "biz", "Failed: DepositMovement in currency didn't match.") - XCTAssertEqual(deposits[1].output?.amount, "foobiz", "Failed: DepositMovement out amount didn't match.") - XCTAssertEqual(deposits[1].output?.currency, "buz", "Failed: DepositMovement out currency didn't match.") + XCTAssertEqual(deposits[1].input!.amount, "foobar", "Failed: DepositMovement in amount didn't match.") + XCTAssertEqual(deposits[1].input!.currency, "biz", "Failed: DepositMovement in currency didn't match.") + XCTAssertEqual(deposits[1].output!.amount, "foobiz", "Failed: DepositMovement out amount didn't match.") + XCTAssertEqual(deposits[1].output!.currency, "buz", "Failed: DepositMovement out currency didn't match.") XCTAssertEqual(deposits[1].transactionId, "foobar", "Failed: TransactionId didn't match.") XCTAssertEqual(deposits[1].type, "bar", "Failed: Deposit type didn't match.") @@ -93,23 +93,23 @@ class ReserveTest: UpholdTestCase { reserve.getStatistics().then { (statistics: [ReserveStatistics]) -> () in XCTAssertEqual(statistics[0].currency, "FOO", "Failed: Currency didn't match.") - XCTAssertEqual(statistics[0].totals?.assets, "foobar", "Failed: Totals assets didn't match.") - XCTAssertEqual(statistics[0].totals?.commissions, "foo", "Failed: Totals comissions didn't match.") - XCTAssertEqual(statistics[0].totals?.transactions, "bar", "Failed: Totals transactions didn't match.") - XCTAssertEqual(statistics[0].totals?.liabilities, "foobiz", "Failed: Totals liabilities didn't match.") - XCTAssertEqual(statistics[0].values?.first?.assets, "foobar", "Failed: Values assests didn't match.") - XCTAssertEqual(statistics[0].values?.first?.currency, "foo", "Failed: Values currency didn't match.") - XCTAssertEqual(statistics[0].values?.first?.liabilities, "bar", "Failed: Values liabilities didn't match.") - XCTAssertEqual(statistics[0].values?.first?.rate, "biz", "Failed: Values rate didn't match.") + XCTAssertEqual(statistics[0].totals!.assets, "foobar", "Failed: Totals assets didn't match.") + XCTAssertEqual(statistics[0].totals!.commissions, "foo", "Failed: Totals comissions didn't match.") + XCTAssertEqual(statistics[0].totals!.transactions, "bar", "Failed: Totals transactions didn't match.") + XCTAssertEqual(statistics[0].totals!.liabilities, "foobiz", "Failed: Totals liabilities didn't match.") + XCTAssertEqual(statistics[0].values!.first!.assets, "foobar", "Failed: Values assests didn't match.") + XCTAssertEqual(statistics[0].values!.first!.currency, "foo", "Failed: Values currency didn't match.") + XCTAssertEqual(statistics[0].values!.first!.liabilities, "bar", "Failed: Values liabilities didn't match.") + XCTAssertEqual(statistics[0].values!.first!.rate, "biz", "Failed: Values rate didn't match.") XCTAssertEqual(statistics[1].currency, "BAR", "Failed: Currency didn't match.") - XCTAssertEqual(statistics[1].totals?.assets, "fuz", "Failed: Totals assets didn't match.") - XCTAssertEqual(statistics[1].totals?.commissions, "fiz", "Failed: Totals comissions didn't match.") - XCTAssertEqual(statistics[1].totals?.transactions, "biz", "Failed: Totals transactions didn't match.") - XCTAssertEqual(statistics[1].totals?.liabilities, "buz", "Failed: Totals liabilities didn't match.") - XCTAssertEqual(statistics[1].values?.first?.assets, "foobiz", "Failed: Values assests didn't match.") - XCTAssertEqual(statistics[1].values?.first?.currency, "biz", "Failed: Values currency didn't match.") - XCTAssertEqual(statistics[1].values?.first?.liabilities, "buz", "Failed: Values liabilities didn't match.") - XCTAssertEqual(statistics[1].values?.first?.rate, "foo", "Failed: Values rate didn't match.") + XCTAssertEqual(statistics[1].totals!.assets, "fuz", "Failed: Totals assets didn't match.") + XCTAssertEqual(statistics[1].totals!.commissions, "fiz", "Failed: Totals comissions didn't match.") + XCTAssertEqual(statistics[1].totals!.transactions, "biz", "Failed: Totals transactions didn't match.") + XCTAssertEqual(statistics[1].totals!.liabilities, "buz", "Failed: Totals liabilities didn't match.") + XCTAssertEqual(statistics[1].values!.first!.assets, "foobiz", "Failed: Values assests didn't match.") + XCTAssertEqual(statistics[1].values!.first!.currency, "biz", "Failed: Values currency didn't match.") + XCTAssertEqual(statistics[1].values!.first!.liabilities, "buz", "Failed: Values liabilities didn't match.") + XCTAssertEqual(statistics[1].values!.first!.rate, "foo", "Failed: Values rate didn't match.") expectation.fulfill() } @@ -139,8 +139,8 @@ class ReserveTest: UpholdTestCase { reserve.adapter = MockRestAdapter(body: json) reserve.getTransactions(0, end: 5).then { (transaction: [Transaction]) -> () in - XCTAssertEqual(transaction.first?.id, "foobar", "Failed: TransactionId didn't match.") - XCTAssertEqual(transaction.last?.id, "foobiz", "Failed: TransactionId didn't match.") + XCTAssertEqual(transaction.first!.id, "foobar", "Failed: TransactionId didn't match.") + XCTAssertEqual(transaction.last!.id, "foobiz", "Failed: TransactionId didn't match.") expectation.fulfill() } diff --git a/Tests/Integration/Model/TransactionTest.swift b/Tests/Integration/Model/TransactionTest.swift index df4529a..b538904 100644 --- a/Tests/Integration/Model/TransactionTest.swift +++ b/Tests/Integration/Model/TransactionTest.swift @@ -1,8 +1,240 @@ import XCTest -import UpholdSdk import ObjectMapper +import PromiseKit +@testable import UpholdSdk -class TransactionTest: XCTestCase { +/// Transaction integration tests. +class TransactionTest: UpholdTestCase { + + func testCancelShouldReturnUnexpectedResponseErrorIfCardIdIsNil() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction: Transaction = Mapper().map("{ \"id\": \"foobar\" }")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.cancel() + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Origin cardId is missing from this transaction.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCancelShouldReturnUnexpectedResponseErrorIfTransactionIdIsNil() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction: Transaction = Mapper().map("{}")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.cancel() + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Transaction id should not be nil.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCancelShouldReturnUnexpectedResponseErrorIfStatusIsNil() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction: Transaction = Mapper().map("{ \"id\": \"foobar\", \"origin\": { \"CardId\": \"fiz\"} }")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.cancel() + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Transaction status should not be nil.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCancelShouldReturnLogicErrorIfStatusIsPending() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction = Fixtures.loadTransaction(["transactionStatus": "pending"]) + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.cancel() + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? LogicError else { + XCTFail("Error should be LogicError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Unable to cancel uncommited transaction.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCancelShouldReturnLogicErrorIfStatusIsNotWaiting() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction = Fixtures.loadTransaction(["transactionStatus": "foobar"]) + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.cancel() + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? LogicError else { + XCTFail("Error should be LogicError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "This transaction cannot be cancelled, because the current status is foobar.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCancelShouldReturnTheCanceledTransaction() { + let expectation = expectationWithDescription("Transaction test: cancel transaction.") + let transaction: Transaction = Fixtures.loadTransaction(["transactionId": "foobar", "transactionStatus": "waiting"]) + transaction.adapter = MockRestAdapter(body: Mapper().toJSONString(transaction)!) + + transaction.cancel().then { (transaction: Transaction) -> () in + XCTAssertEqual(transaction.id, "foobar", "Failed: Wrong transaction object.") + + expectation.fulfill() + } + + wait() + } + + func testCommitShouldReturnUnexpectedResponseErrorIfCardIdIsNil() { + let expectation = expectationWithDescription("Transaction test: commit transaction.") + let transaction: Transaction = Mapper().map("{ \"id\": \"foobar\" }")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.commit(TransactionCommitRequest(message: "foobar")) + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Origin cardId is missing from this transaction.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCommitShouldReturnUnexpectedResponseErrorIfTransactionIdIsNil() { + let expectation = expectationWithDescription("Transaction test: commit transaction.") + let transaction: Transaction = Mapper().map("{}")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.commit(TransactionCommitRequest(message: "foobar")) + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Transaction id should not be nil.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCommitShouldReturnUnexpectedResponseErrorIfStatusIsNil() { + let expectation = expectationWithDescription("Transaction test: commit transaction.") + let transaction: Transaction = Mapper().map("{ \"id\": \"foobar\", \"origin\": { \"CardId\": \"fiz\"} }")! + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.commit(TransactionCommitRequest(message: "foobar")) + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? UnexpectedResponseError else { + XCTFail("Error should be UnexpectedResponseError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "Transaction status should not be nil.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } + + func testCommitShouldReturnLogicErrorIfStatusIsNotPending() { + let expectation = expectationWithDescription("Transaction test: commit transaction.") + let transaction = Fixtures.loadTransaction(["transactionStatus": "foobar"]) + transaction.adapter = MockRestAdapter(body: "foobar") + let promise: Promise = transaction.commit(TransactionCommitRequest(message: "foobar")) + + promise.recover { (error: ErrorType) -> Promise in + guard let error = error as? LogicError else { + XCTFail("Error should be LogicError.") + + return promise + } + + XCTAssertNil(error.code, "Failed: Wrong code.") + XCTAssertEqual(error.description, "This transaction cannot be committed, because the current status is foobar.", "Failed: Wrong message.") + + expectation.fulfill() + + return promise + } + + wait() + } func testTransactionMapperShouldReturnATransaction() { let json: String = "{" + @@ -19,6 +251,7 @@ class TransactionTest: XCTestCase { "\"rate\": \"1.00\"" + "}," + "\"origin\": {" + + "\"AccountId\": \"fizbuz\"," + "\"CardId\": \"fizbiz\"," + "\"amount\": \"0.1\"," + "\"base\": \"0.1\"," + @@ -35,6 +268,7 @@ class TransactionTest: XCTestCase { "\"username\": \"fuzbuz\"" + "}," + "\"destination\": {" + + "\"AccountId\": \"fizbuz\"," + "\"CardId\": \"fuzbuz\"," + "\"amount\": \"0.1\"," + "\"base\": \"0.1\"," + @@ -52,6 +286,7 @@ class TransactionTest: XCTestCase { "\"pair\": \"BTCBTC\"," + "\"progress\": \"foo\"," + "\"rate\": \"1.00\"," + + "\"refunds\": \"fizbiz\"," + "\"ttl\": 30000," + "\"txid\": \"bar\"," + "\"type\": \"invite\"" + @@ -72,6 +307,7 @@ class TransactionTest: XCTestCase { XCTAssertEqual(transaction!.denomination!.currency!, "BTC", "Failed: Transaction denomination currency didn't match.") XCTAssertEqual(transaction!.denomination!.pair!, "BTCBTC", "Failed: Transaction denomination pair didn't match.") XCTAssertEqual(transaction!.denomination!.rate!, "1.00", "Failed: Transaction denomination rate didn't match.") + XCTAssertEqual(transaction!.destination!.accountId!, "fizbuz", "Failed: Transaction destination accountId didn't match.") XCTAssertEqual(transaction!.destination!.cardId!, "fuzbuz", "Failed: Transaction destination cardId didn't match.") XCTAssertEqual(transaction!.destination!.amount!, "0.1", "Failed: Transaction destination amount didn't match.") XCTAssertEqual(transaction!.destination!.base!, "0.1", "Failed: Transaction destination base didn't match.") @@ -83,11 +319,13 @@ class TransactionTest: XCTestCase { XCTAssertEqual(transaction!.destination!.type!, "email", "Failed: Transaction destination type didn't match.") XCTAssertEqual(transaction!.destination!.username!, "fizbiz", "Failed: Transaction destination username didn't match.") XCTAssertEqual(transaction!.message!, "foobar message", "Failed: Transaction message didn't match.") + XCTAssertEqual(transaction!.normalized!.count, 1, "Failed: Normalized didn't match.") XCTAssertEqual(transaction!.normalized![0].amount, "14.00", "Failed: Normalized amount didn't match.") XCTAssertEqual(transaction!.normalized![0].commission, "1.20", "Failed: Normalized comission didn't match.") XCTAssertEqual(transaction!.normalized![0].currency, "BTC", "Failed: Normalized currency didn't match.") XCTAssertEqual(transaction!.normalized![0].fee, "1.00", "Failed: Normalized fee didn't match.") XCTAssertEqual(transaction!.normalized![0].rate, "2.345", "Failed: Normalized rate didn't match.") + XCTAssertEqual(transaction!.origin!.accountId!, "fizbuz", "Failed: Transaction origin accountId didn't match.") XCTAssertEqual(transaction!.origin!.cardId!, "fizbiz", "Failed: Transaction origin cardId didn't match.") XCTAssertEqual(transaction!.origin!.amount!, "0.1", "Failed: Transaction origin amount didn't match.") XCTAssertEqual(transaction!.origin!.base!, "0.1", "Failed: Transaction origin base didn't match.") @@ -101,14 +339,13 @@ class TransactionTest: XCTestCase { XCTAssertEqual(transaction!.origin!.sources![0].amount!, "2.00", "Failed: Transaction origin type didn't match.") XCTAssertEqual(transaction!.origin!.type!, "card", "Failed: Transaction origin type didn't match.") XCTAssertEqual(transaction!.origin!.username!, "fuzbuz", "Failed: Transaction origin username didn't match.") - XCTAssertEqual(transaction!.params!.currency, "BTC", "Failed: Transaction parameter currency didn't match.") - XCTAssertEqual(transaction!.params!.margin, "0.00", "Failed: Transaction parameter margin didn't match.") - XCTAssertEqual(transaction!.params!.pair, "BTCBTC", "Failed: Transaction parameter pair didn't match.") - XCTAssertEqual(transaction!.params!.progress, "foo", "Failed: Transaction parameter progress didn't match.") - XCTAssertEqual(transaction!.params!.rate, "1.00", "Failed: Transaction parameter rate didn't match.") - XCTAssertEqual(transaction!.params!.ttl, 30000, "Failed: Transaction parameter ttl didn't match.") - XCTAssertEqual(transaction!.params!.txid, "bar", "Failed: Transaction parameter txid didn't match.") - XCTAssertEqual(transaction!.params!.type, "invite", "Failed: Transaction parameter txid didn't match.") + XCTAssertEqual(transaction!.params!.currency, "BTC", "Failed: Wrong transaction parameter currency.") + XCTAssertEqual(transaction!.params!.margin, "0.00", "Failed: Wrong transaction parameter margin.") + XCTAssertEqual(transaction!.params!.pair, "BTCBTC", "Failed: Wrong transaction parameter pair.") + XCTAssertEqual(transaction!.params!.rate, "1.00", "Failed: Wrong transaction parameter rate.") + XCTAssertEqual(transaction!.params!.refunds, "fizbiz", "Failed: Wrong transaction parameter refunds.") + XCTAssertEqual(transaction!.params!.ttl, 30000, "Failed: Wrong transaction parameter ttl.") + XCTAssertEqual(transaction!.params!.type, "invite", "Failed: Wrong transaction parameter type.") XCTAssertEqual(transaction!.refundedById!, "foobiz", "Failed: Transaction refundedById didn't match.") XCTAssertEqual(transaction!.status!, "pending", "Failed: Transaction status didn't match.") XCTAssertEqual(transaction!.type!, "transfer", "Failed: Transaction type didn't match.") diff --git a/Tests/Integration/Model/UserTest.swift b/Tests/Integration/Model/UserTest.swift index caff88f..1473b40 100644 --- a/Tests/Integration/Model/UserTest.swift +++ b/Tests/Integration/Model/UserTest.swift @@ -326,7 +326,7 @@ class UserTest: UpholdTestCase { XCTAssertEqual(contact[0].addresses!.count, 1, "Failed: Wrong number of addresses.") XCTAssertEqual(contact[0].addresses![0], "FooBar FooBiz", "Failed: Wrong contact object.") XCTAssertEqual(contact[0].company, "biz", "Failed: Wrong contact object.") - XCTAssertEqual(contact[0].emails?.count, 1, "Failed: Wrong contact object.") + XCTAssertEqual(contact[0].emails!.count, 1, "Failed: Wrong contact object.") XCTAssertEqual(contact[0].emails![0], "foo@bar.org", "Failed: Wrong contact object.") XCTAssertEqual(contact[0].firstName, "Foo", "Failed: Wrong contact object.") XCTAssertEqual(contact[0].id, "foobar", "Failed: Wrong contact object.") @@ -335,7 +335,7 @@ class UserTest: UpholdTestCase { XCTAssertEqual(contact[1].addresses!.count, 1, "Failed: Wrong number of addresses.") XCTAssertEqual(contact[1].addresses![0], "FizBiz FooBiz", "Failed: Wrong contact object.") XCTAssertEqual(contact[1].company, "fiz", "Failed: Wrong contact object.") - XCTAssertEqual(contact[1].emails?.count, 1, "Failed: Wrong contact object.") + XCTAssertEqual(contact[1].emails!.count, 1, "Failed: Wrong contact object.") XCTAssertEqual(contact[1].emails![0], "fuzbuz@buz.org", "Failed: Wrong contact object.") XCTAssertEqual(contact[1].firstName, "Fuz", "Failed: Wrong contact object.") XCTAssertEqual(contact[1].id, "fizbar", "Failed: Wrong contact object.") diff --git a/Tests/Integration/Service/UserCardServiceTest.swift b/Tests/Integration/Service/UserCardServiceTest.swift index 4805a1a..dac4310 100644 --- a/Tests/Integration/Service/UserCardServiceTest.swift +++ b/Tests/Integration/Service/UserCardServiceTest.swift @@ -13,7 +13,7 @@ class UserCardServiceTest: XCTestCase { } func testConfirmTransactionShouldReturnTheRequest() { - let request = UserCardService.confirmTransaction("bar", transactionId: "foo", message: "foobar") + let request = UserCardService.confirmTransaction("bar", transactionId: "foo", transactionCommitRequest: "foobar") XCTAssertEqual(request.url, "https://api.uphold.com/v0/me/cards/bar/transactions/foo/commit", "Failed: Wrong URL.") XCTAssertEqual(request.method, "POST", "Failed: Wrong method.") @@ -21,9 +21,21 @@ class UserCardServiceTest: XCTestCase { } func testCreateTransactionShouldReturnTheRequest() { - let request = UserCardService.createTransaction("bar", transactionRequest: "foobar") + let request = UserCardService.createTransaction("bar", commit: false, transactionRequest: "foobar") XCTAssertEqual(request.url, "https://api.uphold.com/v0/me/cards/bar/transactions", "Failed: Wrong URL.") + XCTAssertEqual(request.query.count, 1, "Failed: Wrong number of query parameters.") + XCTAssertEqual(request.query[0], "commit=false", "Failed: Wrong URL.") + XCTAssertEqual(request.method, "POST", "Failed: Wrong method.") + XCTAssertEqual(request.data! as? String, "foobar", "Failed: Wrong body.") + } + + func testCreateTransactionWithCommitShouldReturnTheRequest() { + let request = UserCardService.createTransaction("bar", commit: true, transactionRequest: "foobar") + + XCTAssertEqual(request.url, "https://api.uphold.com/v0/me/cards/bar/transactions", "Failed: Wrong URL.") + XCTAssertEqual(request.query.count, 1, "Failed: Wrong number of query parameters.") + XCTAssertEqual(request.query[0], "commit=true", "Failed: Wrong URL.") XCTAssertEqual(request.method, "POST", "Failed: Wrong method.") XCTAssertEqual(request.data! as? String, "foobar", "Failed: Wrong body.") } diff --git a/Tests/Util/JSONTestUtils.swift b/Tests/Util/JSONTestUtils.swift index a2f10d5..d59413d 100644 --- a/Tests/Util/JSONTestUtils.swift +++ b/Tests/Util/JSONTestUtils.swift @@ -9,7 +9,7 @@ public class JSONTestUtils { - parameter json: The JSON string to convert. - returns: The dictionary. - */ + */ public static func JSONtoDictionary(json: String) -> [String: AnyObject]? { if let data = json.dataUsingEncoding(NSUTF8StringEncoding) { do { diff --git a/UpholdSdk.xcodeproj/project.pbxproj b/UpholdSdk.xcodeproj/project.pbxproj index bad2e44..54615fe 100644 --- a/UpholdSdk.xcodeproj/project.pbxproj +++ b/UpholdSdk.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ BC02EB191C10503C00FCA0D9 /* TransactionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC02EB181C10503C00FCA0D9 /* TransactionRequest.swift */; }; BC02EB1B1C1051B500FCA0D9 /* TransactionDenominationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC02EB1A1C1051B500FCA0D9 /* TransactionDenominationRequest.swift */; }; + BC12F44F1C10691E00EB2E2B /* TransactionCommitRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC12F44E1C10691E00EB2E2B /* TransactionCommitRequest.swift */; }; BC1CFA461BDFCF1D00979038 /* LogicError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1CFA451BDFCF1D00979038 /* LogicError.swift */; }; BC2381621BB4626F0060CC80 /* UpholdClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2381481BB4626F0060CC80 /* UpholdClient.swift */; }; BC2381651BB4626F0060CC80 /* CardSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC23814D1BB4626F0060CC80 /* CardSettings.swift */; }; @@ -99,6 +100,7 @@ /* Begin PBXFileReference section */ BC02EB181C10503C00FCA0D9 /* TransactionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionRequest.swift; sourceTree = ""; }; BC02EB1A1C1051B500FCA0D9 /* TransactionDenominationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDenominationRequest.swift; sourceTree = ""; }; + BC12F44E1C10691E00EB2E2B /* TransactionCommitRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionCommitRequest.swift; sourceTree = ""; }; BC1CFA451BDFCF1D00979038 /* LogicError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogicError.swift; sourceTree = ""; }; BC23812C1BB462170060CC80 /* UpholdSdk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UpholdSdk.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BC2381361BB462180060CC80 /* UpholdSdkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UpholdSdkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -289,6 +291,7 @@ BC81F7011C0753BA0025A1AF /* NormalizedTransaction.swift */, BC02EB1A1C1051B500FCA0D9 /* TransactionDenominationRequest.swift */, BC02EB181C10503C00FCA0D9 /* TransactionRequest.swift */, + BC12F44E1C10691E00EB2E2B /* TransactionCommitRequest.swift */, ); path = Transaction; sourceTree = ""; @@ -680,6 +683,7 @@ BC4110531C05E68E0063761C /* Phone.swift in Sources */, BC2381711BB4626F0060CC80 /* UserSettings.swift in Sources */, BC2381621BB4626F0060CC80 /* UpholdClient.swift in Sources */, + BC12F44F1C10691E00EB2E2B /* TransactionCommitRequest.swift in Sources */, BC2381731BB4626F0060CC80 /* ReserveService.swift in Sources */, BC8386CF1BF2458B00D67F98 /* SessionManager.swift in Sources */, BC02EB1B1C1051B500FCA0D9 /* TransactionDenominationRequest.swift in Sources */,