mirror of
https://gitlab.com/simple-stock-bots/simple-stock-bot.git
synced 2026-06-03 21:00:26 +00:00
Add pytest test suite for core functionality
- Add pytest and pytest-asyncio to dev-reqs.txt - Add tests for Router symbol detection regex (stocks and crypto patterns) - Add tests for Symbol, Stock, and Coin class construction - Add tests for rate_limited decorator functionality - Add conftest.py with path setup and test environment variables Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"""Test the Router symbol detection functionality."""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
from common.symbol_router import Router
|
||||
from common.Symbol import Stock, Coin
|
||||
|
||||
|
||||
class TestRouter:
|
||||
"""Test Router symbol detection and parsing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
# Mock the MarketData and cg_Crypto dependencies
|
||||
with patch("common.symbol_router.MarketData") as mock_md, patch("common.symbol_router.cg_Crypto") as mock_crypto:
|
||||
# Mock MarketData
|
||||
mock_md_instance = Mock()
|
||||
mock_md.return_value = mock_md_instance
|
||||
|
||||
# Mock cg_Crypto
|
||||
mock_crypto_instance = Mock()
|
||||
mock_crypto.return_value = mock_crypto_instance
|
||||
|
||||
# Create mock symbol list for crypto
|
||||
import pandas as pd
|
||||
|
||||
mock_crypto_instance.symbol_list = pd.DataFrame(
|
||||
{
|
||||
"symbol": ["btc", "eth", "ada"],
|
||||
"id": ["bitcoin", "ethereum", "cardano"],
|
||||
"name": ["Bitcoin", "Ethereum", "Cardano"],
|
||||
}
|
||||
)
|
||||
|
||||
self.router = Router()
|
||||
self.mock_stock = mock_md_instance
|
||||
self.mock_crypto = mock_crypto_instance
|
||||
|
||||
def test_stock_symbol_regex(self):
|
||||
"""Test that stock symbol regex correctly identifies patterns."""
|
||||
import re
|
||||
|
||||
# Test the regex pattern directly
|
||||
pattern = self.router.STOCK_REGEX
|
||||
|
||||
# Should match
|
||||
assert re.search(pattern, "$AAPL")
|
||||
assert re.search(pattern, "What about $TSLA today?")
|
||||
assert re.search(pattern, "Look at $NVDA and $AMD")
|
||||
|
||||
# Should not match crypto patterns
|
||||
assert not re.search(pattern, "$$BTC")
|
||||
|
||||
# Should not match without $
|
||||
assert not re.search(pattern, "AAPL")
|
||||
|
||||
def test_crypto_symbol_regex(self):
|
||||
"""Test that crypto symbol regex correctly identifies patterns."""
|
||||
import re
|
||||
|
||||
pattern = self.router.CRYPTO_REGEX
|
||||
|
||||
# Should match
|
||||
assert re.search(pattern, "$$BTC")
|
||||
assert re.search(pattern, "How is $$ETH doing?")
|
||||
assert re.search(pattern, "$$BTC and $$ETH")
|
||||
|
||||
# Should not match single $ patterns
|
||||
assert not re.search(pattern, "$BTC")
|
||||
assert not re.search(pattern, "$AAPL")
|
||||
|
||||
def test_find_symbols_stock(self):
|
||||
"""Test stock symbol detection."""
|
||||
# Mock successful stock lookup
|
||||
self.mock_stock.symbol_id.return_value = {"ticker": "AAPL", "title": "Apple Inc.", "mkt_cap_rank": 1}
|
||||
|
||||
result = self.router.find_symbols("$AAPL")
|
||||
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], Stock)
|
||||
self.mock_stock.symbol_id.assert_called_with("AAPL")
|
||||
|
||||
def test_find_symbols_crypto(self):
|
||||
"""Test crypto symbol detection."""
|
||||
# Mock crypto dataframe lookup
|
||||
import pandas as pd
|
||||
|
||||
mock_crypto_df = pd.DataFrame({"symbol": ["btc"], "id": ["bitcoin"], "name": ["Bitcoin"], "type_id": ["$$btc"]})
|
||||
|
||||
# Set up the mock to return the DataFrame when filtered
|
||||
self.mock_crypto.symbol_list = pd.DataFrame(
|
||||
{
|
||||
"symbol": ["btc", "eth"],
|
||||
"id": ["bitcoin", "ethereum"],
|
||||
"name": ["Bitcoin", "Ethereum"],
|
||||
"type_id": ["$$btc", "$$eth"],
|
||||
}
|
||||
)
|
||||
|
||||
# Mock the pandas filtering
|
||||
with patch.object(self.mock_crypto.symbol_list, "str") as mock_str:
|
||||
mock_str.fullmatch.return_value = mock_crypto_df
|
||||
mock_crypto_df.empty = False
|
||||
|
||||
result = self.router.find_symbols("$$BTC")
|
||||
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], Coin)
|
||||
|
||||
def test_find_symbols_no_matches(self):
|
||||
"""Test behavior when no symbols are found."""
|
||||
result = self.router.find_symbols("Hello world")
|
||||
assert result == []
|
||||
|
||||
def test_find_symbols_invalid_stock(self):
|
||||
"""Test behavior when stock symbol is not found."""
|
||||
# Mock failed stock lookup
|
||||
self.mock_stock.symbol_id.return_value = None
|
||||
|
||||
result = self.router.find_symbols("$INVALID")
|
||||
|
||||
assert result == []
|
||||
self.mock_stock.symbol_id.assert_called_with("INVALID")
|
||||
|
||||
def test_find_symbols_multiple(self):
|
||||
"""Test finding multiple symbols."""
|
||||
# Mock successful lookups for both
|
||||
self.mock_stock.symbol_id.side_effect = [
|
||||
{"ticker": "AAPL", "title": "Apple Inc.", "mkt_cap_rank": 1},
|
||||
{"ticker": "TSLA", "title": "Tesla Inc.", "mkt_cap_rank": 5},
|
||||
]
|
||||
|
||||
result = self.router.find_symbols("$AAPL and $TSLA")
|
||||
|
||||
assert len(result) == 2
|
||||
assert all(isinstance(symbol, Stock) for symbol in result)
|
||||
|
||||
def test_trending_weight_tracking(self):
|
||||
"""Test that trending counts are updated."""
|
||||
# Mock successful stock lookup
|
||||
self.mock_stock.symbol_id.return_value = {"ticker": "AAPL", "title": "Apple Inc.", "mkt_cap_rank": 1}
|
||||
|
||||
# Clear trending count
|
||||
self.router.trending_count = {}
|
||||
|
||||
result = self.router.find_symbols("$AAPL", trending_weight=2)
|
||||
|
||||
assert len(result) == 1
|
||||
assert "$AAPL" in self.router.trending_count
|
||||
assert self.router.trending_count["$AAPL"] == 2
|
||||
Reference in New Issue
Block a user