test_peer_comparison.pyβ’15.1 kB
"""
Test cases for get_peer_comparison tool (TDD Red Phase)
"""
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from typing import Dict, Any
# Mock the modules before importing server
import sys
from unittest.mock import MagicMock
sys.modules['src.tools.peer_comparison_tools'] = MagicMock()
from src.server import MCPStockDetailsServer
from src.exceptions import MCPStockDetailsError, InsufficientDataError
class TestPeerComparison:
"""Test cases for peer comparison functionality"""
@pytest.fixture
def server_with_peer_comparison(self):
"""Create server instance with peer comparison capability"""
server = MCPStockDetailsServer()
server.peer_comparison_analyzer = AsyncMock()
return server
@pytest.fixture
def sample_peer_data(self):
"""Sample peer comparison data for testing"""
return {
"005930": { # Samsung Electronics
"company_info": {
"name": "μΌμ±μ μ",
"market_cap": 450000000000000,
"industry": "λ°λ체",
"sector": "μ 보기μ "
},
"peer_companies": [
{
"company_code": "000660",
"company_name": "SKνμ΄λμ€",
"market_cap": 95000000000000,
"similarity_score": 0.85
},
{
"company_code": "035420",
"company_name": "NAVER",
"market_cap": 45000000000000,
"similarity_score": 0.65
}
],
"peer_metrics": {
"revenue_2023": {
"company": 302231000000000,
"peer_avg": 180500000000000,
"peer_median": 165000000000000,
"percentile": 95
},
"operating_margin": {
"company": 15.2,
"peer_avg": 18.5,
"peer_median": 17.8,
"percentile": 35
},
"roe": {
"company": 12.8,
"peer_avg": 14.2,
"peer_median": 13.5,
"percentile": 42
},
"per": {
"company": 18.5,
"peer_avg": 22.3,
"peer_median": 21.2,
"percentile": 25
},
"pbr": {
"company": 1.85,
"peer_avg": 2.15,
"peer_median": 2.05,
"percentile": 28
}
}
}
}
@pytest.mark.asyncio
async def test_get_peer_comparison_tool_registration(self, server_with_peer_comparison):
"""Test peer comparison tool is properly registered"""
tools = await server_with_peer_comparison.list_tools()
peer_comparison_tool = None
for tool in tools:
if tool.name == "get_peer_comparison":
peer_comparison_tool = tool
break
assert peer_comparison_tool is not None
assert peer_comparison_tool.description == "λμ’
μ
κ³ κ²½μμ¬ λΉκ΅ λΆμ"
# Check required parameters
properties = peer_comparison_tool.inputSchema["properties"]
assert "company_code" in properties
assert properties["company_code"]["type"] == "string"
@pytest.mark.asyncio
async def test_peer_comparison_analysis(self, server_with_peer_comparison, sample_peer_data):
"""Test basic peer comparison analysis"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'get_peer_data') as mock_data:
mock_data.return_value = sample_peer_data[company_code]
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_financial_comparison": True
})
assert result is not None
content = result[0].text
# Should include peer comparison information
assert "PEER COMPARISON" in content
assert "μΌμ±μ μ" in content
assert "SKνμ΄λμ€" in content
assert "Market Cap" in content
@pytest.mark.asyncio
async def test_financial_metrics_comparison(self, server_with_peer_comparison, sample_peer_data):
"""Test financial metrics comparison"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'get_peer_data') as mock_data:
mock_data.return_value = sample_peer_data[company_code]
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_financial_comparison": True,
"include_valuation_comparison": True
})
assert result is not None
content = result[0].text
# Should include financial metrics
assert "FINANCIAL COMPARISON" in content
assert "Operating Margin" in content
assert "ROE" in content
assert "Percentile" in content
@pytest.mark.asyncio
async def test_valuation_comparison(self, server_with_peer_comparison, sample_peer_data):
"""Test valuation metrics comparison"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'compare_valuation_metrics') as mock_valuation:
mock_valuation.return_value = {
"valuation_comparison": {
"per_comparison": {
"company_per": 18.5,
"peer_avg_per": 22.3,
"relative_valuation": "undervalued",
"discount_premium": -17.0
},
"pbr_comparison": {
"company_pbr": 1.85,
"peer_avg_pbr": 2.15,
"relative_valuation": "undervalued",
"discount_premium": -14.0
}
},
"valuation_insights": [
"Company trades at discount to peers",
"Lower PER suggests potential value opportunity"
]
}
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_valuation_comparison": True
})
assert result is not None
content = result[0].text
# Should include valuation comparison
assert "VALUATION" in content
assert "PER" in content or "P/E" in content
assert "undervalued" in content or "Undervalued" in content
@pytest.mark.asyncio
async def test_market_position_analysis(self, server_with_peer_comparison, sample_peer_data):
"""Test market position analysis"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'analyze_market_position') as mock_position:
mock_position.return_value = {
"market_position": {
"market_share_rank": 1,
"revenue_rank": 1,
"profitability_rank": 3,
"efficiency_rank": 2
},
"competitive_advantages": [
"Market leadership in semiconductors",
"Strong R&D capabilities",
"Global manufacturing scale"
],
"competitive_weaknesses": [
"Lower operating margins vs peers",
"Cyclical business exposure"
]
}
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_market_position": True
})
assert result is not None
content = result[0].text
# Should include market position
assert "MARKET POSITION" in content or "COMPETITIVE" in content
assert "Rank" in content
assert "Market leadership" in content
@pytest.mark.asyncio
async def test_growth_comparison(self, server_with_peer_comparison, sample_peer_data):
"""Test growth metrics comparison"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'compare_growth_metrics') as mock_growth:
mock_growth.return_value = {
"growth_comparison": {
"revenue_growth_3y": {
"company": 8.5,
"peer_avg": 12.3,
"peer_median": 11.8,
"percentile": 25
},
"profit_growth_3y": {
"company": 15.2,
"peer_avg": 18.7,
"peer_median": 17.5,
"percentile": 35
}
},
"growth_insights": [
"Revenue growth below peer average",
"Profit growth recovering strongly"
]
}
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_growth_comparison": True
})
assert result is not None
content = result[0].text
# Should include growth comparison
assert "GROWTH" in content
assert "Revenue Growth" in content
assert "Profit Growth" in content
@pytest.mark.asyncio
async def test_peer_selection_criteria(self, server_with_peer_comparison, sample_peer_data):
"""Test peer selection with different criteria"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'select_peer_companies') as mock_selection:
mock_selection.return_value = {
"selected_peers": [
{
"company_code": "000660",
"company_name": "SKνμ΄λμ€",
"selection_reason": "Same industry - semiconductors",
"similarity_score": 0.85
}
],
"selection_criteria": {
"industry_match": True,
"size_similarity": True,
"business_model_similarity": True,
"market_segment_overlap": True
}
}
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"peer_selection_method": "industry_focused",
"max_peers": 5
})
assert result is not None
content = result[0].text
# Should include peer selection information
assert "PEER SELECTION" in content or "Selected Peers" in content
assert "SKνμ΄λμ€" in content
assert "similarity" in content.lower()
@pytest.mark.asyncio
async def test_comprehensive_peer_report(self, server_with_peer_comparison, sample_peer_data):
"""Test comprehensive peer comparison report"""
company_code = "005930"
with patch.object(server_with_peer_comparison.peer_comparison_analyzer, 'comprehensive_peer_analysis') as mock_comprehensive:
mock_comprehensive.return_value = {
"peer_overview": {
"total_peers_analyzed": 8,
"primary_peers": 3,
"industry_coverage": "95%"
},
"relative_performance": {
"overall_rank": 2,
"strengths": ["Market share", "Technology leadership"],
"weaknesses": ["Profitability", "Valuation premium"]
},
"strategic_insights": [
"Company leads in market share but lags in margins",
"Valuation discount presents opportunity"
]
}
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"analysis_type": "comprehensive"
})
assert result is not None
content = result[0].text
# Should include comprehensive analysis
assert "COMPREHENSIVE" in content
assert "Strategic Insights" in content or "STRATEGIC" in content
assert "Overall Rank" in content or "Performance" in content
@pytest.mark.asyncio
async def test_sector_benchmark_analysis(self, server_with_peer_comparison, sample_peer_data):
"""Test sector benchmark analysis"""
company_code = "005930"
result = await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": company_code,
"include_sector_benchmark": True,
"benchmark_metrics": ["roe", "roa", "operating_margin"]
})
assert result is not None
content = result[0].text
# Should include sector benchmark
assert "SECTOR" in content or "BENCHMARK" in content
assert "ROE" in content or "Operating Margin" in content
@pytest.mark.asyncio
async def test_peer_comparison_error_handling(self, server_with_peer_comparison):
"""Test error handling for peer comparison"""
# Test invalid company code
with pytest.raises(MCPStockDetailsError):
await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": "",
"include_financial_comparison": True
})
# Test insufficient peer data
with pytest.raises(InsufficientDataError):
await server_with_peer_comparison._handle_get_peer_comparison({
"company_code": "INVALID",
"include_financial_comparison": True
})