Skip to main content
Glama

RL-MCP

by rlefko
test_market_service.pyโ€ข14.6 kB
from datetime import datetime, timezone from unittest.mock import AsyncMock, Mock, patch import feedparser import pytest from app.api.v1.stock.models_stock import ( MarketSummary, StockAnalysis, StockNews, StockPrice, ) from app.api.v1.stock.services.market_service import MarketDataService, market_service class TestMarketDataService: """Test suite for MarketDataService""" @pytest.fixture def market_service_instance(self): """Create a fresh MarketDataService instance for testing""" return MarketDataService() @pytest.mark.asyncio async def test_analyze_sentiment_positive(self, market_service_instance): """Test sentiment analysis for positive text""" positive_text = ( "Great earnings, strong growth, excellent performance, bullish outlook" ) sentiment = await market_service_instance._analyze_sentiment(positive_text) assert sentiment > 0 assert -1.0 <= sentiment <= 1.0 @pytest.mark.asyncio async def test_analyze_sentiment_negative(self, market_service_instance): """Test sentiment analysis for negative text""" negative_text = ( "Bad earnings, terrible performance, bearish outlook, stock crash" ) sentiment = await market_service_instance._analyze_sentiment(negative_text) assert sentiment < 0 assert -1.0 <= sentiment <= 1.0 def test_extract_symbols(self, market_service_instance): """Test stock symbol extraction from text""" text = "AAPL and MSFT are performing well, but GOOGL is down. The market is volatile." symbols = market_service_instance._extract_symbols(text) assert "AAPL" in symbols assert "MSFT" in symbols assert "GOOGL" in symbols # Common words should be filtered out assert "THE" not in symbols assert "AND" not in symbols assert "IS" not in symbols @pytest.mark.asyncio async def test_get_stock_price_success(self, market_service_instance): """Test successful stock price retrieval with yahooquery""" mock_price_data = { "AAPL": { "regularMarketPrice": 150.25, "regularMarketPreviousClose": 148.50, "regularMarketVolume": 50000000, "marketCap": 2500000000000, "trailingPE": 25.5, } } with patch("app.api.v1.stock.services.market_service.Ticker") as mock_ticker: mock_ticker.return_value.price = mock_price_data result = await market_service_instance.get_stock_price("AAPL") assert result is not None assert result.symbol == "AAPL" assert result.price == 150.25 assert result.change == 1.75 # 150.25 - 148.50 assert result.change_percent == pytest.approx(1.18, rel=1e-2) assert result.volume == 50000000 @pytest.mark.asyncio async def test_get_stock_price_not_found(self, market_service_instance): """Test stock price retrieval for invalid symbol""" with patch("app.api.v1.stock.services.market_service.Ticker") as mock_ticker: mock_ticker.return_value.price = {} result = await market_service_instance.get_stock_price("INVALID") assert result is None @pytest.mark.asyncio async def test_get_stock_news_success(self, market_service_instance): """Test successful news retrieval""" mock_feed_data = Mock() mock_feed_data.entries = [ Mock( title="Apple Reports Strong Earnings", summary="Apple Inc. announced record quarterly earnings with AAPL stock rising.", link="https://example.com/news1", published_parsed=(2024, 1, 15, 10, 30, 0, 0, 15, 0), get=Mock( side_effect=lambda key, default=None: { "title": "Apple Reports Strong Earnings", "summary": "Apple Inc. announced record quarterly earnings with AAPL stock rising.", "link": "https://example.com/news1", }.get(key, default) ), ), Mock( title="Tesla Innovation Update", summary="Tesla unveils new technology for TSLA vehicles.", link="https://example.com/news2", published_parsed=(2024, 1, 15, 9, 0, 0, 0, 15, 0), get=Mock( side_effect=lambda key, default=None: { "title": "Tesla Innovation Update", "summary": "Tesla unveils new technology for TSLA vehicles.", "link": "https://example.com/news2", }.get(key, default) ), ), ] mock_feed_data.feed = Mock() mock_feed_data.feed.get.return_value = "TechNews" with patch("feedparser.parse", return_value=mock_feed_data), patch.object( market_service_instance, "_get_cached_data", return_value=None ), patch.object(market_service_instance, "_cache_data"): result = await market_service_instance.get_stock_news(limit=5) assert len(result) > 0 assert all(isinstance(article, StockNews) for article in result) assert any("AAPL" in article.symbols for article in result) @pytest.mark.asyncio async def test_get_stock_news_with_symbol_filter(self, market_service_instance): """Test news retrieval with symbol filter""" mock_feed_data = Mock() mock_feed_data.entries = [ Mock( title="Apple Reports Strong Earnings", summary="Apple Inc. announced record quarterly earnings with AAPL stock rising.", link="https://example.com/news1", published_parsed=(2024, 1, 15, 10, 30, 0, 0, 15, 0), get=Mock( side_effect=lambda key, default=None: { "title": "Apple Reports Strong Earnings", "summary": "Apple Inc. announced record quarterly earnings with AAPL stock rising.", "link": "https://example.com/news1", }.get(key, default) ), ), ] mock_feed_data.feed = Mock() mock_feed_data.feed.get.return_value = "TechNews" with patch("feedparser.parse", return_value=mock_feed_data), patch.object( market_service_instance, "_get_cached_data", return_value=None ), patch.object(market_service_instance, "_cache_data"): result = await market_service_instance.get_stock_news("AAPL", limit=5) assert len(result) > 0 assert all("AAPL" in article.symbols for article in result) @pytest.mark.asyncio async def test_analyze_stock_comprehensive(self, market_service_instance, session): """Test comprehensive stock analysis""" mock_price_data = StockPrice( symbol="AAPL", price=150.25, change=2.50, change_percent=1.69, volume=50000000, market_cap=2500000000000, pe_ratio=25.5, timestamp=datetime.now(timezone.utc), ) mock_news = [ StockNews( title="Apple Strong Performance", summary="Apple shows excellent growth", url="https://example.com", source="TechNews", published_at=datetime.now(timezone.utc), symbols=["AAPL"], sentiment_score=0.8, relevance_score=0.9, ) ] with patch.object( market_service_instance, "get_stock_price", return_value=mock_price_data ), patch.object( market_service_instance, "get_stock_news", return_value=mock_news ), patch.object( market_service_instance, "_store_analysis" ) as mock_store: result = await market_service_instance.analyze_stock("AAPL", session) assert isinstance(result, StockAnalysis) assert result.symbol == "AAPL" assert result.recommendation in ["BUY", "HOLD", "SELL"] assert 0.0 <= result.confidence_score <= 1.0 assert result.risk_level in ["LOW", "MEDIUM", "HIGH"] mock_store.assert_called_once() @pytest.mark.asyncio async def test_get_market_summary(self, market_service_instance, session): """Test market summary generation""" mock_prices = { "AAPL": StockPrice( symbol="AAPL", price=150.25, change=5.50, change_percent=3.8, volume=50000000, timestamp=datetime.now(timezone.utc), ), "GOOGL": StockPrice( symbol="GOOGL", price=2800.75, change=-25.25, change_percent=-0.89, volume=15000000, timestamp=datetime.now(timezone.utc), ), "MSFT": StockPrice( symbol="MSFT", price=350.50, change=8.25, change_percent=2.41, volume=30000000, timestamp=datetime.now(timezone.utc), ), } def mock_get_price(symbol): return mock_prices.get(symbol) mock_news = [ StockNews( title="Market Update", summary="Market shows mixed signals", url="https://example.com", source="MarketNews", published_at=datetime.now(timezone.utc), symbols=["AAPL", "GOOGL"], sentiment_score=0.2, relevance_score=0.7, ) ] with patch.object( market_service_instance, "get_stock_price", side_effect=mock_get_price ), patch.object( market_service_instance, "get_stock_news", return_value=mock_news ), patch.object( market_service_instance, "_get_cached_data", return_value=None ), patch.object( market_service_instance, "_cache_data" ): result = await market_service_instance.get_market_summary(session) assert isinstance(result, MarketSummary) assert result.total_symbols > 0 assert len(result.top_gainers) <= 3 assert len(result.top_losers) <= 3 assert -1.0 <= result.market_sentiment <= 1.0 @pytest.mark.asyncio async def test_calculate_relevance(self, market_service_instance): """Test relevance calculation for news articles""" # Test exact symbol match relevance = await market_service_instance._calculate_relevance( "AAPL stock rises", "Apple stock performance", "AAPL" ) assert relevance == 1.0 # Test company name match relevance = await market_service_instance._calculate_relevance( "Apple reports earnings", "Apple Inc. quarterly results", "AAPL" ) assert relevance == 0.8 # Test no match relevance = await market_service_instance._calculate_relevance( "Tesla news", "Tesla updates", "AAPL" ) assert relevance == 0.3 @pytest.mark.asyncio async def test_generate_recommendation(self, market_service_instance): """Test recommendation generation logic""" # Test BUY recommendation price_data = StockPrice( symbol="AAPL", price=150.0, change=5.0, change_percent=3.5, pe_ratio=18.0, volume=1000000, timestamp=datetime.now(timezone.utc), ) recommendation = await market_service_instance._generate_recommendation( price_data, 0.5 ) assert recommendation == "BUY" # Test SELL recommendation price_data = StockPrice( symbol="AAPL", price=150.0, change=-8.0, change_percent=-5.0, pe_ratio=35.0, volume=1000000, timestamp=datetime.now(timezone.utc), ) recommendation = await market_service_instance._generate_recommendation( price_data, -0.5 ) assert recommendation == "SELL" @pytest.mark.asyncio async def test_assess_risk(self, market_service_instance): """Test risk assessment logic""" # Test HIGH risk price_data = StockPrice( symbol="AAPL", price=150.0, change=20.0, change_percent=15.0, pe_ratio=50.0, volume=1000000, timestamp=datetime.now(timezone.utc), ) risk = await market_service_instance._assess_risk(price_data, -0.5) assert risk == "HIGH" # Test LOW risk price_data = StockPrice( symbol="AAPL", price=150.0, change=1.0, change_percent=0.7, pe_ratio=20.0, volume=1000000, timestamp=datetime.now(timezone.utc), ) risk = await market_service_instance._assess_risk(price_data, 0.2) assert risk == "LOW" @pytest.mark.asyncio async def test_caching_functionality(self, market_service_instance): """Test that caching works properly""" cache_key = "test_price_AAPL" # Mock the cache methods with patch.object( market_service_instance, "_get_cached_data", return_value=None ) as mock_get_cache, patch.object( market_service_instance, "_cache_data" ) as mock_cache_data, patch( "app.api.v1.stock.services.market_service.Ticker" ) as mock_ticker: mock_price_data = { "AAPL": { "regularMarketPrice": 150.25, "regularMarketPreviousClose": 148.50, "regularMarketVolume": 50000000, } } mock_ticker.return_value.price = mock_price_data result = await market_service_instance.get_stock_price( "AAPL", use_cache=True ) # Verify cache was checked and data was cached mock_get_cache.assert_called_once() mock_cache_data.assert_called_once() assert result is not None

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rlefko/rl-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server