test_advanced_company_tools.pyβ’15.9 kB
"""
Unit tests for advanced company analysis tools
TDD Red Phase: Write failing tests for enhanced functionality
"""
import pytest
import asyncio
from unittest.mock import Mock, AsyncMock, patch
from datetime import datetime, date
from typing import Dict, Any, List
# Test imports will fail initially - this is expected in TDD Red phase
try:
from src.tools.company_tools import CompanyAnalyzer
from src.models.company import CompanyOverview, FinancialData, BusinessSegment, ShareholderInfo
from src.exceptions import MCPStockDetailsError, CompanyNotFoundError, InsufficientDataError
from src.utils.financial_calculator import FinancialCalculator
except ImportError:
# Expected in Red phase - we haven't implemented these yet
pass
class TestCompanyAnalyzer:
"""Test cases for enhanced company analysis functionality"""
@pytest.fixture
def company_analyzer(self):
"""Create company analyzer instance for testing"""
return CompanyAnalyzer()
@pytest.fixture
def sample_company_data(self):
"""Sample company data for testing"""
return {
"company_code": "005930",
"company_name": "μΌμ±μ μ",
"industry_code": "26211",
"industry_name": "λ°λ체 μ μ‘°μ
",
"market_cap": 400_000_000_000_000, # 400 trillion KRW
"shares_outstanding": 5_969_782_550,
"establishment_date": "1969-01-13",
"listing_date": "1975-06-11",
"ceo_name": "μ΄μ¬μ©",
"headquarters": "κ²½κΈ°λ μμμ μν΅κ΅¬",
"employee_count": 267_937,
"main_products": ["λ°λ체", "μ€λ§νΈν°", "λμ€νλ μ΄", "κ°μ μ ν"]
}
@pytest.fixture
def sample_financial_data(self):
"""Sample multi-year financial data"""
return [
{
"year": 2023,
"revenue": 258_774_000_000_000,
"operating_profit": 22_034_000_000_000,
"net_profit": 15_349_000_000_000,
"total_assets": 426_071_000_000_000,
"total_equity": 319_167_000_000_000,
"total_debt": 106_904_000_000_000,
"operating_margin": 8.5,
"net_margin": 5.9,
"roe": 4.8,
"roa": 3.6
},
{
"year": 2022,
"revenue": 302_231_000_000_000,
"operating_profit": 43_376_000_000_000,
"net_profit": 28_095_000_000_000,
"total_assets": 399_653_000_000_000,
"total_equity": 295_321_000_000_000,
"total_debt": 104_332_000_000_000,
"operating_margin": 14.4,
"net_margin": 9.3,
"roe": 9.5,
"roa": 7.0
}
]
@pytest.mark.asyncio
async def test_get_enhanced_company_overview(self, company_analyzer, sample_company_data):
"""Test enhanced company overview with detailed information"""
company_code = "005930"
enhanced_overview = await company_analyzer.get_enhanced_company_overview(
company_code=company_code,
include_subsidiaries=True,
include_recent_news=True,
include_financial_highlights=True
)
assert enhanced_overview is not None
assert enhanced_overview["company_code"] == company_code
assert "basic_info" in enhanced_overview
assert "financial_highlights" in enhanced_overview
assert "business_segments" in enhanced_overview
assert "subsidiaries" in enhanced_overview
assert "recent_news" in enhanced_overview
assert "key_metrics" in enhanced_overview
# Test financial highlights
highlights = enhanced_overview["financial_highlights"]
assert "revenue" in highlights
assert "market_cap" in highlights
assert "pe_ratio" in highlights
assert "dividend_yield" in highlights
@pytest.mark.asyncio
async def test_calculate_financial_ratios(self, company_analyzer, sample_financial_data):
"""Test financial ratio calculations"""
company_code = "005930"
ratios = await company_analyzer.calculate_financial_ratios(
company_code=company_code,
years=3
)
assert ratios is not None
assert "profitability" in ratios
assert "liquidity" in ratios
assert "leverage" in ratios
assert "efficiency" in ratios
assert "market" in ratios
# Test profitability ratios
profitability = ratios["profitability"]
assert "operating_margin" in profitability
assert "net_margin" in profitability
assert "roe" in profitability
assert "roa" in profitability
assert "roic" in profitability
# Validate ratio values are reasonable
assert 0 <= profitability["operating_margin"] <= 100
assert 0 <= profitability["net_margin"] <= 100
assert profitability["roe"] > 0
@pytest.mark.asyncio
async def test_get_business_segments(self, company_analyzer):
"""Test business segment analysis"""
company_code = "005930"
segments = await company_analyzer.get_business_segments(company_code)
assert segments is not None
assert isinstance(segments, list)
assert len(segments) > 0
# Check segment structure
segment = segments[0]
assert "segment_name" in segment
assert "revenue" in segment
assert "operating_profit" in segment
assert "revenue_ratio" in segment
assert "profit_margin" in segment
assert "yoy_growth" in segment
# Test that revenue ratios sum to approximately 100%
total_ratio = sum(s["revenue_ratio"] for s in segments)
assert 95 <= total_ratio <= 105 # Allow some rounding error
@pytest.mark.asyncio
async def test_get_shareholder_information(self, company_analyzer):
"""Test shareholder information retrieval"""
company_code = "005930"
shareholders = await company_analyzer.get_shareholder_info(company_code)
assert shareholders is not None
assert "major_shareholders" in shareholders
assert "ownership_structure" in shareholders
assert "share_distribution" in shareholders
# Test major shareholders
major_shareholders = shareholders["major_shareholders"]
assert isinstance(major_shareholders, list)
assert len(major_shareholders) > 0
shareholder = major_shareholders[0]
assert "shareholder_name" in shareholder
assert "share_count" in shareholder
assert "share_ratio" in shareholder
assert "shareholder_type" in shareholder
@pytest.mark.asyncio
async def test_analyze_financial_trends(self, company_analyzer, sample_financial_data):
"""Test financial trend analysis"""
company_code = "005930"
trends = await company_analyzer.analyze_financial_trends(
company_code=company_code,
years=5
)
assert trends is not None
assert "revenue_trend" in trends
assert "profit_trend" in trends
assert "margin_trend" in trends
assert "growth_rates" in trends
assert "trend_analysis" in trends
# Test growth rates
growth_rates = trends["growth_rates"]
assert "revenue_cagr" in growth_rates
assert "profit_cagr" in growth_rates
assert "asset_growth" in growth_rates
# Test trend analysis
analysis = trends["trend_analysis"]
assert "overall_trend" in analysis
assert "key_insights" in analysis
assert isinstance(analysis["key_insights"], list)
@pytest.mark.asyncio
async def test_compare_with_industry_averages(self, company_analyzer):
"""Test industry comparison functionality"""
company_code = "005930"
comparison = await company_analyzer.compare_with_industry(
company_code=company_code,
industry_code="26211" # Semiconductor manufacturing
)
assert comparison is not None
assert "company_metrics" in comparison
assert "industry_averages" in comparison
assert "comparison_analysis" in comparison
assert "ranking" in comparison
# Test comparison metrics
company_metrics = comparison["company_metrics"]
industry_averages = comparison["industry_averages"]
for key in ["operating_margin", "net_margin", "roe", "debt_ratio"]:
assert key in company_metrics
assert key in industry_averages
# Test ranking
ranking = comparison["ranking"]
assert "percentile" in ranking
assert "industry_rank" in ranking
assert 0 <= ranking["percentile"] <= 100
@pytest.mark.asyncio
async def test_get_valuation_metrics(self, company_analyzer):
"""Test comprehensive valuation metrics"""
company_code = "005930"
valuation = await company_analyzer.get_valuation_metrics(
company_code=company_code,
include_peers=True
)
assert valuation is not None
assert "price_multiples" in valuation
assert "enterprise_multiples" in valuation
assert "dividend_metrics" in valuation
assert "peer_comparison" in valuation
assert "valuation_summary" in valuation
# Test price multiples
price_multiples = valuation["price_multiples"]
expected_multiples = ["per", "pbr", "psr", "pcr", "pfr"]
for multiple in expected_multiples:
assert multiple in price_multiples
assert price_multiples[multiple] > 0
@pytest.mark.asyncio
async def test_error_handling_invalid_company(self, company_analyzer):
"""Test error handling for invalid company codes"""
invalid_code = "999999"
with pytest.raises(CompanyNotFoundError):
await company_analyzer.get_enhanced_company_overview(invalid_code)
@pytest.mark.asyncio
async def test_error_handling_insufficient_data(self, company_analyzer):
"""Test handling when insufficient data is available"""
# Test with a newly listed company that might not have full financial history
new_company_code = "123456" # Hypothetical new listing
with pytest.raises(InsufficientDataError):
await company_analyzer.analyze_financial_trends(
company_code=new_company_code,
years=10 # Requesting more years than available
)
class TestFinancialCalculator:
"""Test financial calculation utilities"""
@pytest.fixture
def calculator(self):
"""Create financial calculator instance"""
return FinancialCalculator()
@pytest.fixture
def sample_financial_data(self):
"""Sample financial data for calculations"""
return {
"revenue": 258_774_000_000_000,
"operating_profit": 22_034_000_000_000,
"net_profit": 15_349_000_000_000,
"total_assets": 426_071_000_000_000,
"total_equity": 319_167_000_000_000,
"total_debt": 106_904_000_000_000,
"current_assets": 200_000_000_000_000,
"current_liabilities": 80_000_000_000_000,
"shares_outstanding": 5_969_782_550,
"stock_price": 70_000
}
def test_calculate_profitability_ratios(self, calculator, sample_financial_data):
"""Test profitability ratio calculations"""
ratios = calculator.calculate_profitability_ratios(sample_financial_data)
assert "operating_margin" in ratios
assert "net_margin" in ratios
assert "roe" in ratios
assert "roa" in ratios
assert "roic" in ratios
# Validate calculations
expected_operating_margin = (22_034_000_000_000 / 258_774_000_000_000) * 100
assert abs(ratios["operating_margin"] - expected_operating_margin) < 0.1
expected_roe = (15_349_000_000_000 / 319_167_000_000_000) * 100
assert abs(ratios["roe"] - expected_roe) < 0.1
def test_calculate_liquidity_ratios(self, calculator, sample_financial_data):
"""Test liquidity ratio calculations"""
ratios = calculator.calculate_liquidity_ratios(sample_financial_data)
assert "current_ratio" in ratios
assert "quick_ratio" in ratios
assert "cash_ratio" in ratios
# Validate current ratio calculation
expected_current_ratio = 200_000_000_000_000 / 80_000_000_000_000
assert abs(ratios["current_ratio"] - expected_current_ratio) < 0.1
def test_calculate_leverage_ratios(self, calculator, sample_financial_data):
"""Test leverage ratio calculations"""
ratios = calculator.calculate_leverage_ratios(sample_financial_data)
assert "debt_to_equity" in ratios
assert "debt_to_assets" in ratios
assert "equity_ratio" in ratios
assert "interest_coverage" in ratios
# Validate debt-to-equity calculation
expected_dte = 106_904_000_000_000 / 319_167_000_000_000
assert abs(ratios["debt_to_equity"] - expected_dte) < 0.01
def test_calculate_market_ratios(self, calculator, sample_financial_data):
"""Test market ratio calculations"""
ratios = calculator.calculate_market_ratios(sample_financial_data)
assert "per" in ratios
assert "pbr" in ratios
assert "market_cap" in ratios
assert "enterprise_value" in ratios
# Validate market cap calculation
expected_market_cap = 5_969_782_550 * 70_000
assert abs(ratios["market_cap"] - expected_market_cap) < 1000
def test_calculate_growth_rates(self, calculator):
"""Test growth rate calculations"""
historical_data = [
{"year": 2021, "revenue": 200_000_000_000_000},
{"year": 2022, "revenue": 230_000_000_000_000},
{"year": 2023, "revenue": 258_774_000_000_000}
]
growth_rates = calculator.calculate_growth_rates(historical_data, "revenue")
assert "yoy_growth" in growth_rates
assert "cagr" in growth_rates
assert "average_growth" in growth_rates
# CAGR should be positive for growing revenue
assert growth_rates["cagr"] > 0
assert len(growth_rates["yoy_growth"]) == 2 # 2 year-over-year calculations
def test_calculate_cagr(self, calculator):
"""Test CAGR calculation"""
start_value = 100_000_000_000_000
end_value = 150_000_000_000_000
years = 3
cagr = calculator.calculate_cagr(start_value, end_value, years)
# Manual verification: (150/100)^(1/3) - 1 β 14.47%
expected_cagr = 14.47
assert abs(cagr - expected_cagr) < 0.1
def test_calculate_percentile_ranking(self, calculator):
"""Test percentile ranking calculation"""
industry_values = [10, 15, 20, 25, 30, 35, 40]
company_value = 28
percentile = calculator.calculate_percentile_ranking(company_value, industry_values)
# 28 is at position 4 out of 7 positions (0-6), so 4/7 * 100 β 57.1%
assert 55 <= percentile <= 60
if __name__ == "__main__":
# Run tests with pytest
pytest.main([__file__, "-v"])