mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Mock external API calls in price provider tests (#2523)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: k9ert <117085+k9ert@users.noreply.github.com>
This commit is contained in:
parent
6644b7cad9
commit
5743b58035
2 changed files with 123 additions and 39 deletions
|
|
@ -118,7 +118,6 @@ def get_price_at(specter, timestamp="now"):
|
|||
)
|
||||
except AttributeError:
|
||||
raise SpecterError(f"Currency not supported: {currency}")
|
||||
print("MUH" + specter.price_provider)
|
||||
if exchange not in currency_mapping[currency]["support"]:
|
||||
raise SpecterError(
|
||||
f"The currency {currency} is not supported on exchange {exchange}"
|
||||
|
|
|
|||
|
|
@ -9,92 +9,177 @@ from cryptoadvance.specter.util.price_providers import (
|
|||
_parse_exchange_currency,
|
||||
failsafe_request_get,
|
||||
)
|
||||
from mock import MagicMock
|
||||
from mock import MagicMock, Mock, patch
|
||||
|
||||
|
||||
def test_underlying_requests(empty_data_folder):
|
||||
"""This test might fail on MacOS
|
||||
see #1512
|
||||
Mocked to avoid external API dependency and flaky test failures.
|
||||
"""
|
||||
specter_mock = Specter(data_folder=empty_data_folder, checker_threads=False)
|
||||
requests_session = specter_mock.requests_session()
|
||||
currency = "eur"
|
||||
price = requests_session.get(
|
||||
"https://www.bitstamp.net/api/v2/ticker/btc{}".format(currency)
|
||||
).json()["last"]
|
||||
assert type(price) == str
|
||||
assert float(price)
|
||||
|
||||
# Mock the Bitstamp API response
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"last": "45123.50"}
|
||||
|
||||
with patch.object(requests_session, 'get', return_value=mock_response):
|
||||
price = requests_session.get(
|
||||
"https://www.bitstamp.net/api/v2/ticker/btc{}".format(currency)
|
||||
).json()["last"]
|
||||
assert type(price) == str
|
||||
assert float(price)
|
||||
|
||||
|
||||
def test_failsafe_request_get(empty_data_folder):
|
||||
specter_mock = Specter(data_folder=empty_data_folder, checker_threads=False)
|
||||
requests_session = specter_mock.requests_session()
|
||||
|
||||
# Test 1: Mock 404 error for invalid currency
|
||||
currency = "notExisting"
|
||||
url = "https://www.bitstamp.net/api/v2/ticker/btc{}".format(currency)
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, url)
|
||||
assert (
|
||||
f"HttpError 404 for https://www.bitstamp.net/api/v2/ticker/btcnotExisting"
|
||||
in str(se.value)
|
||||
|
||||
mock_404_response = Mock()
|
||||
mock_404_response.status_code = 404
|
||||
mock_404_response.raise_for_status.side_effect = requests.exceptions.HTTPError(
|
||||
response=Mock(status_code=404)
|
||||
)
|
||||
mock_404_response.json.return_value = {"error": "Not found"}
|
||||
|
||||
with patch.object(requests_session, 'get', return_value=mock_404_response):
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, url)
|
||||
assert (
|
||||
f"HttpError 404 for https://www.bitstamp.net/api/v2/ticker/btcnotExisting"
|
||||
in str(se.value)
|
||||
)
|
||||
|
||||
# Test 2: Mock JSON error response (response with "errors" key)
|
||||
currency = "usd"
|
||||
# timestamp most probably in the future
|
||||
url = "https://www.bitstamp.net/api/v2/ohlc/btc{}/?limit=A&step=86400&start={}".format(
|
||||
currency, 6275453759
|
||||
)
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, url)
|
||||
assert str(se.value).startswith("JSON error:")
|
||||
|
||||
mock_json_error_response = Mock()
|
||||
mock_json_error_response.status_code = 200
|
||||
mock_json_error_response.json.return_value = {"errors": ["Invalid parameters"]}
|
||||
|
||||
with patch.object(requests_session, 'get', return_value=mock_json_error_response):
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, url)
|
||||
assert str(se.value).startswith("JSON error:")
|
||||
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, "https://httpbin.org/status/404")
|
||||
# Also allow for Gateway Timeout (504 error), mainly for the CI
|
||||
assert f"HttpError 404 for https://httpbin.org/status/404" in str(
|
||||
se.value
|
||||
) or f"HttpError 504 for https://httpbin.org/status/404" in str(se.value)
|
||||
# Test 3: Mock 404 error for httpbin
|
||||
mock_httpbin_404 = Mock()
|
||||
mock_httpbin_404.status_code = 404
|
||||
mock_httpbin_404.raise_for_status.side_effect = requests.exceptions.HTTPError(
|
||||
response=Mock(status_code=404)
|
||||
)
|
||||
mock_httpbin_404.json.return_value = {}
|
||||
|
||||
with patch.object(requests_session, 'get', return_value=mock_httpbin_404):
|
||||
with pytest.raises(SpecterError) as se:
|
||||
failsafe_request_get(requests_session, "https://httpbin.org/status/404")
|
||||
# Also allow for Gateway Timeout (504 error), mainly for the CI
|
||||
assert f"HttpError 404 for https://httpbin.org/status/404" in str(
|
||||
se.value
|
||||
) or f"HttpError 504 for https://httpbin.org/status/404" in str(se.value)
|
||||
|
||||
json = failsafe_request_get(requests_session, "https://httpbin.org/json")
|
||||
assert json["slideshow"]
|
||||
# Test 4: Mock successful JSON response
|
||||
mock_success_response = Mock()
|
||||
mock_success_response.status_code = 200
|
||||
mock_success_response.json.return_value = {
|
||||
"slideshow": {
|
||||
"author": "Yours Truly",
|
||||
"date": "date of publication",
|
||||
"slides": [{"title": "Wake up to WonderWidgets!", "type": "all"}],
|
||||
"title": "Sample Slide Show",
|
||||
}
|
||||
}
|
||||
|
||||
with patch.object(requests_session, 'get', return_value=mock_success_response):
|
||||
json = failsafe_request_get(requests_session, "https://httpbin.org/json")
|
||||
assert json["slideshow"]
|
||||
|
||||
|
||||
def test_get_price_at():
|
||||
specter_mock = MagicMock()
|
||||
# specter_mock = Specter()
|
||||
specter_mock.requests_session.return_value = requests.Session()
|
||||
specter_mock.price_check = True
|
||||
|
||||
# Mock responses for different providers
|
||||
def mock_get(url):
|
||||
response = Mock()
|
||||
response.status_code = 200
|
||||
|
||||
# Bitstamp ticker endpoint
|
||||
if "bitstamp.net/api/v2/ticker" in url:
|
||||
response.json.return_value = {"last": "45000.50"}
|
||||
# Coindesk currentprice endpoint
|
||||
elif "api.coindesk.com/v1/bpi/currentprice" in url:
|
||||
currency_code = url.split("/")[-1].replace(".json", "")
|
||||
response.json.return_value = {
|
||||
"bpi": {
|
||||
currency_code: {"rate_float": 45000.50}
|
||||
}
|
||||
}
|
||||
else:
|
||||
response.json.return_value = {}
|
||||
|
||||
return response
|
||||
|
||||
# Test with bitstamp_eur
|
||||
specter_mock.price_provider = "bitstamp_eur"
|
||||
specter_mock.price_provider = "bitstamp_eur"
|
||||
# returns a tuple like (True, price, currency_symbol)
|
||||
mytuple = get_price_at(specter_mock)
|
||||
assert float(mytuple[0])
|
||||
assert mytuple[1] == "€"
|
||||
with patch('requests.Session.get', side_effect=mock_get):
|
||||
mytuple = get_price_at(specter_mock)
|
||||
assert float(mytuple[0])
|
||||
assert mytuple[1] == "€"
|
||||
|
||||
# Now test them all for bitstamp
|
||||
# https://www.bitstamp.net/api/#ticker
|
||||
# Now test them all for bitstamp and coindesk (skip spotbit as it requires Tor)
|
||||
for currency in currency_mapping.keys():
|
||||
for exchange in currency_mapping[currency]["support"]:
|
||||
if exchange.startswith("spotbit_"):
|
||||
# ignore for now
|
||||
continue
|
||||
specter_mock.price_provider = f"{exchange}_{currency}"
|
||||
mytuple = get_price_at(specter_mock)
|
||||
assert float(mytuple[0])
|
||||
assert mytuple[1] == currency_mapping[currency]["symbol"]
|
||||
with patch('requests.Session.get', side_effect=mock_get):
|
||||
mytuple = get_price_at(specter_mock)
|
||||
assert float(mytuple[0])
|
||||
assert mytuple[1] == currency_mapping[currency]["symbol"]
|
||||
|
||||
|
||||
def test_get_price_at_historic():
|
||||
specter_mock = MagicMock()
|
||||
specter_mock.requests_session.return_value = requests.Session()
|
||||
# historic-prices:
|
||||
|
||||
specter_mock.price_check = True
|
||||
|
||||
# Mock response for historic prices
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"data": {
|
||||
"ohlc": [
|
||||
{
|
||||
"close": "62000.00",
|
||||
"timestamp": "1636551359"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
specter_mock.price_provider = f"bitstamp_usd"
|
||||
mytuple = get_price_at(specter_mock, 1636551359)
|
||||
assert float(mytuple[0]) > 60000
|
||||
with patch('requests.Session.get', return_value=mock_response):
|
||||
mytuple = get_price_at(specter_mock, 1636551359)
|
||||
assert float(mytuple[0]) > 60000
|
||||
|
||||
|
||||
def test_get_price_at_errorhandling():
|
||||
specter_mock = MagicMock()
|
||||
specter_mock.requests_session.return_value = requests.Session()
|
||||
specter_mock.price_check = True
|
||||
# some unsupported stuff:
|
||||
|
||||
specter_mock.price_provider = f"bitstamp_ils"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue