From 33c6806c057ce071c1ef71fd79bf98ef60c38cd8 Mon Sep 17 00:00:00 2001 From: stepanVrtek Date: Mon, 8 Jun 2020 18:03:45 +0200 Subject: [PATCH] feat(test): decred tests + minor fixes on decred --- blockapi/api/dcrdata.py | 29 ++++--- .../TestDcrdataAPI.test_get_balance.yaml | 58 +++++++++++++ .../cassettes/TestDcrdataAPI.test_get_tx.yaml | 74 +++++++++++++++++ .../TestDcrdataAPI.test_get_txs.yaml | 83 +++++++++++++++++++ blockapi/test/api/test_cosmos.py | 15 ++-- blockapi/test/api/test_dcrdata.py | 58 +++++++++++++ 6 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 blockapi/test/api/cassettes/TestDcrdataAPI.test_get_balance.yaml create mode 100644 blockapi/test/api/cassettes/TestDcrdataAPI.test_get_tx.yaml create mode 100644 blockapi/test/api/cassettes/TestDcrdataAPI.test_get_txs.yaml create mode 100644 blockapi/test/api/test_dcrdata.py diff --git a/blockapi/api/dcrdata.py b/blockapi/api/dcrdata.py index 11795d98..fdfada5d 100644 --- a/blockapi/api/dcrdata.py +++ b/blockapi/api/dcrdata.py @@ -49,22 +49,22 @@ def get_txs(self, offset=None, limit=None, unconfirmed=False): return [self.parse_tx(tx) for tx in txs] def get_tx(self, tx_hash): + """Despite the name this method is not returning single tx + but might result in the array of txs + """ tx = self.request('get_transaction', tx_hash=tx_hash) - for parsed in self.parse_tx(tx): - return parsed[0] - return None + return self.parse_tx(tx)['result'] def parse_tx(self, tx): kind = self.get_tx_kind(tx) + parsed = { 'transaction': self.parse_regular_tx, 'ticket': self.parse_ticket, 'vote': self.parse_vote, 'revocation': self.parse_revocation }.get(kind)(tx) - - parsed['kind'] = kind - return parsed + return {'kind': kind, 'result': parsed} @staticmethod def get_tx_kind(tx): @@ -83,10 +83,14 @@ def parse_regular_tx(self, tx): # Tx in decred could contain several addresses, filter only mine ins = [v for v in tx['vin'] if self.address in v.get('prevOut', {}).get('addresses', [])] - outs = [o for o in tx['vout'] - if self.address in o.get('scriptPubKey', {}).get('addresses', [])] + outs = [o for o in tx['vout'] if self.address + in o.get('scriptPubKey', {}).get('addresses', [])] - date = datetime.fromtimestamp(tx['time'], pytz.utc) + # get_txs has time attribute, get_tx has block.time attribute + if 'time' in tx: + date = datetime.fromtimestamp(tx['time'], pytz.utc) + else: + date = datetime.fromtimestamp(tx['block']['time'], pytz.utc) parsed = [] for i in ins: @@ -133,7 +137,8 @@ def parse_ticket(tx): if v['scriptPubKey']['type'] == 'stakesubmission'), 0) - # pool fee is lower value then ticket cost, but not sure if it's correct + # pool fee is lower value then ticket cost, + # but not sure if it's correct pool_fee = (min([v['amount_in'] for v in tx['vin']]) if len(tx['vin']) > 1 else 0) @@ -155,7 +160,9 @@ def parse_ticket(tx): @staticmethod def get_ticket_status(tx): # params for mainnet: - # https://github.com/decred/dcrd/blob/9da132b9823b20870122dc0bf795884cee99d922/chaincfg/mainnetparams.go#L321 + # https://github.com/decred/dcrd/blob + # /9da132b9823b20870122dc0bf795884cee99d922 + # /chaincfg/mainnetparams.go#L321 if tx['confirmations'] < 1: return 'unmined' elif tx['confirmations'] < 256: diff --git a/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_balance.yaml b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_balance.yaml new file mode 100644 index 00000000..09bc653b --- /dev/null +++ b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_balance.yaml @@ -0,0 +1,58 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.23.0 + method: GET + uri: https://explorer.dcrdata.org/api/address/DsXt3he1A9KB2uL1g3MJvbAbXEB1CxN2rNF/totals + response: + body: + string: '{"address":"DsXt3he1A9KB2uL1g3MJvbAbXEB1CxN2rNF","blockhash":"00000000000000000081b3ca6f99e5c72603397e69f546d5f74263d7039bb783","blockheight":456630,"num_stxos":165,"num_utxos":2,"dcr_spent":7509.3490332,"dcr_unspent":135.0176} + +' + headers: + Alt-Svc: + - clear + Content-Length: + - '228' + Content-Security-Policy-Report-Only: + - default-src 'self'; script-src 'self' 'sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb' + 'sha256-8in1YfOPOMqtBgRhX5uaoR+Gb7WGEjhl6XqBqFHsTjI=' 'sha256-967sY4EvCS9bV1jFWgKgDeLOvpSpm2Sp9aCP537+BCA=' + 'sha256-ncTH9jyLor0RXzz2qH5UZEOpKBNwPyO/SrZW+vrYkA8=' 'sha384-e0qFAVkArN1dyymxMH35JdOyqaonxrxODNFAvMxThOhlG4XGCjfW92Y8GQmud8hK' + 'sha384-eEQWQilDnDtUa8WKud8RTuw5eAoUbvrfoBjxyn1emDfDHoh8qXFpmy8IeP+2voml'; + font-src 'self'; object-src 'none'; img-src 'self' data:; style-src 'self' + 'unsafe-inline'; connect-src 'self' wss://*.dcrdata.org; manifest-src 'self'; + frame-ancestors 'none' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 08 Jun 2020 11:41:46 GMT + Referrer-Policy: + - same-origin + Server: + - nginx + Set-Cookie: + - GCLB=CPbn_86Ksuu3zAE; path=/; HttpOnly + Strict-Transport-Security: + - max-age=15552001 + Vary: + - Origin + Via: + - 1.1 google + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_tx.yaml b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_tx.yaml new file mode 100644 index 00000000..8ac63788 --- /dev/null +++ b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_tx.yaml @@ -0,0 +1,74 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.23.0 + method: GET + uri: https://explorer.dcrdata.org/api/tx/193b59d8926588181aad5a5bed672e1fccf443f4d05dc16a0cdaacf3b4b4ed7c?spends=true + response: + body: + string: !!binary | + H4sIAAAAAAAAA81VTW8bNxC991cYexaM4fDbN0eR6tip68ZNkDYIgiE5tBaOJUcrKVIM/ffOSlYK + 1D0Y6aW6SLskZ96b9/j00CzWbWlOGhV1srGEiM6GoIIiKpZs4uI8sqo5V2N0NQVsycoR5EKUq04m + GS4+N4Oma79xc4IWB82K5107mzYnatB8nuXbRXsnSzBoeH3fzje7n6tW1j88HACY6qstUpsRrSOf + tcnSKDvH2mO1ynHyoEpxkGRBUdVWKwxUyPuQvABYzZaLXenFnPftOv6y5GmWB4PRRKES7aChu9ly + uujb66CPVdDRoQ6DJvVQJ9zeTKSM0YAKH1+208LrHZsuz9v7xXV705w8NNTdCXINxgKiAkhFQTEB + kMkZFbOPSifyxQVPKaakFehoM7EP5BUl9kkB2Zq8Y5RFqQJGMCJizlmkiJGUrsrFkBi59mNxGarV + 2Rsj9b1TLE1qqFkFNM5zBHUEaGV8jgwiSy0ZmlRjnTByKUA+BquLHI9Bi1rB5ZKgCsRiGDMHApBh + TnrCjQn/N3aC5L/T224/HuwiDlzR52Xv3BiO5bDam2G6N+nByHCQ/mqZLnjzt/q/Xn16+fbqSL7O + Tq/PlIOjYpQSfla7YJyARhJy3kQoGiz3/tbiYgr9mdFvb09fvxu9eTX+o38cno2GF9evfv4+f+8o + KvPciiFQfxHn/EUM2u38utjcC7XmfplueTOhbiLrVMqcu45lx4fmZXd7PjKzOv5zQ+PL4dVw2r5b + /b4J9fJ8PX7zbf2VXPNxK+zveVp62rsi4gzjhGyNCD5yhQomyritqGdCDpQoZNmgUWySfNCBaxYT + iNClkkhgm10CfHq8WbDdDr4LEcyx9navgfoxDUwqkgshivEM9MkltxzFWyVaw9qw672oSnq+Bs+t + +GMavF/oCavTePECl6/Vjf7lfJVO0/vRCzVcX+L8cvyvGsTqnRYdktKmSJYlBslkKuIG8FyoilEk + 02swoUCK3tSQLXgvQmStdXmigdyKPJvWdn5HCxm5gEMHeIjGvvM+I/ft4cknkACJIctQUOJb0qGa + /mI6waky1eoLxmoNJLRQm6eR6xCeRO7+z0NZMRGgd+5xwz9fb7c//QUwvECTzgYAAA== + headers: + Alt-Svc: + - clear + Content-Encoding: + - gzip + Content-Security-Policy-Report-Only: + - default-src 'self'; script-src 'self' 'sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb' + 'sha256-8in1YfOPOMqtBgRhX5uaoR+Gb7WGEjhl6XqBqFHsTjI=' 'sha256-967sY4EvCS9bV1jFWgKgDeLOvpSpm2Sp9aCP537+BCA=' + 'sha256-ncTH9jyLor0RXzz2qH5UZEOpKBNwPyO/SrZW+vrYkA8=' 'sha384-e0qFAVkArN1dyymxMH35JdOyqaonxrxODNFAvMxThOhlG4XGCjfW92Y8GQmud8hK' + 'sha384-eEQWQilDnDtUa8WKud8RTuw5eAoUbvrfoBjxyn1emDfDHoh8qXFpmy8IeP+2voml'; + font-src 'self'; object-src 'none'; img-src 'self' data:; style-src 'self' + 'unsafe-inline'; connect-src 'self' wss://*.dcrdata.org; manifest-src 'self'; + frame-ancestors 'none' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 08 Jun 2020 13:42:58 GMT + Referrer-Policy: + - same-origin + Server: + - nginx + Set-Cookie: + - GCLB=CMXAx6ObyODqFg; path=/; HttpOnly + Strict-Transport-Security: + - max-age=15552001 + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding + - Origin + Via: + - 1.1 google + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_txs.yaml b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_txs.yaml new file mode 100644 index 00000000..ccbfd85b --- /dev/null +++ b/blockapi/test/api/cassettes/TestDcrdataAPI.test_get_txs.yaml @@ -0,0 +1,83 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.23.0 + method: GET + uri: https://explorer.dcrdata.org/api/address/DsXt3he1A9KB2uL1g3MJvbAbXEB1CxN2rNF/count/2/skip/0/raw + response: + body: + string: !!binary | + H4sIAAAAAAAAA81X204cRxB9z1dY84xQVXX1jTfuGLCzNgKTWCjqSzW7xtx2Fwuw+PfUgMGxHSdr + nEjwAMzsTlfX6VPnnHn7sZuMrqVbIEtz3fRyVLuFLjbvDDvMaLiaYLJAKi1VUyt4qalRolhDCxwq + 5Oi5hWLBezJYjDG1m+s+yHgyOj3pFnCue39ajqajYy0C+sFIb779eF8Ko8lW14rkbAgYMKVqk81S + nSfBVkpjNo0r2FrQJSg16V5M5sxSfelLnV5Mb+tMx3JXIx2fXpxM+0KB5423Ya7L/SaGMjoc6nfZ + gCP4dHN0UuXy9vlJGY/Opjujw27hY5cmx7o9A2yBCAEkWSvZpYS1xuZAGjcPujnbqkTrEKsp3HKq + zdgMwE0QmGwuLbZsSFdRhICaWKNdNEdNe9XrkgJxkQJJWqxk2YpvNnOpWpxcYeakhagmBnwGWgR1 + wWZJMSCXULwJoXlJUq1IvzESVymFiBGKR5QcY+UATlIsJipiw77hjsNT60538vPt3cx1Z2P58GtP + Cj3GWscymchESdetTPanZii4GLeW6GIbD82LzQ95Me+vLuHy5Usav1zrDpRQ6f2FPHBH15vI+YWc + FL3HFDkqM6O9ObhnnpL5/gkzr1Q7ueP5/QDAPbEGF3lLrj5z69fBHyu7g2f6Z2NxZwMdPGuAoBPG + CSk7L5wKoYCNklwJhaQ4iy6Y1j+z+mp3cXtv9fXztd/6y+WN1eWtnefrD6frXYrIs64YQupHaSzn + Sv/J3TRdnWlL3dlFPpKrYZoM9fOv0DwZr50era1MGg2uNncOeaOuDXlbwsXy9HzTn19jd3BzM/cA + D8xbb71nf4sRPg6jFFMtxfvolEghlOiglWhjQGszOtOcNsQpz47RrCs+DqPDHTvNu3uTVy348eXe + 0puj/bOdF5t7e+VsOJi8HMUeI+VSOT1po/Fxmiog/eJGZ+FetvqFFzr4+ieH3Niyjm5iZyA4n2vE + QJh7rY4p99ocsq9CnqAf/DsVRhu8i8Y496nA17f7M7tzBUb34Aq5CDuFIavsR5/IqiZggkSFrGvk + deybNSHpf44zep3Q5hTZHK2PXuIPuEIUj5KMTcET669K6gWiMqBGE4ECZxOyMyaq9KDKFNpqMlQx + Kkuo7vEvrsBxPn5jCSYyzmgJ3MudeqMvxid1sIYtFesy+8xVghpVIyxggsqnRxBFyYQqAgWLZa/X + qCTrJVP1U8GrKaqcWgFsEPSLRbmosLIiTSW2qvAlNUHHzYm6sQct25oJ7j82BP+0entadtCT5nte + MPfAXCoQkpZvmZSU2qnKSwZNNCGFJsn3pku5UQxSIbWkrstOdZhrrTp5n5kLf59nYF6ji3cevyUw + OIgzEviT65eosKkX21oxZQuqGjbouJYQQcWErdet6cmZAHoAlU1SHqgiOvBq3JhvjxkyZq/bB47C + lMlaG6QkCH2sI2VBEWGj2U1PJYZgM4FGDDAaHlwo/2OmeRrdPS0S/4U/P5BrjD7F6iGW4s/EGwEI + Rk+HjRov2UasfuVIZ4CMzo2zJut7gKmzW/esKz7Ouo8Hu4c7V0fr6/uTrfW0OzxZmgi/4zq4Nq/P + V/fT3pfxJqpA0M9EG7VsMd60qFAHRCOFIiJVTdWoPNF8ki0qv2fHZ9YVH4fPm5XpyK1fn4ffX22/ + KVfJLXO+ajw4D+/2dsri6Pg70YYt0j9HG30xQGLvNDPpvGkPtRgBY4mo6dtJjpxFs4XRKw0gQF9E + G/IWIn8Tbe5u3xz88ifb5nWK8w4AAA== + headers: + Alt-Svc: + - clear + Content-Encoding: + - gzip + Content-Security-Policy-Report-Only: + - default-src 'self'; script-src 'self' 'sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb' + 'sha256-8in1YfOPOMqtBgRhX5uaoR+Gb7WGEjhl6XqBqFHsTjI=' 'sha256-967sY4EvCS9bV1jFWgKgDeLOvpSpm2Sp9aCP537+BCA=' + 'sha256-ncTH9jyLor0RXzz2qH5UZEOpKBNwPyO/SrZW+vrYkA8=' 'sha384-e0qFAVkArN1dyymxMH35JdOyqaonxrxODNFAvMxThOhlG4XGCjfW92Y8GQmud8hK' + 'sha384-eEQWQilDnDtUa8WKud8RTuw5eAoUbvrfoBjxyn1emDfDHoh8qXFpmy8IeP+2voml'; + font-src 'self'; object-src 'none'; img-src 'self' data:; style-src 'self' + 'unsafe-inline'; connect-src 'self' wss://*.dcrdata.org; manifest-src 'self'; + frame-ancestors 'none' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 08 Jun 2020 12:01:12 GMT + Referrer-Policy: + - same-origin + Server: + - nginx + Set-Cookie: + - GCLB=CKb55uHG9bv_DA; path=/; HttpOnly + Strict-Transport-Security: + - max-age=15552001 + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding + - Origin + Via: + - 1.1 google + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/blockapi/test/api/test_cosmos.py b/blockapi/test/api/test_cosmos.py index a5bc86b1..64705a33 100644 --- a/blockapi/test/api/test_cosmos.py +++ b/blockapi/test/api/test_cosmos.py @@ -1,4 +1,4 @@ -import pytest +from pytest import mark, raises from blockapi.api.cosmos import CosmosAPI from blockapi.services import InternalServerError, AddressNotExist @@ -12,7 +12,6 @@ class TestCosmosAPI: def test_init(self): api = CosmosAPI(address=self.ADDRESS) - assert api def test_process_error_response(self): @@ -21,15 +20,15 @@ def test_process_error_response(self): response.text = "Error" response.status_code = 500 - with pytest.raises(InternalServerError): + with raises(InternalServerError): CosmosAPI(address=self.ADDRESS).process_error_response(response) response.text = "Error decoding bech32 failed" - with pytest.raises(AddressNotExist): + with raises(AddressNotExist): CosmosAPI(address=self.ADDRESS).process_error_response(response) - @pytest.mark.vcr() + @mark.vcr() def test_get_info(self): api = CosmosAPI(address=self.ADDRESS) result = api.get_info() @@ -44,7 +43,7 @@ def test_get_info(self): assert "account_number" in result["result"]["value"] assert "sequence" in result["result"]["value"] - @pytest.mark.vcr() + @mark.vcr() def test_get_balance(self): api = CosmosAPI(address=self.ADDRESS) result = api.get_balance() @@ -52,14 +51,14 @@ def test_get_balance(self): assert result["balances"] == [{"symbol": "ATOM", "amount": 0.005959}] assert "height" in result - @pytest.mark.vcr() + @mark.vcr() def test_get_incoming_txs(self): api = CosmosAPI(address=self.ADDRESS) api.get_incoming_txs() # TODO: provider is no sending correct data - @pytest.mark.vcr() + @mark.vcr() def test_get_outgoing_txs(self): api = CosmosAPI(address=self.ADDRESS) api.get_outgoing_txs() diff --git a/blockapi/test/api/test_dcrdata.py b/blockapi/test/api/test_dcrdata.py new file mode 100644 index 00000000..cc413d8a --- /dev/null +++ b/blockapi/test/api/test_dcrdata.py @@ -0,0 +1,58 @@ +from pytest import mark, raises + +from blockapi.api.dcrdata import DcrdataAPI +from blockapi.services import InternalServerError, AddressNotExist +from blockapi.test_init import test_addresses + + +class TestDcrdataAPI: + + ADDRESS = test_addresses["DCR"][0] + + def test_init(self): + api = DcrdataAPI(address=self.ADDRESS) + assert api + + def test_process_error_response(self): + response = TestResponse() + + response.text = "Error" + response.status_code = 500 + + with raises(InternalServerError): + DcrdataAPI(address=self.ADDRESS).process_error_response(response) + + response.status_code = 422 + + with raises(AddressNotExist): + DcrdataAPI(address=self.ADDRESS).process_error_response(response) + + @mark.vcr() + def test_get_balance(self): + api = DcrdataAPI(address=self.ADDRESS) + result = api.get_balance() + + assert result == [{'symbol': 'DCR', 'amount': 135.0176}] + + @mark.vcr() + def test_get_txs(self): + api = DcrdataAPI(address=self.ADDRESS) + result = api.get_txs(limit=2) + + assert len(result) == 2 + assert all(k in ["kind", "result"] for k in result[0].keys()) + + @mark.vcr() + def test_get_tx(self): + hash_ = "193b59d8926588181aad5a5bed672e1fccf443f4d05dc1" \ + "6a0cdaacf3b4b4ed7c" + api = DcrdataAPI(address=self.ADDRESS) + result = api.get_tx(tx_hash=hash_) + + assert len(result) == 1 + + +class TestResponse: + + text = None + status_code = 200