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

T196 Add transaction consumption events on transaction request listener #27

Merged
merged 3 commits into from Apr 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions 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'
Expand All @@ -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
2 changes: 1 addition & 1 deletion OmiseGOTests/Helpers/DummySocketEventDelegate.swift
Expand Up @@ -11,7 +11,7 @@ import XCTest

class DummySocketEventDelegate {

let eventExpectation: XCTestExpectation?
var eventExpectation: XCTestExpectation?
let joinExpectation: XCTestExpectation?

init(eventExpectation: XCTestExpectation? = nil, joinExpectation: XCTestExpectation? = nil) {
Expand Down
141 changes: 93 additions & 48 deletions OmiseGOTests/LiveTests/TransactionRequestLiveTests.swift
Expand Up @@ -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() {
Expand All @@ -92,15 +131,15 @@ 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,
mintedTokenId: self.validMintedTokenId,
amount: 1,
address: nil,
correlationId: creationCorrelationId,
requireConfirmation: true,
requireConfirmation: requiresConfirmation,
maxConsumptions: 2,
consumptionLifetime: nil,
expirationDate: Date().addingTimeInterval(60),
Expand Down Expand Up @@ -168,63 +207,69 @@ 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)
wait(for: [consumeExpectation], timeout: 15.0)
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)
return delegate
}

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)
return delegate.didReceiveTransactionConsumptionApproved!
}

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)
Expand Down
2 changes: 1 addition & 1 deletion Podfile
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions 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
22 changes: 19 additions & 3 deletions README.md
Expand Up @@ -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

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion Source/Info.plist
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.10</string>
<string>0.9.11</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
2 changes: 2 additions & 0 deletions Source/Websocket/SocketDelegate.swift
Expand Up @@ -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 {
Expand Down