smobilpay.TestClient_CollectSyncNoRetry   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 38
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 26
nop 1
dl 0
loc 38
rs 9.256
c 0
b 0
f 0
1
package smobilpay
2
3
import (
4
	"context"
5
	"crypto/hmac"
6
	"crypto/sha1"
7
	"encoding/base64"
8
	"net/http"
9
	"net/url"
10
	"testing"
11
	"time"
12
13
	"github.com/NdoleStudio/smobilpay-go/internal/helpers"
14
	"github.com/NdoleStudio/smobilpay-go/internal/stubs"
15
	"github.com/stretchr/testify/assert"
16
)
17
18
func TestClient_PingOk(t *testing.T) {
19
	// Setup
20
	t.Parallel()
21
22
	// Arrange
23
	server := helpers.MakeTestServer(http.StatusOK, stubs.PingOk())
24
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
25
	client := New(
26
		WithBaseURL(server.URL),
27
		WithAccessToken(accessToken),
28
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
29
	)
30
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
31
32
	// Act
33
	status, response, err := client.Ping(
34
		context.Background(),
35
		WithRequestTimestamp(time.Now()),
36
		WithRequestNonce(nonce),
37
	)
38
39
	// Assert
40
	assert.Nil(t, err)
41
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
42
	assert.Equal(t, nonce, status.Nonce)
43
	assert.Equal(t, accessToken, status.Key)
44
45
	// Teardown
46
	server.Close()
47
}
48
49
func TestClient_PingError(t *testing.T) {
50
	// Setup
51
	t.Parallel()
52
53
	// Arrange
54
	server := helpers.MakeTestServer(http.StatusBadRequest, stubs.PingError())
55
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
56
	client := New(
57
		WithBaseURL(server.URL),
58
		WithAccessToken(accessToken),
59
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
60
	)
61
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
62
63
	// Act
64
	status, response, err := client.Ping(
65
		context.Background(),
66
		WithRequestTimestamp(time.Now()),
67
		WithRequestNonce(nonce),
68
	)
69
70
	// Assert
71
	assert.NotNil(t, err)
72
	assert.Nil(t, status)
73
	assert.Equal(t, http.StatusBadRequest, response.HTTPResponse.StatusCode)
74
75
	// Teardown
76
	server.Close()
77
}
78
79
func TestClient_PingRequest(t *testing.T) {
80
	// Setup
81
	t.Parallel()
82
83
	// Arrange
84
	request := new(http.Request)
85
	server := helpers.MakeRequestCapturingTestServer(http.StatusOK, stubs.PingOk(), request)
86
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
87
	accessSecret := "1B875FB0-4717-11ED-963F-0800200C9A66"
88
	client := New(
89
		WithBaseURL(server.URL),
90
		WithAccessToken(accessToken),
91
		WithAccessSecret(accessSecret),
92
	)
93
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
94
	signature := computeHmac(
95
		"GET&"+url.QueryEscape(server.URL)+"%2Fping&s3pAuth_nonce%3D95cdf110-4614-4d95-b6c2-f14fe01c4995%26s3pAuth_signature_method%3DHMAC-SHA1%26s3pAuth_timestamp%3D1613869830%26s3pAuth_token%3D6B352110-4716-11ED-963F-0800200C9A66",
96
		accessSecret,
97
	)
98
99
	// Act
100
	_, _, err := client.Ping(
101
		context.Background(),
102
		WithRequestTimestamp(time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC)),
103
		WithRequestNonce(nonce),
104
	)
105
106
	// Assert
107
	assert.Nil(t, err)
108
	assert.Equal(
109
		t,
110
		"s3pAuth,s3pAuth_nonce=\"95cdf110-4614-4d95-b6c2-f14fe01c4995\",s3pAuth_signature=\""+signature+"\",s3pAuth_signature_method=\"HMAC-SHA1\",s3pAuth_timestamp=\"1613869830\",s3pAuth_token=\"6B352110-4716-11ED-963F-0800200C9A66\"",
111
		request.Header.Get("Authorization"),
112
	)
113
114
	// Teardown
115
	server.Close()
116
}
117
118
func TestClient_Quote(t *testing.T) {
119
	// Setup
120
	t.Parallel()
121
122
	// Arrange
123
	server := helpers.MakeTestServer(http.StatusOK, stubs.QuoteOk())
124
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
125
	client := New(
126
		WithBaseURL(server.URL),
127
		WithAccessToken(accessToken),
128
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
129
	)
130
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
131
	payItemID := "S-112-951-CMORANGE-20062-CM_ORANGE_VTU_CUSTOM-1"
132
133
	// Act
134
	quote, response, err := client.Quote(
135
		context.Background(),
136
		&QuoteParams{
137
			PayItemID: payItemID,
138
			Amount:    "100",
139
		},
140
		WithRequestNonce(nonce),
141
	)
142
143
	// Assert
144
	assert.Nil(t, err)
145
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
146
	assert.Equal(t, payItemID, quote.PayItemID)
147
	assert.Equal(t, "100", quote.PriceSystemCurrency)
148
149
	// Teardown
150
	server.Close()
151
}
152
153
func TestClient_Collect(t *testing.T) {
154
	// Setup
155
	t.Parallel()
156
157
	// Arrange
158
	server := helpers.MakeTestServer(http.StatusOK, stubs.CollectOk())
159
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
160
	client := New(
161
		WithBaseURL(server.URL),
162
		WithAccessToken(accessToken),
163
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
164
	)
165
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
166
	params := &CollectParams{
167
		QuoteID:               "15380e55-6227-4a25-8e1f-23c8735ce242",
168
		CustomerPhoneNumber:   "697777777",
169
		CustomerEmailAddress:  "[email protected]",
170
		ServiceNumber:         "697777777",
171
		ExternalTransactionID: "999992624813740205",
172
	}
173
174
	// Act
175
	transaction, response, err := client.Collect(
176
		context.Background(),
177
		params,
178
		WithRequestNonce(nonce),
179
	)
180
181
	// Assert
182
	assert.Nil(t, err)
183
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
184
	assert.Equal(t, params.ExternalTransactionID, *transaction.ExternalTransactionID)
185
	assert.False(t, transaction.IsFailed())
186
	assert.Equal(t, "SUCCESS", transaction.Status)
187
188
	// Teardown
189
	server.Close()
190
}
191
192
func TestClient_CollectSync(t *testing.T) {
193
	// Setup
194
	t.Parallel()
195
196
	// Arrange
197
	server := helpers.MakeTestServerWithMultipleResponses(http.StatusOK, [][]byte{
198
		stubs.CollectPending(),
199
		stubs.VerifyInProcess(),
200
		stubs.VerifyOk(),
201
	})
202
	verifyInterval := 100 * time.Millisecond
203
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
204
	client := New(
205
		WithBaseURL(server.URL),
206
		WithCollectSyncVerifyInterval(verifyInterval),
207
		WithAccessToken(accessToken),
208
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
209
	)
210
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
211
	params := &CollectParams{
212
		QuoteID:               "15380e55-6227-4a25-8e1f-23c8735ce242",
213
		CustomerPhoneNumber:   "697777777",
214
		CustomerEmailAddress:  "[email protected]",
215
		ServiceNumber:         "697777777",
216
		ExternalTransactionID: "999992624813740205",
217
	}
218
219
	// Act
220
	start := time.Now()
221
	transaction, response, err := client.CollectSync(
222
		context.Background(),
223
		params,
224
		WithRequestNonce(nonce),
225
	)
226
227
	// Assert
228
	assert.Nil(t, err)
229
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
230
	assert.Equal(t, params.ExternalTransactionID, *transaction.ExternalTransactionID)
231
	assert.False(t, transaction.IsFailed())
232
	assert.Equal(t, "SUCCESS", transaction.Status)
233
	assert.GreaterOrEqual(t, time.Since(start), 2*verifyInterval)
234
235
	// Teardown
236
	server.Close()
237
}
238
239
func TestClient_CollectSyncWithContextTimeout(t *testing.T) {
240
	// Setup
241
	t.Parallel()
242
243
	// Arrange
244
	server := helpers.MakeTestServerWithMultipleResponses(http.StatusOK, [][]byte{
245
		stubs.CollectPending(),
246
		stubs.VerifyInProcess(),
247
		stubs.VerifyOk(),
248
	})
249
250
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
251
	client := New(
252
		WithBaseURL(server.URL),
253
		WithAccessToken(accessToken),
254
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
255
	)
256
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
257
	params := &CollectParams{
258
		QuoteID:               "15380e55-6227-4a25-8e1f-23c8735ce242",
259
		CustomerPhoneNumber:   "697777777",
260
		CustomerEmailAddress:  "[email protected]",
261
		ServiceNumber:         "697777777",
262
		ExternalTransactionID: "999992624813740205",
263
	}
264
	ctxTimeout := 50 * time.Millisecond
265
	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
266
	start := time.Now()
267
	defer cancel()
268
269
	// Act
270
	_, _, err := client.CollectSync(
271
		ctx,
272
		params,
273
		WithRequestNonce(nonce),
274
	)
275
276
	// Assert
277
	assert.NotNil(t, err)
278
	assert.LessOrEqual(t, time.Since(start), 2*ctxTimeout)
279
280
	// Teardown
281
	server.Close()
282
}
283
284
func TestClient_CollectSyncNoRetry(t *testing.T) {
285
	// Setup
286
	t.Parallel()
287
288
	// Arrange
289
	server := helpers.MakeTestServer(http.StatusOK, stubs.CollectPending())
290
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
291
	client := New(
292
		WithBaseURL(server.URL),
293
		WithAccessToken(accessToken),
294
		WithCollectSyncVerifyRetryCount(0),
295
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
296
	)
297
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
298
	params := &CollectParams{
299
		QuoteID:               "15380e55-6227-4a25-8e1f-23c8735ce242",
300
		CustomerPhoneNumber:   "697777777",
301
		CustomerEmailAddress:  "[email protected]",
302
		ServiceNumber:         "697777777",
303
		ExternalTransactionID: "999992624813740205",
304
	}
305
306
	// Act
307
	transaction, response, err := client.CollectSync(
308
		context.Background(),
309
		params,
310
		WithRequestNonce(nonce),
311
	)
312
313
	// Assert
314
	assert.Nil(t, err)
315
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
316
	assert.Equal(t, params.ExternalTransactionID, *transaction.ExternalTransactionID)
317
	assert.True(t, transaction.IsPending())
318
	assert.Equal(t, "PENDING", transaction.Status)
319
320
	// Teardown
321
	server.Close()
322
}
323
324
func TestClient_Verify(t *testing.T) {
325
	// Setup
326
	t.Parallel()
327
328
	// Arrange
329
	server := helpers.MakeTestServer(http.StatusOK, stubs.VerifyOk())
330
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
331
	client := New(
332
		WithBaseURL(server.URL),
333
		WithAccessToken(accessToken),
334
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
335
	)
336
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
337
	paymentTransactionNumber := "99999166542651400095315364801168"
338
339
	// Act
340
	transaction, response, err := client.Verify(
341
		context.Background(),
342
		paymentTransactionNumber,
343
		WithRequestNonce(nonce),
344
	)
345
346
	// Assert
347
	assert.Nil(t, err)
348
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
349
	assert.Equal(t, paymentTransactionNumber, transaction.PaymentTransactionNumber)
350
	assert.False(t, transaction.IsFailed())
351
	assert.Equal(t, "SUCCESS", transaction.Status)
352
353
	// Teardown
354
	server.Close()
355
}
356
357
func TestClient_TransactionHistory(t *testing.T) {
358
	// Setup
359
	t.Parallel()
360
361
	// Arrange
362
	server := helpers.MakeTestServer(http.StatusOK, stubs.VerifyOk())
363
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
364
	client := New(
365
		WithBaseURL(server.URL),
366
		WithAccessToken(accessToken),
367
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
368
	)
369
	paymentTransactionNumber := "99999166542651400095315364801168"
370
371
	// Act
372
	transactions, response, err := client.TransactionHistory(context.Background(), time.Now(), time.Now())
373
374
	// Assert
375
	assert.Nil(t, err)
376
	assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
377
	assert.Equal(t, 1, len(transactions))
378
	assert.Equal(t, paymentTransactionNumber, transactions[0].PaymentTransactionNumber)
379
	assert.False(t, transactions[0].IsFailed())
380
	assert.Equal(t, "SUCCESS", transactions[0].Status)
381
382
	// Teardown
383
	server.Close()
384
}
385
386
func TestClient_VerifyEmpty(t *testing.T) {
387
	// Setup
388
	t.Parallel()
389
390
	// Arrange
391
	server := helpers.MakeTestServer(http.StatusOK, stubs.VerifyEmpty())
392
	accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
393
	client := New(
394
		WithBaseURL(server.URL),
395
		WithAccessToken(accessToken),
396
		WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
397
	)
398
	nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
399
	paymentTransactionNumber := "99999166542651400095315364801168"
400
401
	// Act
402
	_, _, err := client.Verify(
403
		context.Background(),
404
		paymentTransactionNumber,
405
		WithRequestNonce(nonce),
406
	)
407
408
	// Assert
409
	assert.NotNil(t, err)
410
411
	// Teardown
412
	server.Close()
413
}
414
415
func computeHmac(message string, secret string) string {
416
	key := []byte(secret)
417
	h := hmac.New(sha1.New, key)
418
	_, _ = h.Write([]byte(message))
419
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
420
}
421