Completed
Push — master ( a48a20...2f6ef6 )
by Andreas
8s
created

TestBtcdeAPIDocu.test_signature_get()   A

Complexity

Conditions 1

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Importance

Changes 6
Bugs 1 Features 2
Metric Value
cc 1
c 6
b 1
f 2
dl 15
loc 15
rs 9.4285
1
from unittest import TestCase
2
import hashlib
3
import hmac
4
import requests_mock
5
from mock import patch
6
import json
7
import btcde
8
9
10
@patch('btcde.log')
11
@requests_mock.Mocker()
12
class TestBtcdeAPIDocu(TestCase):
13
    '''Tests are as in bitcoin.de API documentation.
14
    https://www.bitcoin.de/de/api/tapi/v2/docu'''
15
    
16
    def sampleData(self, file):
17
        '''Retrieve sample data from json files.'''
18
        filepath = 'tests/resources/{}.json'.format(file)
19
        data = json.load(open(filepath))
20
        return data
21
22 View Code Duplication
    def sortParams(self, url, params={}):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
23
        '''To sort params for url string.'''
24
        self.encoded_string = ''
25
        if len(params) > 0:
26
            for key, value in sorted(params.items()):
27
                self.encoded_string += str(key) + '=' + str(value) + '&'
28
            self.encoded_string = self.encoded_string[:-1]
29
            self.url = url + '?' + self.encoded_string
30
        else:
31
            self.url = url
32
        return self.url, self.encoded_string
33
34
    def verifySignature(self, url, method, params):
35
        '''To verify API Signature.'''
36
        self.XAPINONCE += 1
37
        self.url, self.encoded_string = self.sortParams(url, params)
38
        if method == 'POST':
39
            md5_encoded_query_string = hashlib.md5(self.encoded_string.encode()
40
                                                   ).hexdigest()
41
        else:
42
            md5_encoded_query_string = hashlib.md5(b'').hexdigest()
43
        hmac_data = '{method}#{url}#{key}#{nonce}#{md5}'\
44
                    .format(method=method,
45
                            url=self.url,
46
                            key=self.XAPIKEY,
47
                            nonce=str(self.XAPINONCE),
48
                            md5=md5_encoded_query_string)
49
        hmac_signed = hmac.new(bytearray(self.XAPISECRET.encode()),
50
                               msg=hmac_data.encode(),
51
                               digestmod=hashlib.sha256).hexdigest()
52
        return hmac_signed
53
54
    def setUp(self):
55
        self.XAPIKEY = 'f00b4r'
56
        self.XAPISECRET = 'b4rf00'
57
        self.conn = btcde.Connection(self.XAPIKEY, self.XAPISECRET)
58
        self.XAPINONCE = self.conn.nonce
59
60
    def tearDown(self):
61
        del self.XAPIKEY
62
        del self.XAPISECRET
63
        del self.conn
64
65 View Code Duplication
    def test_signature_post(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
66
        '''Test signature on a post request.'''
67
        params = {'type': 'buy',
68
                  'trading_pair': 'btceur',
69
                  'max_amount': 10,
70
                  'price': 1337}
71
        response = self.sampleData('createOrder')
72
        m.post(requests_mock.ANY, json=response, status_code=201)
73
        self.conn.createOrder(params.get('type'),
74
                              params.get('trading_pair'),
75
                              params.get('max_amount'),
76
                              params.get('price'))
77
        history = m.request_history
78
        request_signature = history[0].headers.get('X-API-SIGNATURE')
79
        verified_signature = self.verifySignature(self.conn.orderuri,
80
                                                  'POST',
81
                                                  params)
82
        self.assertEqual(request_signature, verified_signature)
83
        self.assertTrue(mock_logger.debug.called)
84
85 View Code Duplication
    def test_signature_get(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
86
        '''Test signature on a get request.'''
87
        params = {'type': 'buy',
88
                  'trading_pair': 'btceur'}
89
        response = self.sampleData('showOrderbook_buy')
90
        m.get(requests_mock.ANY, json=response, status_code=200)
91
        self.conn.showOrderbook(params.get('type'),
92
                                params.get('trading_pair'))
93
        history = m.request_history
94
        request_signature = history[0].headers.get('X-API-SIGNATURE')
95
        verified_signature = self.verifySignature(self.conn.orderuri,
96
                                                  'GET',
97
                                                  params)
98
        self.assertEqual(request_signature, verified_signature)
99
        self.assertTrue(mock_logger.debug.called)
100
101
    def test_signature_delete(self, mock_logger, m):
102
        '''Test signature on a delete request.'''
103
        order_id = 'A1234BC'
104
        trading_pair = 'btceur'
105
        m.delete(requests_mock.ANY, json={}, status_code=200)
106
        self.conn.deleteOrder(order_id, trading_pair)
107
        history = m.request_history
108
        request_signature = history[0].headers.get('X-API-SIGNATURE')
109
        url = self.conn.orderuri + "/" + order_id + "/" + trading_pair
110
        verified_signature = self.verifySignature(url, 'DELETE', {})
111
        self.assertEqual(request_signature, verified_signature)
112
        self.assertTrue(mock_logger.debug.called)
113
114 View Code Duplication
    def test_show_orderbook(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
115
        '''Test function showOrderbook.'''
116
        params = {'type': 'buy',
117
                  'trading_pair': 'btceur',
118
                  'max_amount': 10,
119
                  'price': 1337}
120
        base_url = 'https://api.bitcoin.de/v2/orders'
121
        url_args = '?price={}&trading_pair={}&type={}'\
122
                   .format(params.get('price'),
123
                           params.get('trading_pair'),
124
                           params.get('type'))
125
        response = self.sampleData('showOrderbook_buy')
126
        m.get(requests_mock.ANY, json=response, status_code=200)
127
        self.conn.showOrderbook(params.get('type'),
128
                                params.get('trading_pair'),
129
                                price=params.get('price'))
130
        history = m.request_history
131
        self.assertEqual(history[0].method, "GET")
132
        self.assertEqual(history[0].url, base_url + url_args)
133
        self.assertTrue(mock_logger.debug.called)
134
135 View Code Duplication
    def test_createOrder(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
136
        '''Test function createOrder.'''
137
        params = {'type': 'buy',
138
                  'trading_pair': 'btceur',
139
                  'max_amount': '10',
140
                  'price': '10'}
141
        base_url = 'https://api.bitcoin.de/v2/orders'
142
        url_args = '?max_amount={}&price={}&trading_pair={}&type={}'\
143
                   .format(params.get('max_amount'),
144
                           params.get('price'),
145
                           params.get('trading_pair'),
146
                           params.get('type'))
147
        response = self.sampleData('createOrder')
148
        m.post(requests_mock.ANY, json=response, status_code=201)
149
        self.conn.createOrder(params.get('type'),
150
                              params.get('trading_pair'),
151
                              params.get('max_amount'),
152
                              params.get('price'))
153
        history = m.request_history
154
        self.assertEqual(history[0].method, "POST")
155
        self.assertEqual(history[0].url, base_url + url_args)
156
        self.assertTrue(mock_logger.debug.called)
157
158 View Code Duplication
    def test_deleteOrder(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
159
        '''Test function deleteOrder.'''
160
        params = {'trading_pair': 'btceur',
161
                  'order_id': '1337'}
162
        base_url = 'https://api.bitcoin.de/v2/orders'
163
        url_args = '/{}/{}'.format(params.get('order_id'),
164
                                   params.get('trading_pair'))
165
        response = self.sampleData('deleteOrder')
166
        m.delete(requests_mock.ANY, json=response, status_code=200)
167
        self.conn.deleteOrder(params.get('order_id'),
168
                              params.get('trading_pair'))
169
        history = m.request_history
170
        self.assertEqual(history[0].method, "DELETE")
171
        self.assertEqual(history[0].url, base_url + url_args)
172
        self.assertTrue(mock_logger.debug.called)
173
174 View Code Duplication
    def test_showMyOrders(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
175
        '''Test function showMyOrders.'''
176
        params = {'type': 'buy',
177
                  'trading_pair': 'btceur'}
178
        base_url = 'https://api.bitcoin.de/v2/orders/my_own'
179
        url_args = '?trading_pair={}&type={}'\
180
                   .format(params.get('trading_pair'),
181
                           params.get('type'))
182
        response = self.sampleData('showMyOrders')
183
        m.get(requests_mock.ANY, json=response, status_code=200)
184
        self.conn.showMyOrders(type=params.get('type'),
185
                               trading_pair=params.get('trading_pair'))
186
        history = m.request_history
187
        self.assertEqual(history[0].method, "GET")
188
        self.assertEqual(history[0].url, base_url + url_args)
189
        self.assertTrue(mock_logger.debug.called)
190
191 View Code Duplication
    def test_showMyOrderDetails(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
192
        '''Test function showMyOrderDetails.'''
193
        params = {'order_id': '1337'}
194
        base_url = 'https://api.bitcoin.de/v2/orders/{}'\
195
                   .format(params.get('order_id'))
196
        url_args = ''
197
        response = self.sampleData('showMyOrderDetails')
198
        m.get(requests_mock.ANY, json=response, status_code=200)
199
        self.conn.showMyOrderDetails(params.get('order_id'))
200
        history = m.request_history
201
        self.assertEqual(history[0].method, "GET")
202
        self.assertEqual(history[0].url, base_url + url_args)
203
        self.assertTrue(mock_logger.debug.called)
204
205 View Code Duplication
    def test_executeTrade(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
206
        '''Test function executeTrade.'''
207
        params = {'type': 'buy',
208
                  'trading_pair': 'btceur',
209
                  'amount': '10',
210
                  'order_id': '1337'}
211
        base_url = 'https://api.bitcoin.de/v2/trades/{}'\
212
                   .format(params.get('order_id'))
213
        url_args = '?amount={}&order_id={}&trading_pair={}&type={}'\
214
                   .format(params.get('amount'),
215
                           params.get('order_id'),
216
                           params.get('trading_pair'),
217
                           params.get('type'))
218
        response = self.sampleData('executeTrade')
219
        m.get(requests_mock.ANY, json=response, status_code=200)
220
        m.post(requests_mock.ANY, json=response, status_code=201)
221
        self.conn.executeTrade(params.get('order_id'),
222
                               params.get('type'),
223
                               params.get('trading_pair'),
224
                               params.get('amount'))
225
        history = m.request_history
226
        self.assertEqual(history[0].method, "POST")
227
        self.assertEqual(history[0].url, base_url + url_args)
228
        self.assertTrue(mock_logger.debug.called)
229
230 View Code Duplication
    def test_showMyTrades(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
231
        '''Test function showMyTrades.'''
232
        base_url = 'https://api.bitcoin.de/v2/trades'
233
        url_args = ''
234
        response = self.sampleData('showMyTrades')
235
        m.get(requests_mock.ANY, json=response, status_code=200)
236
        self.conn.showMyTrades()
237
        history = m.request_history
238
        self.assertEqual(history[0].method, "GET")
239
        self.assertEqual(history[0].url, base_url + url_args)
240
        self.assertTrue(mock_logger.debug.called)
241
242 View Code Duplication
    def test_showMyTrades_with_params(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
243
        '''Test function showMyTrades with parameters.'''
244
        params = {'type': 'buy',
245
                  'trading_pair': 'btceur'}
246
        base_url = 'https://api.bitcoin.de/v2/trades'
247
        url_args = '?trading_pair={}&type={}'\
248
                   .format(params.get('trading_pair'),
249
                           params.get('type'))
250
        response = self.sampleData('showMyTrades')
251
        m.get(requests_mock.ANY, json=response, status_code=200)
252
        self.conn.showMyTrades(type=params.get('type'),
253
                               trading_pair=params.get('trading_pair'))
254
        history = m.request_history
255
        self.assertEqual(history[0].method, "GET")
256
        self.assertEqual(history[0].url, base_url + url_args)
257
        self.assertTrue(mock_logger.debug.called)
258
259 View Code Duplication
    def test_showMyTradeDetails(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
260
        '''Test function showMyTradeDetails.'''
261
        params = {'trade_id': '1337'}
262
        base_url = 'https://api.bitcoin.de/v2/trades'
263
        url_args = '/{}'.format(params.get('trade_id'))
264
        response = self.sampleData('showMyTradeDetails')
265
        m.get(requests_mock.ANY, json=response, status_code=200)
266
        self.conn.showMyTradeDetails(params.get('trade_id'))
267
        history = m.request_history
268
        self.assertEqual(history[0].method, "GET")
269
        self.assertEqual(history[0].url, base_url + url_args)
270
        self.assertTrue(mock_logger.debug.called)
271
272 View Code Duplication
    def test_showAccountInfo(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
273
        '''Test function showAccountInfo.'''
274
        base_url = 'https://api.bitcoin.de/v2/account'
275
        url_args = ''
276
        response = self.sampleData('showAccountInfo')
277
        m.get(requests_mock.ANY, json=response, status_code=200)
278
        self.conn.showAccountInfo()
279
        history = m.request_history
280
        self.assertEqual(history[0].method, "GET")
281
        self.assertEqual(history[0].url, base_url + url_args)
282
        self.assertTrue(mock_logger.debug.called)
283
284 View Code Duplication
    def test_showOrderbookCompact(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
285
        '''Test function showOrderbookCompact.'''
286
        params = {'trading_pair': 'btceur'}
287
        base_url = 'https://api.bitcoin.de/v2/orders/compact'
288
        url_args = '?trading_pair={}'.format(params.get('trading_pair'))
289
        response = self.sampleData('showOrderbookCompact')
290
        m.get(requests_mock.ANY, json=response, status_code=200)
291
        self.conn.showOrderbookCompact(params.get('trading_pair'))
292
        history = m.request_history
293
        self.assertEqual(history[0].method, "GET")
294
        self.assertEqual(history[0].url, base_url + url_args)
295
        self.assertTrue(mock_logger.debug.called)
296
297 View Code Duplication
    def test_showPublicTradeHistory(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
298
        '''Test function showPublicTradeHistory.'''
299
        params = {'trading_pair': 'btceur'}
300
        base_url = 'https://api.bitcoin.de/v2/trades/history'
301
        url_args = '?trading_pair={}'.format(params.get('trading_pair'))
302
        response = self.sampleData('showPublicTradeHistory')
303
        m.get(requests_mock.ANY, json=response, status_code=200)
304
        self.conn.showPublicTradeHistory(params.get('trading_pair'))
305
        history = m.request_history
306
        self.assertEqual(history[0].method, "GET")
307
        self.assertEqual(history[0].url, base_url + url_args)
308
        self.assertTrue(mock_logger.debug.called)
309
310 View Code Duplication
    def test_showPublicTradeHistory_since(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
311
        '''Test function showPublicTradeHistory with since_tid.'''
312
        params = {'trading_pair': 'btceur', 'since_tid': '123'}
313
        base_url = 'https://api.bitcoin.de/v2/trades/history'
314
        url_args = '?since_tid={}&trading_pair={}'.format(params.get('since_tid'),
315
                                                          params.get('trading_pair'))
316
        response = self.sampleData('showPublicTradeHistory')
317
        m.get(requests_mock.ANY, json=response, status_code=200)
318
        self.conn.showPublicTradeHistory(params.get('trading_pair'),
319
                                         since_tid=params.get('since_tid'))
320
        history = m.request_history
321
        self.assertEqual(history[0].method, "GET")
322
        self.assertEqual(history[0].url, base_url + url_args)
323
        self.assertTrue(mock_logger.debug.called)
324
325 View Code Duplication
    def test_showRates(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
326
        '''Test function showRates.'''
327
        params = {'trading_pair': 'btceur'}
328
        base_url = 'https://api.bitcoin.de/v2/rates'
329
        url_args = '?trading_pair={}'.format(params.get('trading_pair'))
330
        response = self.sampleData('showRates')
331
        m.get(requests_mock.ANY, json=response, status_code=200)
332
        self.conn.showRates(params.get('trading_pair'))
333
        history = m.request_history
334
        self.assertEqual(history[0].method, "GET")
335
        self.assertEqual(history[0].url, base_url + url_args)
336
        self.assertTrue(mock_logger.debug.called)
337
338 View Code Duplication
    def test_showAccountLedger(self, mock_logger, m):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
339
        '''Test function showAccountLedger.'''
340
        params = {'currency': 'btc'}
341
        base_url = 'https://api.bitcoin.de/v2/account/ledger'
342
        url_args = '?currency={}'.format(params.get('currency'))
343
        response = self.sampleData('showAccountLedger')
344
        m.get(requests_mock.ANY, json=response, status_code=200)
345
        self.conn.showAccountLedger(params.get('currency'))
346
        history = m.request_history
347
        self.assertEqual(history[0].method, "GET")
348
        self.assertEqual(history[0].url, base_url + url_args)
349
        self.assertTrue(mock_logger.debug.called)
350
351
352
class TestBtcdeExceptions(TestCase):
353
    '''Test for Exception Handling.'''
354
    
355
    def sampleData(self, file):
356
        '''Retrieve sample data from json files.'''
357
        filepath = 'tests/resources/{}.json'.format(file)
358
        data = json.load(open(filepath))
359
        return data
360
        
361
    def setUp(self):
362
        self.XAPIKEY = 'f00b4r'
363
        self.XAPISECRET = 'b4rf00'
364
        self.conn = btcde.Connection(self.XAPIKEY, self.XAPISECRET)      
365
        self.XAPINONCE = self.conn.nonce
366
367
    def tearDown(self):
368
        del self.XAPIKEY
369
        del self.XAPISECRET
370
        del self.conn
371
372 View Code Duplication
    @requests_mock.Mocker()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
373
    def test_APIException(self, m):
374
        '''Test API Exception.'''
375
        params = {'type': 'buy',
376
                  'trading_pair': 'btceur',
377
                  'max_amount': 10,
378
                  'price': 13}
379
        base_url = 'https://api.bitcoin.de/v2/orders'
380
        url_args = '?max_amount={}&price={}&trading_pair={}&type={}'\
381
                   .format(params.get('max_amount'),
382
                           params.get('price'),
383
                           params.get('trading_pair'),
384
                           params.get('type'))
385
        response = self.sampleData('error')
386
        m.post(requests_mock.ANY, json=response, status_code=400)
387
        self.conn.createOrder(params.get('type'),
388
                              params.get('trading_pair'), params.get('max_amount'),
389
                              price=params.get('price'))
390
        history = m.request_history
391
        self.assertEqual(history[0].method, "POST")
392
        self.assertEqual(history[0].url, base_url + url_args)
393
        
394
    @patch('btcde.log')
395
    def test_RequestException(self, mock_logger):
396
        '''Test Requests Exception.'''
397
        params = {'type': 'buy',
398
                  'trading_pair': 'btceur',
399
                  'max_amount': 10,
400
                  'price': 13} 
401
        self.conn.orderuri = 'https://foo.bar'
402
        self.conn.createOrder(params.get('type'),
403
                              params.get('trading_pair'), params.get('max_amount'),
404
                              price=params.get('price'))
405
        self.assertTrue(mock_logger.warning.called)
406
        
407
    def test_TradingPairValueException(self):
408
        '''Test wrong traiding_pair Value Exception.'''
409
        with self.assertRaises(ValueError) as context:
410
            self.conn.deleteOrder('123', 'usdeur')
411
        self.assertTrue('usdeur is not any of' in str(context.exception))
412
413
    def test_OrderTypeValueException(self):
414
        '''Test wrong type Value Exception.'''
415
        with self.assertRaises(ValueError) as context:
416
            self.conn.createOrder('fail', 'btceur', '100', '100')
417
        self.assertTrue('fail is not any of' in str(context.exception))
418
419
    def test_CurrencyValueException(self):
420
        '''Test wrong currency Value Exception.'''
421
        with self.assertRaises(ValueError) as context:
422
            self.conn.showAccountLedger('usd')
423
        self.assertTrue('usd is not any of' in str(context.exception))
424
425
    def test_BankSeatValueException(self):
426
        '''Test wrong seat_of_bank Value Exception.'''
427
        with self.assertRaises(ValueError) as context:
428
            self.conn.showOrderbook('buy', 'btceur', seat_of_bank='SZ')
429
        self.assertTrue('SZ is not any of' in str(context.exception))
430
431
    def test_TrustLevelValueException(self):
432
        '''Test wrong trust_level Value Exception.'''
433
        with self.assertRaises(ValueError) as context:
434
            self.conn.createOrder('buy', 'btceur', '100', '100',
435
                                   min_trust_level='foo')
436
        self.assertTrue('foo is not any of' in str(context.exception))
437
438
    def test_PaymentOptionValueException(self):
439
        '''Test wrong payment_option Value Exception.'''
440
        with self.assertRaises(ValueError) as context:
441
            self.conn.createOrder('buy', 'btceur', '100', '100',
442
                                   payment_option=4)
443
        self.assertTrue('4 is not any of' in str(context.exception))
444
445
    def test_StateValueException(self):
446
        '''Test wrong state Value Exception.'''
447
        with self.assertRaises(ValueError) as context:
448
            self.conn.showMyOrders(state=4)
449
        self.assertTrue('4 is not any of' in str(context.exception))
450
451
    def test_UnknownKeyException(self):
452
        '''Test wrong Key Exception.'''
453
        with self.assertRaises(KeyError) as context:
454
            self.conn.showMyOrders(foo=4)
455
        self.assertTrue('foo is not any of' in str(context.exception))
456