Skip to content

Commit

Permalink
Merge pull request #80 from merc1er/typing-and-refactor
Browse files Browse the repository at this point in the history
Add typing, tests and do some refactors
  • Loading branch information
merc1er authored Jun 10, 2024
2 parents 4fe543f + 9252b3a commit b39ffd8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 11 deletions.
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ requests==2.32.3

# Dev only packages.
black==24.4.2
coverage==7.5.3
11 changes: 10 additions & 1 deletion run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@


def main():
subprocess.call(["python", "-m", "unittest"])
# Run tests with coverage
subprocess.call(["python", "-m", "coverage", "run", "-m", "unittest", "discover"])

# Generate the coverage report in the terminal
subprocess.call(["python", "-m", "coverage", "report", "-m"])

# Generate the HTML coverage report
subprocess.call(["python", "-m", "coverage", "html"])

print("\n\nTests completed.")
print("HTML report generated at 'htmlcov/index.html'")


if __name__ == "__main__":
Expand Down
51 changes: 50 additions & 1 deletion tests/test_rates.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,58 @@
import json
import unittest
from unittest.mock import patch, MagicMock

import requests

from tipbot import rates
from .samples import UPDATE


class TestRate(unittest.TestCase):
def test_get_rate(self):
rate = rates.get_rate(UPDATE)
self.assertIs(type(rate), float)
self.assertIsInstance(rate, float)

def test_get_rate_unsupported_currency(self):
mock_update = MagicMock()
rates.get_rate(mock_update, "XYZ")

mock_update.message.reply_text.assert_called_once_with(
"XYZ is not a supported currency."
)

@patch("tipbot.rates.requests.get")
def test_get_rate_api_failure(self, mock_requests_get):
mock_requests_get.side_effect = requests.exceptions.RequestException
mock_update = MagicMock()
rates.get_rate(mock_update, "USD")

mock_update.message.reply_text.assert_called_once_with(
f"Unable to contact {rates.RATE_API}"
)

@patch("tipbot.rates.requests.get")
def test_get_rate_http_error(self, mock_requests_get):
mock_response = MagicMock()
mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError
mock_response.text = "Error message"
mock_requests_get.return_value = mock_response
mock_update = MagicMock()
rates.get_rate(mock_update, "USD")

mock_update.message.reply_text.assert_called_once_with(
f"Unable to contact {rates.RATE_API}"
)

@patch("tipbot.rates.requests.get")
def test_get_rate_parse_failure(self, mock_requests_get):
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "", 0)
mock_requests_get.return_value = mock_response
mock_update = MagicMock()
rates.get_rate(mock_update, "USD")

mock_update.message.reply_text.assert_called_once_with(
f"Unable to parse rate data: {mock_response.text}"
)
31 changes: 22 additions & 9 deletions tipbot/rates.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import json
import logging

import requests


logger = logging.getLogger(__name__)


RATE_API = "https://bitpay.com/rates/BCH/"
CURRENCY_CODE = {
"BTC": 0,
Expand Down Expand Up @@ -173,7 +179,7 @@
}


def get_rate(update, currency="USD"):
def get_rate(update, currency: str = "USD") -> float:
"""Returns the BCH price fetching BitPay API
API documentation:
Expand All @@ -182,13 +188,20 @@ def get_rate(update, currency="USD"):
currency = currency.upper()

if currency not in CURRENCY_CODE:
return update.message.reply_text(f"{currency} is not a supported " "currency.")
return update.message.reply_text(f"{currency} is not a supported currency.")

r = requests.get(RATE_API)
if r.status_code != 200: # pragma: no cover
try:
response = requests.get(RATE_API)
response.raise_for_status()
data = response.json()["data"]
rate = data[CURRENCY_CODE[currency]]["rate"]
return rate
except requests.exceptions.RequestException as e:
logger.exception("Failed to fetch rate")
if isinstance(e, requests.exceptions.HTTPError):
logger.info(response.text)
return update.message.reply_text(f"Unable to contact {RATE_API}")

data = r.json()["data"]
rate = data[CURRENCY_CODE[currency]]["rate"]

return rate
except (json.JSONDecodeError, KeyError):
message = f"Unable to parse rate data: {response.text}"
logger.exception(message)
return update.message.reply_text(message)

0 comments on commit b39ffd8

Please sign in to comment.