114 lines
3.4 KiB
Python
114 lines
3.4 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
from typing import Optional
|
|
|
|
from tools import market
|
|
|
|
|
|
class MarketTests(unittest.TestCase):
|
|
def test_get_ratios_short_circuits_after_first_provider_with_ratios(self) -> None:
|
|
calls: list[str] = []
|
|
|
|
def yahoo(_ticker: str):
|
|
calls.append("yahoo")
|
|
return {
|
|
"provider": "yahoo",
|
|
"price": 100.0,
|
|
"pe": 25.0,
|
|
"dividend_yield_pct": 1.2,
|
|
"beta": 1.1,
|
|
}
|
|
|
|
def finviz(_ticker: str):
|
|
calls.append("finviz")
|
|
return {
|
|
"provider": "finviz",
|
|
"price": 100.0,
|
|
"pe": 18.0,
|
|
"dividend_yield_pct": 2.0,
|
|
"beta": 0.9,
|
|
}
|
|
|
|
def stooq(_ticker: str):
|
|
calls.append("stooq")
|
|
return {
|
|
"provider": "stooq",
|
|
"price": 100.0,
|
|
"pe": None,
|
|
"dividend_yield_pct": None,
|
|
"beta": None,
|
|
}
|
|
|
|
with patch("tools.market._fetch_yahoo", yahoo), patch(
|
|
"tools.market._fetch_finviz", finviz
|
|
), patch("tools.market._fetch_stooq", stooq):
|
|
result = market.get_ratios("AAPL")
|
|
|
|
self.assertEqual(result["provider"], "yahoo")
|
|
self.assertEqual(calls, ["yahoo"])
|
|
|
|
def test_get_ratios_uses_second_provider_when_first_has_no_ratios(self) -> None:
|
|
calls: list[str] = []
|
|
|
|
def yahoo(_ticker: str):
|
|
calls.append("yahoo")
|
|
return {
|
|
"provider": "yahoo",
|
|
"price": 100.0,
|
|
"pe": None,
|
|
"dividend_yield_pct": None,
|
|
"beta": None,
|
|
}
|
|
|
|
def finviz(_ticker: str):
|
|
calls.append("finviz")
|
|
return {
|
|
"provider": "finviz",
|
|
"price": 100.0,
|
|
"pe": 18.0,
|
|
"dividend_yield_pct": 2.0,
|
|
"beta": 0.9,
|
|
}
|
|
|
|
def stooq(_ticker: str):
|
|
calls.append("stooq")
|
|
return None
|
|
|
|
with patch("tools.market._fetch_yahoo", yahoo), patch(
|
|
"tools.market._fetch_finviz", finviz
|
|
), patch("tools.market._fetch_stooq", stooq):
|
|
result = market.get_ratios("AAPL")
|
|
|
|
self.assertEqual(result["provider"], "finviz")
|
|
self.assertEqual(calls, ["yahoo", "finviz"])
|
|
|
|
def test_http_get_json_retries_then_succeeds(self) -> None:
|
|
class FakeResponse:
|
|
def __init__(self, status_code: int, payload: Optional[dict] = None) -> None:
|
|
self.status_code = status_code
|
|
self._payload = payload or {}
|
|
|
|
def raise_for_status(self) -> None:
|
|
if self.status_code >= 400:
|
|
raise market.requests.HTTPError(response=self)
|
|
|
|
def json(self) -> dict:
|
|
return self._payload
|
|
|
|
with patch("tools.market.requests.get") as get_mock, patch(
|
|
"tools.market.time.sleep"
|
|
) as sleep_mock:
|
|
get_mock.side_effect = [
|
|
FakeResponse(503),
|
|
FakeResponse(200, {"ok": True}),
|
|
]
|
|
payload = market._http_get_json("https://example.com")
|
|
|
|
self.assertEqual(payload, {"ok": True})
|
|
self.assertEqual(get_mock.call_count, 2)
|
|
sleep_mock.assert_called_once()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|