/
TransactionRequestLiveTests.swift
314 lines (290 loc) · 17.3 KB
/
TransactionRequestLiveTests.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//
// TransactionRequestLiveTests.swift
// OmiseGOTests
//
// Created by Mederic Petit on 23/2/18.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//
import XCTest
import OmiseGO
class TransactionRequestLiveTests: LiveTestCase {
override func setUp() {
super.setUp()
}
/// This test aims to test the full flow of a transfer between users including websocket integration
/// The problem here is that (for obvious reasons) the eWallet doesn't allow a transfer between 2 same addresses,
/// this means that we would require to setup the live tests using 2 different authentication tokens for the purpose of this test.
/// Even if this is doable, we would face an other issue because the user would possibly not have enough balance to make the transfer
/// so the test would fail.
/// So it has been decided, for now, that this test will only scope the following:
// 1) Generate the transaction request
// 2) Test the get transaction request method
// 3) Subscribe to events on this transaction request (listen for consumptions request)
// 4) Try to consume the transaction request
// 5) Wait for the consumption request via the websockets
// 6) Confirm the consumption request
// 7) Assert a same_address error as we did all actions with the same balance
func testGenerateTransactionRequestThenConsumeWithARequiredConfirmation() {
let creationCorrelationId = UUID().uuidString
// FLOW:
// 1) Generate 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 }
// 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,
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 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)
// 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() {
let tc = StubGenerator.transactionConsumption(socketTopic: "transaction_consumption:123")
let joinExpectation = self.expectation(description: "Fail to join the channel")
let delegate = DummySocketEventDelegate(joinExpectation: joinExpectation)
tc.startListeningEvents(withClient: self.testSocketClient, eventDelegate: delegate)
self.wait(for: [joinExpectation], timeout: 15)
guard let error = delegate.didReceiveError else {
XCTFail("Should get an error")
return
}
switch error {
case .api(apiError: let apiError): XCTAssertEqual(apiError.code, .channelNotFound)
default: XCTFail("Should get an API error")
}
}
}
extension TransactionRequestLiveTests {
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: requiresConfirmation,
maxConsumptions: 2,
consumptionLifetime: nil,
expirationDate: Date().addingTimeInterval(60),
allowAmountOverride: true,
metadata: ["a_key": "a_value"])!
var transactionRequestResult: TransactionRequest?
let generateRequest = TransactionRequest.generateTransactionRequest(
using: self.testClient,
params: transactionRequestParams) { (result) in
defer { generateExpectation.fulfill() }
switch result {
case .success(data: let transactionRequest):
transactionRequestResult = transactionRequest
XCTAssertEqual(transactionRequest.mintedToken.id, self.validMintedTokenId)
XCTAssertEqual(transactionRequest.amount, 1)
XCTAssertEqual(transactionRequest.correlationId, creationCorrelationId)
case .fail(error: let error):
XCTFail("\(error)")
}
}
XCTAssertNotNil(generateRequest)
wait(for: [generateExpectation], timeout: 15.0)
return transactionRequestResult
}
func getTransactionRequest(transactionRequestId: String, creationCorrelationId: String) {
var transactionRequestResult: TransactionRequest?
let getExpectation = self.expectation(description: "Get transaction request")
let getRequest = TransactionRequest.retrieveTransactionRequest(
using: self.testClient,
id: transactionRequestId) { (result) in
defer { getExpectation.fulfill() }
switch result {
case .success(data: let transactionRequest):
transactionRequestResult = transactionRequest
XCTAssertEqual(transactionRequest.id, transactionRequestId)
XCTAssertEqual(transactionRequest.mintedToken.id, self.validMintedTokenId)
XCTAssertEqual(transactionRequest.amount, 1)
XCTAssertEqual(transactionRequest.correlationId, creationCorrelationId)
case .fail(error: let error):
XCTFail("\(error)")
}
}
XCTAssertNotNil(getRequest)
wait(for: [getExpectation], timeout: 15.0)
}
func consumeTransactionRequest(transactionRequest: TransactionRequest) -> TransactionConsumption? {
let idempotencyToken = UUID().uuidString
let consumeCorrelationId = UUID().uuidString
let consumeExpectation = self.expectation(description: "Consume transaction request")
let transactionConsumptionParams = TransactionConsumptionParams(
transactionRequest: transactionRequest,
address: nil,
mintedTokenId: nil,
amount: nil,
idempotencyToken: idempotencyToken,
correlationId: consumeCorrelationId,
expirationDate: nil,
metadata: ["a_key": "a_value"])
var transactionConsumptionResult: TransactionConsumption?
let consumeRequest = TransactionConsumption.consumeTransactionRequest(
using: self.testClient,
params: transactionConsumptionParams!) { (result) in
defer { consumeExpectation.fulfill() }
switch result {
case .success(data: let transactionConsumption):
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):
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 startListeningForEvent(forTransactionRequest transactionRequest: TransactionRequest) -> DummySocketEventDelegate {
let joinExpectation = self.expectation(description: "Join channel")
let delegate = DummySocketEventDelegate(joinExpectation: joinExpectation)
transactionRequest.startListeningEvents(withClient: self.testSocketClient, eventDelegate: delegate)
wait(for: [joinExpectation], timeout: 15.0)
XCTAssertTrue(delegate.didJoin)
return delegate
}
func startListeningForEvent(forConsumption transactionConsumption: TransactionConsumption) -> DummySocketEventDelegate {
let joinExpectation = self.expectation(description: "Join channel")
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 {
wait(for: [delegate.eventExpectation!], timeout: 15.0)
XCTAssertNotNil(delegate.didReceiveTransactionConsumptionRequest)
XCTAssertEqual(delegate.didReceiveEvent!, SocketEvent.transactionConsumptionRequest)
return delegate.didReceiveTransactionConsumptionRequest!
}
func waitForApproval(withDelegate delegate: DummySocketEventDelegate) -> TransactionConsumption {
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 {
wait(for: [delegate.eventExpectation!], timeout: 15.0)
XCTAssertNotNil(delegate.didReceiveTransactionConsumptionRejected)
XCTAssertNotNil(delegate.didReceiveEvent)
XCTAssertEqual(delegate.didReceiveEvent!, SocketEvent.transactionConsumptionRejected)
return delegate.didReceiveTransactionConsumptionRejected!
}
func confirmConsumption(withConsumption transactionConsumption: TransactionConsumption) {
let confirmExpectation = self.expectation(description: "Confirm consumption request")
let confirmationRequest = transactionConsumption.approve(using: self.testClient) { (result) in
defer { confirmExpectation.fulfill() }
switch result {
case .success:
XCTFail("Shouldn't succeed as we're trying to transfer between the same address")
case .fail(error: let error):
switch error {
case .api(apiError: let apiError): XCTAssertEqual(apiError.code, .sameAddress)
default: XCTFail("Expected to receive same_address error")
}
}
}
XCTAssertNotNil(confirmationRequest)
wait(for: [confirmExpectation], timeout: 15.0)
}
func rejectConsumption(withConsumption transactionConsumption: TransactionConsumption) {
let rejectExpectation = self.expectation(description: "Confirm consumption request")
let rejectRequest = transactionConsumption.reject(using: self.testClient) { (result) in
defer { rejectExpectation.fulfill() }
switch result {
case .success(data: let transactionConsumption):
XCTAssertEqual(transactionConsumption.status, .rejected)
XCTAssertFalse(transactionConsumption.approved)
case .fail:
XCTFail("Shouldn't receive error")
}
}
XCTAssertNotNil(rejectRequest)
wait(for: [rejectExpectation], timeout: 15.0)
}
}