"""利润模拟服务单元测试 测试 ProfitService 的核心业务逻辑 """ import pytest from decimal import Decimal from unittest.mock import AsyncMock, patch, MagicMock from sqlalchemy.ext.asyncio import AsyncSession from app.services.profit_service import ProfitService from app.schemas.profit import PeriodType from app.models import PricingPlan, FixedCost class TestProfitService: """利润模拟服务测试类""" @pytest.mark.unit def test_calculate_profit_basic(self): """测试基础利润计算""" service = ProfitService(None) revenue, cost, profit, margin = service.calculate_profit( price=100.0, cost_per_unit=60.0, volume=100 ) assert revenue == 10000.0 assert cost == 6000.0 assert profit == 4000.0 assert margin == 40.0 @pytest.mark.unit def test_calculate_profit_zero_revenue(self): """测试零收入时的处理""" service = ProfitService(None) revenue, cost, profit, margin = service.calculate_profit( price=100.0, cost_per_unit=60.0, volume=0 ) assert revenue == 0 assert cost == 0 assert profit == 0 assert margin == 0 @pytest.mark.unit def test_calculate_profit_negative(self): """测试亏损情况""" service = ProfitService(None) revenue, cost, profit, margin = service.calculate_profit( price=50.0, cost_per_unit=60.0, volume=100 ) assert revenue == 5000.0 assert cost == 6000.0 assert profit == -1000.0 assert margin == -20.0 @pytest.mark.unit def test_calculate_breakeven_basic(self): """测试基础盈亏平衡计算""" service = ProfitService(None) breakeven = service.calculate_breakeven( price=100.0, variable_cost=60.0, fixed_cost=0 ) assert breakeven == 1 @pytest.mark.unit def test_calculate_breakeven_with_fixed_cost(self): """测试有固定成本的盈亏平衡""" service = ProfitService(None) breakeven = service.calculate_breakeven( price=100.0, variable_cost=60.0, fixed_cost=4000.0 ) assert breakeven == 101 @pytest.mark.unit def test_calculate_breakeven_no_margin(self): """测试边际贡献为负时的处理""" service = ProfitService(None) breakeven = service.calculate_breakeven( price=50.0, variable_cost=60.0, fixed_cost=1000.0 ) assert breakeven == 999999 @pytest.mark.unit @pytest.mark.asyncio async def test_get_pricing_plan( self, db_session: AsyncSession, sample_pricing_plan: PricingPlan ): """测试获取定价方案""" service = ProfitService(db_session) plan = await service.get_pricing_plan(sample_pricing_plan.id) assert plan.id == sample_pricing_plan.id assert plan.plan_name == "2026年Q1定价" @pytest.mark.unit @pytest.mark.asyncio async def test_get_pricing_plan_not_found( self, db_session: AsyncSession ): """测试获取不存在的方案""" service = ProfitService(db_session) with pytest.raises(ValueError, match="定价方案不存在"): await service.get_pricing_plan(99999) @pytest.mark.unit @pytest.mark.asyncio async def test_get_monthly_fixed_cost( self, db_session: AsyncSession, sample_fixed_cost: FixedCost ): """测试获取月度固定成本""" service = ProfitService(db_session) total = await service.get_monthly_fixed_cost() assert total == Decimal("30000.00") @pytest.mark.unit @pytest.mark.asyncio async def test_simulate_profit( self, db_session: AsyncSession, sample_pricing_plan: PricingPlan ): """测试利润模拟""" service = ProfitService(db_session) response = await service.simulate_profit( pricing_plan_id=sample_pricing_plan.id, price=580.0, estimated_volume=100, period_type=PeriodType.MONTHLY, ) assert response.pricing_plan_id == sample_pricing_plan.id assert response.input.price == 580.0 assert response.input.estimated_volume == 100 @pytest.mark.unit @pytest.mark.asyncio async def test_sensitivity_analysis( self, db_session: AsyncSession, sample_pricing_plan: PricingPlan ): """测试敏感性分析""" service = ProfitService(db_session) sim_response = await service.simulate_profit( pricing_plan_id=sample_pricing_plan.id, price=580.0, estimated_volume=100, period_type=PeriodType.MONTHLY, ) response = await service.sensitivity_analysis( simulation_id=sim_response.simulation_id, price_change_rates=[-20, -10, 0, 10, 20] ) assert response.simulation_id == sim_response.simulation_id assert len(response.sensitivity_results) == 5 @pytest.mark.unit @pytest.mark.asyncio async def test_breakeven_analysis( self, db_session: AsyncSession, sample_pricing_plan: PricingPlan, sample_fixed_cost: FixedCost ): """测试盈亏平衡分析""" service = ProfitService(db_session) response = await service.breakeven_analysis( pricing_plan_id=sample_pricing_plan.id ) assert response.pricing_plan_id == sample_pricing_plan.id assert response.price > 0 assert response.breakeven_volume > 0