From 872aeda9b8605597209f30d361e03c33af4074e7 Mon Sep 17 00:00:00 2001
From: Mederic <32560642+mederic-p@users.noreply.github.com>
Date: Mon, 9 Apr 2018 11:09:57 +0700
Subject: [PATCH] T196 Handle more events on transaction request listener (#27)
* Add transaction request approved and rejected events
* Update version number
* Update README
---
OmiseGO.podspec | 4 +-
.../Helpers/DummySocketEventDelegate.swift | 2 +-
.../TransactionRequestLiveTests.swift | 141 ++++++++++++------
Podfile | 2 +-
Podfile.lock | 8 +-
README.md | 22 ++-
Source/Info.plist | 2 +-
Source/Websocket/SocketDelegate.swift | 2 +
Source/Websocket/SocketDispatcher.swift | 12 +-
9 files changed, 133 insertions(+), 62 deletions(-)
diff --git a/OmiseGO.podspec b/OmiseGO.podspec
index 27131af..7a08f7a 100644
--- a/OmiseGO.podspec
+++ b/OmiseGO.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'OmiseGO'
- s.version = '0.9.10'
+ s.version = '0.9.11'
s.license = 'Apache'
s.summary = 'The OmiseGO iOS SDK allows developers to easily interact with a node of the OmiseGO eWallet.'
s.homepage = 'https://github.com/omisego/ios-sdk'
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.platform = :ios, '10.0'
s.swift_version = '4.0'
- s.dependency 'Starscream', '~> 3.0.2'
+ s.dependency 'Starscream', '~> 3.0'
s.source_files = 'Source/**/*.swift'
end
diff --git a/OmiseGOTests/Helpers/DummySocketEventDelegate.swift b/OmiseGOTests/Helpers/DummySocketEventDelegate.swift
index dd58b46..2668e86 100644
--- a/OmiseGOTests/Helpers/DummySocketEventDelegate.swift
+++ b/OmiseGOTests/Helpers/DummySocketEventDelegate.swift
@@ -11,7 +11,7 @@ import XCTest
class DummySocketEventDelegate {
- let eventExpectation: XCTestExpectation?
+ var eventExpectation: XCTestExpectation?
let joinExpectation: XCTestExpectation?
init(eventExpectation: XCTestExpectation? = nil, joinExpectation: XCTestExpectation? = nil) {
diff --git a/OmiseGOTests/LiveTests/TransactionRequestLiveTests.swift b/OmiseGOTests/LiveTests/TransactionRequestLiveTests.swift
index e5f8709..7f8db0a 100644
--- a/OmiseGOTests/LiveTests/TransactionRequestLiveTests.swift
+++ b/OmiseGOTests/LiveTests/TransactionRequestLiveTests.swift
@@ -29,47 +29,86 @@ class TransactionRequestLiveTests: LiveTestCase {
// 6) Confirm the consumption request
// 7) Assert a same_address error as we did all actions with the same balance
- func testGenerateTransactionRequestThenConsume() {
+ func testGenerateTransactionRequestThenConsumeWithARequiredConfirmation() {
let creationCorrelationId = UUID().uuidString
// FLOW:
// 1) Generate the transaction request
- guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId) else { return }
- // 2) Test the get method
- self.getTransactionRequest(transactionRequestId: transactionRequest.id, creationCorrelationId: creationCorrelationId)
- // 3) Subscribe to events on this transaction request (listen for consumptions)
- let consumptionDelegate = self.startToListenForConsumption(forTransactionRequest: transactionRequest)
- // 4) Try to consume the transaction request
+ guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId,
+ requiresConfirmation: true) else { return }
+ // 2) Subscribe to events on this transaction request (listen for consumptions)
+ let transactionRequestEventDelegate = self.startListeningForEvent(forTransactionRequest: transactionRequest)
+ // 3) Try to consume the transaction request
// As it requires confirmation, an event should be send to listeners
+ transactionRequestEventDelegate.eventExpectation = self.expectation(description: "Receives a consumption request")
guard self.consumeTransactionRequest(transactionRequest: transactionRequest) != nil else { return }
- // 5) Wait for the consumption to be sent
- let consumptionRequest = self.waitForConsumption(withDelegate: consumptionDelegate)
- // 6) Confirm the consumption
+ // 4) Wait for the consumption to be sent
+ let consumptionRequest = self.waitForConsumption(withDelegate: transactionRequestEventDelegate)
+ // 5) Confirm the consumption
+ transactionRequestEventDelegate.eventExpectation = self.expectation(description: "Receives an approved, but failed consumption")
self.confirmConsumption(withConsumption: consumptionRequest)
+ // 6) Wait for the failed consumption to be sent
+ let failedConsumption = self.waitForApproval(withDelegate: transactionRequestEventDelegate)
+ XCTAssertEqual(failedConsumption.status, .failed)
+ XCTAssertTrue(failedConsumption.approved)
+ XCTAssertNotNil(failedConsumption.finalizedAt)
+ }
+
+ func testGetATransactionRequest() {
+ let creationCorrelationId = UUID().uuidString
+ guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId, requiresConfirmation: true) else { return }
+ self.getTransactionRequest(transactionRequestId: transactionRequest.id, creationCorrelationId: creationCorrelationId)
+ }
+
+ /// This test asserts that an approved consumption event is immediately sent without the need to approve the consumption
+ func testGenerateTransactionRequestThenConsumeWithAnAutomaticConfirmation() {
+ let creationCorrelationId = UUID().uuidString
+ // FLOW:
+ // 1) Generate the transaction request
+ guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId,
+ requiresConfirmation: false) else { return }
+ // 2) Subscribe to events on this transaction request
+ let transactionRequestEventDelegate = self.startListeningForEvent(forTransactionRequest: transactionRequest)
+ // 3) Try to consume the transaction request
+ // As it requires confirmation, an event should be send to listeners
+ transactionRequestEventDelegate.eventExpectation = self.expectation(description: "Receives an approved, but failed consumption")
+ let consumption = self.consumeTransactionRequest(transactionRequest: transactionRequest)
+ XCTAssertNil(consumption)
+ // 4) Wait for the failed consumption to be sent
+ let failedConsumption = self.waitForApproval(withDelegate: transactionRequestEventDelegate)
+ XCTAssertEqual(failedConsumption.status, .failed)
+ XCTAssertTrue(failedConsumption.approved)
+ XCTAssertNotNil(failedConsumption.finalizedAt)
}
+ /// This test check that the rejection flow works as expected
func testGenerateTransactionRequestThenRejectConsumption() {
let creationCorrelationId = UUID().uuidString
// FLOW:
// 1) Generate the transaction request
- guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId) else { return }
- // 2) Test the get method
- self.getTransactionRequest(transactionRequestId: transactionRequest.id, creationCorrelationId: creationCorrelationId)
- // 3) Subscribe to events on this transaction request (listen for consumptions)
- let consumptionDelegate = self.startToListenForConsumption(forTransactionRequest: transactionRequest)
- // 4) Try to consume the transaction request
+ guard let transactionRequest = self.generateTransactionRequest(creationCorrelationId: creationCorrelationId,
+ requiresConfirmation: true) else { return }
+ // 2) Subscribe to events on this transaction request (listen for consumptions)
+ let transactionRequestEventDelegate = self.startListeningForEvent(forTransactionRequest: transactionRequest)
+ // 3) Try to consume the transaction request
// As it requires confirmation, an event should be send to listeners
+ transactionRequestEventDelegate.eventExpectation = self.expectation(description: "Receives a consumption request")
guard let transactionConsumption = self.consumeTransactionRequest(transactionRequest: transactionRequest) else { return }
+ // 4) Wait for the consumption to be sent
+ let consumptionRequest = self.waitForConsumption(withDelegate: transactionRequestEventDelegate)
// 5) Subscribe to events on this transaction consumption (listen for confirmations)
- let confirmationDelegate = self.startToListenForConfirmation(forConsumption: transactionConsumption)
- // 6) Wait for the consumption to be sent
- let consumptionRequest = self.waitForConsumption(withDelegate: consumptionDelegate)
- // 7) Reject the consumption
+ let transactionConsumptionEventDelegate = self.startListeningForEvent(forConsumption: transactionConsumption)
+ // 6) Reject the consumption
+ transactionRequestEventDelegate.eventExpectation = self.expectation(description: "Receives a rejected consumption")
+ transactionConsumptionEventDelegate.eventExpectation = self.expectation(description: "Receives a rejected consumption")
self.rejectConsumption(withConsumption: consumptionRequest)
- // 8) Wait for the rejection to be sent
- let rejectedConsumption = self.waitForRejection(withDelegate: confirmationDelegate)
- XCTAssertEqual(rejectedConsumption.status, .rejected)
- XCTAssertFalse(rejectedConsumption.approved)
- XCTAssertNotNil(rejectedConsumption.finalizedAt)
+ // 7) Wait for the rejection to be sent
+ let rejectedConsumptionSentOnTransactionConsumptionListener = self.waitForRejection(withDelegate: transactionConsumptionEventDelegate)
+ let rejectedConsumptionSentOnTransactionRequestListener = self.waitForRejection(withDelegate: transactionRequestEventDelegate)
+
+ XCTAssertEqual(rejectedConsumptionSentOnTransactionConsumptionListener, rejectedConsumptionSentOnTransactionRequestListener)
+ XCTAssertEqual(rejectedConsumptionSentOnTransactionConsumptionListener.status, .rejected)
+ XCTAssertFalse(rejectedConsumptionSentOnTransactionConsumptionListener.approved)
+ XCTAssertNotNil(rejectedConsumptionSentOnTransactionConsumptionListener.finalizedAt)
}
func testChannelNotFound() {
@@ -92,7 +131,7 @@ class TransactionRequestLiveTests: LiveTestCase {
extension TransactionRequestLiveTests {
- func generateTransactionRequest(creationCorrelationId: String) -> TransactionRequest? {
+ func generateTransactionRequest(creationCorrelationId: String, requiresConfirmation: Bool) -> TransactionRequest? {
let generateExpectation = self.expectation(description: "Generate transaction request")
let transactionRequestParams = TransactionRequestCreateParams(
type: .receive,
@@ -100,7 +139,7 @@ extension TransactionRequestLiveTests {
amount: 1,
address: nil,
correlationId: creationCorrelationId,
- requireConfirmation: true,
+ requireConfirmation: requiresConfirmation,
maxConsumptions: 2,
consumptionLifetime: nil,
expirationDate: Date().addingTimeInterval(60),
@@ -168,16 +207,27 @@ extension TransactionRequestLiveTests {
defer { consumeExpectation.fulfill() }
switch result {
case .success(data: let transactionConsumption):
- transactionConsumptionResult = transactionConsumption
- let mintedToken = transactionConsumption.mintedToken
- XCTAssertEqual(mintedToken.id, self.validMintedTokenId)
- XCTAssertEqual(transactionConsumption.amount, 1)
- XCTAssertEqual(transactionConsumption.correlationId, consumeCorrelationId)
- XCTAssertEqual(transactionConsumption.idempotencyToken, idempotencyToken)
- XCTAssertEqual(transactionConsumption.transactionRequestId, transactionRequest.id)
- XCTAssertEqual(transactionConsumption.status, .pending)
+ if transactionRequest.requireConfirmation {
+ transactionConsumptionResult = transactionConsumption
+ let mintedToken = transactionConsumption.mintedToken
+ XCTAssertEqual(mintedToken.id, self.validMintedTokenId)
+ XCTAssertEqual(transactionConsumption.amount, 1)
+ XCTAssertEqual(transactionConsumption.correlationId, consumeCorrelationId)
+ XCTAssertEqual(transactionConsumption.idempotencyToken, idempotencyToken)
+ XCTAssertEqual(transactionConsumption.transactionRequestId, transactionRequest.id)
+ XCTAssertEqual(transactionConsumption.status, .pending)
+ } else {
+ XCTFail("Should raise a same address error")
+ }
case .fail(error: let error):
- XCTFail("\(error)")
+ if transactionRequest.requireConfirmation {
+ XCTFail("\(error)")
+ } else {
+ switch error {
+ case .api(apiError: let apiError): XCTAssertEqual(apiError.code, .sameAddress)
+ default: XCTFail("Expected to receive same_address error")
+ }
+ }
}
}
XCTAssertNotNil(consumeRequest)
@@ -185,20 +235,18 @@ extension TransactionRequestLiveTests {
return transactionConsumptionResult
}
- func startToListenForConsumption(forTransactionRequest transactionRequest: TransactionRequest) -> DummySocketEventDelegate {
- let eventExpectation = self.expectation(description: "Start listening for consumption")
+ func startListeningForEvent(forTransactionRequest transactionRequest: TransactionRequest) -> DummySocketEventDelegate {
let joinExpectation = self.expectation(description: "Join channel")
- let delegate = DummySocketEventDelegate(eventExpectation: eventExpectation, joinExpectation: joinExpectation)
+ let delegate = DummySocketEventDelegate(joinExpectation: joinExpectation)
transactionRequest.startListeningEvents(withClient: self.testSocketClient, eventDelegate: delegate)
wait(for: [joinExpectation], timeout: 15.0)
XCTAssertTrue(delegate.didJoin)
return delegate
}
- func startToListenForConfirmation(forConsumption transactionConsumption: TransactionConsumption) -> DummySocketEventDelegate {
- let eventExpectation = self.expectation(description: "Start listening for confirmation")
+ func startListeningForEvent(forConsumption transactionConsumption: TransactionConsumption) -> DummySocketEventDelegate {
let joinExpectation = self.expectation(description: "Join channel")
- let delegate = DummySocketEventDelegate(eventExpectation: eventExpectation, joinExpectation: joinExpectation)
+ let delegate = DummySocketEventDelegate(joinExpectation: joinExpectation)
transactionConsumption.startListeningEvents(withClient: self.testSocketClient, eventDelegate: delegate)
wait(for: [joinExpectation], timeout: 15.0)
XCTAssertTrue(delegate.didJoin)
@@ -206,16 +254,14 @@ extension TransactionRequestLiveTests {
}
func waitForConsumption(withDelegate delegate: DummySocketEventDelegate) -> TransactionConsumption {
- let expectation = delegate.eventExpectation!
- wait(for: [expectation], timeout: 15.0)
+ wait(for: [delegate.eventExpectation!], timeout: 15.0)
XCTAssertNotNil(delegate.didReceiveTransactionConsumptionRequest)
XCTAssertEqual(delegate.didReceiveEvent!, SocketEvent.transactionConsumptionRequest)
return delegate.didReceiveTransactionConsumptionRequest!
}
func waitForApproval(withDelegate delegate: DummySocketEventDelegate) -> TransactionConsumption {
- let expectation = delegate.eventExpectation!
- wait(for: [expectation], timeout: 15.0)
+ wait(for: [delegate.eventExpectation!], timeout: 15.0)
XCTAssertNotNil(delegate.didReceiveTransactionConsumptionApproved)
XCTAssertNotNil(delegate.didReceiveEvent)
XCTAssertEqual(delegate.didReceiveEvent!, SocketEvent.transactionConsumptionApproved)
@@ -223,8 +269,7 @@ extension TransactionRequestLiveTests {
}
func waitForRejection(withDelegate delegate: DummySocketEventDelegate) -> TransactionConsumption {
- let expectation = delegate.eventExpectation!
- wait(for: [expectation], timeout: 15.0)
+ wait(for: [delegate.eventExpectation!], timeout: 15.0)
XCTAssertNotNil(delegate.didReceiveTransactionConsumptionRejected)
XCTAssertNotNil(delegate.didReceiveEvent)
XCTAssertEqual(delegate.didReceiveEvent!, SocketEvent.transactionConsumptionRejected)
diff --git a/Podfile b/Podfile
index 458360f..a019db8 100644
--- a/Podfile
+++ b/Podfile
@@ -5,7 +5,7 @@ def shared_pods
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# websocket library
- pod 'Starscream', '~> 3.0.2'
+ pod 'Starscream', '~> 3.0'
end
target 'OmiseGO' do
diff --git a/Podfile.lock b/Podfile.lock
index 0a7458e..894804a 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,12 +1,12 @@
PODS:
- - Starscream (3.0.4)
+ - Starscream (3.0.5)
DEPENDENCIES:
- - Starscream (~> 3.0.2)
+ - Starscream (~> 3.0)
SPEC CHECKSUMS:
- Starscream: f5da93fe6984c77b694366bf7299b7dc63a76f26
+ Starscream: faf918b2f2eff7d5dd21180646bf015a669673bd
-PODFILE CHECKSUM: 60bd2f337ffb2f565edc37401613b7b316f33a95
+PODFILE CHECKSUM: b65b55b7361189d8c9b6d31f609db7eba1c61ae8
COCOAPODS: 1.4.0
diff --git a/README.md b/README.md
index 80dae50..66f641a 100644
--- a/README.md
+++ b/README.md
@@ -467,11 +467,22 @@ Where:
- `client` is a `SocketClient`
- `eventDelegate` is a `TransactionRequestEventDelegate` that will receive incoming events.
-An object conforming to `TransactionRequestEventDelegate` needs to implement the 3 common methods mentioned above and also `didReceiveTransactionConsumptionRequest(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+An object conforming to `TransactionRequestEventDelegate` needs to implement the 3 common methods mentioned above and also:
+
+- `didReceiveTransactionConsumptionRequest(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
This method will be called when a `TransactionConsumption` is trying to consume the `TransactionRequest`.
This allows the requester to [confirm](#confirm-a-transaction-consumption) or not the consumption if legitimate.
+- `didReceiveTransactionConsumptionApproval(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+
+If the `TransactionRequest` requires a confirmation then this method will be called when a `TransactionConsumption` is approved by the requester.
+If it doesn't require a confirmation it will be called when the request is consumed.
+
+- `didReceiveTransactionConsumptionRejection(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+
+This method will be called when a `TransactionConsumption` is rejected by the requester.
+
#### Transaction consumption events
@@ -483,10 +494,15 @@ Where:
- `client` is a `SocketClient`
- `eventDelegate` is a `TransactionConsumptionEventDelegate` that will receive incoming events.
-An object conforming to `TransactionConsumptionEventDelegate` needs to implement the 3 common methods mentioned above and also `didReceiveTransactionConsumptionConfirmation(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+An object conforming to `TransactionConsumptionEventDelegate` needs to implement the 3 common methods mentioned above and also:
+
+- `didReceiveTransactionConsumptionApproval(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+
+This method will be called when a `TransactionConsumption` is approved by the requester.
-This method will be called when a `TransactionConsumption` is confirmed by the requester.
+- `didReceiveTransactionConsumptionRejection(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)`.
+This method will be called when a `TransactionConsumption` is rejected by the requester.
#### User events
diff --git a/Source/Info.plist b/Source/Info.plist
index c0d5e7f..7522f76 100644
--- a/Source/Info.plist
+++ b/Source/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 0.9.10
+ 0.9.11
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/Source/Websocket/SocketDelegate.swift b/Source/Websocket/SocketDelegate.swift
index 35f0e2e..f831e23 100644
--- a/Source/Websocket/SocketDelegate.swift
+++ b/Source/Websocket/SocketDelegate.swift
@@ -23,6 +23,8 @@ public protocol UserEventDelegate: EventDelegate {
public protocol TransactionRequestEventDelegate: EventDelegate {
func didReceiveTransactionConsumptionRequest(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)
+ func didReceiveTransactionConsumptionApproval(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)
+ func didReceiveTransactionConsumptionRejection(_ transactionConsumption: TransactionConsumption, forEvent event: SocketEvent)
}
public protocol TransactionConsumptionEventDelegate: EventDelegate {
diff --git a/Source/Websocket/SocketDispatcher.swift b/Source/Websocket/SocketDispatcher.swift
index 7afefe4..f70f303 100644
--- a/Source/Websocket/SocketDispatcher.swift
+++ b/Source/Websocket/SocketDispatcher.swift
@@ -57,8 +57,16 @@ enum SocketDispatcher {
payload: GenericObjectEnum,
event: SocketEvent) {
switch payload {
- case .transactionConsumption(object: let transactionConsumption) where event == .transactionConsumptionRequest:
- handler?.didReceiveTransactionConsumptionRequest(transactionConsumption, forEvent: event)
+ case .transactionConsumption(object: let transactionConsumption):
+ switch event {
+ case .transactionConsumptionRequest:
+ handler?.didReceiveTransactionConsumptionRequest(transactionConsumption, forEvent: event)
+ case .transactionConsumptionApproved:
+ handler?.didReceiveTransactionConsumptionApproval(transactionConsumption, forEvent: event)
+ case .transactionConsumptionRejected:
+ handler?.didReceiveTransactionConsumptionRejection(transactionConsumption, forEvent: event)
+ default: break
+ }
case .error(error: let error):
self.dispatchError(error)
default: break