Files
s2f/backend/tests/test_vouchers_integration.py
selfrelease 5278190750 feat: 凭证生成、成本分析、AI问答、前端页面、集成测试与E2E测试
- 后端: 凭证生成引擎、金蝶导出器、凭证模板服务
- 后端: 成本分析服务、AI问答服务
- 后端: 科目映射CRUD API、分析API、QA API
- 后端: 集成测试(认证/任务/凭证) 49个测试全部通过
- 前端: 凭证管理、成本分析、导出中心、知识库、系统设置页面
- 前端: AuthGuard认证守卫、Dashboard AI聊天功能
- 前端: Playwright E2E测试 16 passed, 1 skipped
- 基础设施: Docker Compose、Nginx反向代理、.env.example
- 文档: 用户手册、管理员手册、发布检查清单
2026-07-07 21:21:29 +08:00

179 lines
6.5 KiB
Python

"""
凭证 API 集成测试
测试科目映射 CRUD、凭证模板等端点
使用同步 TestClient + Mock 数据库会话
"""
from unittest.mock import AsyncMock, MagicMock
from fastapi.testclient import TestClient
from app.models.account_mapping import AccountMapping
from tests.conftest import make_db_result
def _make_mapping(mapping_id=1, field="基本工资", debit="6601.01", credit="2211.01"):
"""构造测试科目映射对象"""
return AccountMapping(
id=mapping_id,
company_id=1,
standard_field=field,
debit_account=debit,
debit_account_name=f"管理费用-{field}",
credit_account=credit,
credit_account_name="应付职工薪酬",
cost_center=None,
is_active=True,
)
class TestVoucherTemplatesAPI:
"""凭证模板 API 测试"""
def test_get_templates(self, client: TestClient, auth_headers: dict):
"""测试获取默认凭证模板"""
response = client.get("/api/vouchers/templates/list", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert "基本工资" in data
assert "养老保险(公司)" in data
def test_get_templates_no_auth(self, client: TestClient):
"""测试无认证访问模板(该端点不需要认证)"""
response = client.get("/api/vouchers/templates/list")
assert response.status_code == 200
class TestAccountMappingsAPI:
"""科目映射 CRUD API 测试"""
def test_list_mappings_empty(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试空映射列表"""
mock_db.execute = AsyncMock(return_value=make_db_result(items=[]))
response = client.get(
"/api/vouchers/account-mappings/list", headers=auth_headers
)
assert response.status_code == 200
assert response.json() == []
def test_create_mapping(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试创建科目映射"""
def refresh_side_effect(obj, *args, **kwargs):
obj.id = 1
obj.is_active = True
mock_db.refresh.side_effect = refresh_side_effect
response = client.post(
"/api/vouchers/account-mappings",
headers=auth_headers,
json={
"standard_field": "基本工资",
"debit_account": "6601.01",
"debit_account_name": "管理费用-工资",
"credit_account": "2211.01",
"credit_account_name": "应付职工薪酬-工资",
},
)
assert response.status_code == 200
data = response.json()
assert data["standard_field"] == "基本工资"
assert data["debit_account"] == "6601.01"
assert data["is_active"] is True
def test_create_and_list_mappings(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试创建后查询映射列表"""
mappings = [_make_mapping(1, "基本工资"), _make_mapping(2, "奖金", "6601.02")]
mock_db.execute = AsyncMock(return_value=make_db_result(items=mappings))
response = client.get(
"/api/vouchers/account-mappings/list", headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert len(data) == 2
def test_update_mapping(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试更新科目映射"""
existing = _make_mapping(1)
mock_db.get = AsyncMock(return_value=existing)
def refresh_side_effect(obj, *args, **kwargs):
pass
mock_db.refresh.side_effect = refresh_side_effect
response = client.put(
"/api/vouchers/account-mappings/1",
headers=auth_headers,
json={
"standard_field": "基本工资",
"debit_account": "6601.03",
"debit_account_name": "销售费用-工资",
"credit_account": "2211.01",
"credit_account_name": "应付职工薪酬-工资",
},
)
assert response.status_code == 200
data = response.json()
assert data["debit_account"] == "6601.03"
def test_update_mapping_not_found(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试更新不存在的映射"""
mock_db.get = AsyncMock(return_value=None)
response = client.put(
"/api/vouchers/account-mappings/99999",
headers=auth_headers,
json={
"standard_field": "基本工资",
"debit_account": "6601.01",
"debit_account_name": "管理费用-工资",
"credit_account": "2211.01",
"credit_account_name": "应付职工薪酬-工资",
},
)
assert response.status_code == 404
def test_delete_mapping(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试删除科目映射"""
existing = _make_mapping(1, "奖金")
mock_db.get = AsyncMock(return_value=existing)
response = client.delete(
"/api/vouchers/account-mappings/1", headers=auth_headers
)
assert response.status_code == 200
def test_delete_mapping_not_found(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试删除不存在的映射"""
mock_db.get = AsyncMock(return_value=None)
response = client.delete(
"/api/vouchers/account-mappings/99999", headers=auth_headers
)
assert response.status_code == 404
def test_create_mapping_with_cost_center(self, client: TestClient, auth_headers: dict, mock_db: AsyncMock):
"""测试带成本中心创建映射"""
def refresh_side_effect(obj, *args, **kwargs):
obj.id = 1
obj.is_active = True
mock_db.refresh.side_effect = refresh_side_effect
response = client.post(
"/api/vouchers/account-mappings",
headers=auth_headers,
json={
"standard_field": "基本工资",
"debit_account": "6601.01",
"debit_account_name": "管理费用-工资",
"credit_account": "2211.01",
"credit_account_name": "应付职工薪酬-工资",
"cost_center": "技术部",
},
)
assert response.status_code == 200
data = response.json()
assert data["cost_center"] == "技术部"