212 lines
6.0 KiB
Python
212 lines
6.0 KiB
Python
"""利润模拟服务单元测试
|
|
|
|
测试 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
|