5278190750
- 后端: 凭证生成引擎、金蝶导出器、凭证模板服务 - 后端: 成本分析服务、AI问答服务 - 后端: 科目映射CRUD API、分析API、QA API - 后端: 集成测试(认证/任务/凭证) 49个测试全部通过 - 前端: 凭证管理、成本分析、导出中心、知识库、系统设置页面 - 前端: AuthGuard认证守卫、Dashboard AI聊天功能 - 前端: Playwright E2E测试 16 passed, 1 skipped - 基础设施: Docker Compose、Nginx反向代理、.env.example - 文档: 用户手册、管理员手册、发布检查清单
150 lines
5.0 KiB
Python
150 lines
5.0 KiB
Python
"""
|
|
成本分析计算服务单元测试
|
|
|
|
测试 CostCalculatorService 和 CostSummary / DepartmentCost 的核心逻辑
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from app.services.analysis.cost_calculator import (
|
|
CostCalculatorService,
|
|
CostSummary,
|
|
DepartmentCost,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_db():
|
|
"""模拟数据库会话"""
|
|
return AsyncMock()
|
|
|
|
|
|
@pytest.fixture
|
|
def cost_calculator(mock_db):
|
|
"""创建成本计算服务实例"""
|
|
return CostCalculatorService(mock_db)
|
|
|
|
|
|
class TestCostSummary:
|
|
"""成本汇总结果测试"""
|
|
|
|
def test_init_defaults(self):
|
|
"""测试默认值"""
|
|
summary = CostSummary()
|
|
assert summary.total_cost == 0.0
|
|
assert summary.salary_cost == 0.0
|
|
assert summary.social_security_cost == 0.0
|
|
assert summary.fund_cost == 0.0
|
|
assert summary.employee_count == 0
|
|
|
|
def test_init_with_values(self):
|
|
"""测试带值初始化"""
|
|
summary = CostSummary(
|
|
total_cost=50000,
|
|
salary_cost=30000,
|
|
social_security_cost=10000,
|
|
fund_cost=10000,
|
|
employee_count=10,
|
|
)
|
|
assert summary.total_cost == 50000
|
|
assert summary.salary_cost == 30000
|
|
assert summary.employee_count == 10
|
|
|
|
def test_to_dict(self):
|
|
"""测试转字典"""
|
|
summary = CostSummary(total_cost=10000, salary_cost=8000, employee_count=5)
|
|
d = summary.to_dict()
|
|
assert d["total_cost"] == 10000
|
|
assert d["salary_cost"] == 8000
|
|
assert d["employee_count"] == 5
|
|
|
|
|
|
class TestDepartmentCost:
|
|
"""部门成本测试"""
|
|
|
|
def test_init(self):
|
|
"""测试初始化"""
|
|
dept = DepartmentCost(department="技术部", employee_count=10, salary_cost=100000)
|
|
assert dept.department == "技术部"
|
|
assert dept.employee_count == 10
|
|
assert dept.salary_cost == 100000
|
|
|
|
def test_total_cost_property(self):
|
|
"""测试 total_cost 属性计算"""
|
|
dept = DepartmentCost(
|
|
department="财务部",
|
|
salary_cost=10000,
|
|
social_security_cost=3000,
|
|
fund_cost=1200,
|
|
)
|
|
assert dept.total_cost == 14200
|
|
|
|
def test_to_dict(self):
|
|
"""测试转字典"""
|
|
dept = DepartmentCost(department="技术部", salary_cost=10000, employee_count=5)
|
|
d = dept.to_dict()
|
|
assert d["department"] == "技术部"
|
|
assert d["total_cost"] == 10000
|
|
assert d["employee_count"] == 5
|
|
|
|
|
|
class TestCostCalculatorService:
|
|
"""成本计算服务测试"""
|
|
|
|
def test_init(self, mock_db):
|
|
"""测试初始化"""
|
|
service = CostCalculatorService(mock_db)
|
|
assert service.db == mock_db
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_calculate_total_cost(self, cost_calculator):
|
|
"""测试计算总成本"""
|
|
mock_data = [
|
|
{"应发工资": 10000, "养老保险(公司)": 2000, "医疗保险(公司)": 1000, "失业保险(公司)": 500, "公积金(公司)": 1200},
|
|
{"应发工资": 8000, "养老保险(公司)": 1600, "医疗保险(公司)": 800, "失业保险(公司)": 400, "公积金(公司)": 960},
|
|
]
|
|
with patch.object(
|
|
cost_calculator, "_load_cleaned_data", return_value=mock_data
|
|
):
|
|
summary = await cost_calculator.calculate_total_cost(task_id=1)
|
|
|
|
assert summary.employee_count == 2
|
|
assert summary.salary_cost == 18000
|
|
assert summary.social_security_cost == 6300 # (2000+1000+500) + (1600+800+400)
|
|
assert summary.fund_cost == 2160 # 1200 + 960
|
|
assert summary.total_cost == 18000 + 6300 + 2160
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_calculate_total_cost_empty(self, cost_calculator):
|
|
"""测试空数据计算"""
|
|
with patch.object(
|
|
cost_calculator, "_load_cleaned_data", return_value=[]
|
|
):
|
|
summary = await cost_calculator.calculate_total_cost(task_id=1)
|
|
|
|
assert summary.employee_count == 0
|
|
assert summary.total_cost == 0.0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_calculate_by_department(self, cost_calculator):
|
|
"""测试按部门汇总"""
|
|
mock_data = [
|
|
{"部门": "技术部", "应发工资": 10000, "养老保险(公司)": 2000, "公积金(公司)": 1200},
|
|
{"部门": "技术部", "应发工资": 8000, "养老保险(公司)": 1600, "公积金(公司)": 960},
|
|
{"部门": "财务部", "应发工资": 12000, "养老保险(公司)": 2400, "公积金(公司)": 1440},
|
|
]
|
|
with patch.object(
|
|
cost_calculator, "_load_cleaned_data", return_value=mock_data
|
|
):
|
|
departments = await cost_calculator.calculate_by_department(task_id=1)
|
|
|
|
assert len(departments) == 2
|
|
tech = [d for d in departments if d.department == "技术部"][0]
|
|
assert tech.employee_count == 2
|
|
assert tech.salary_cost == 18000
|
|
finance = [d for d in departments if d.department == "财务部"][0]
|
|
assert finance.employee_count == 1
|
|
assert finance.salary_cost == 12000
|
|
|