5278190750
- 后端: 凭证生成引擎、金蝶导出器、凭证模板服务 - 后端: 成本分析服务、AI问答服务 - 后端: 科目映射CRUD API、分析API、QA API - 后端: 集成测试(认证/任务/凭证) 49个测试全部通过 - 前端: 凭证管理、成本分析、导出中心、知识库、系统设置页面 - 前端: AuthGuard认证守卫、Dashboard AI聊天功能 - 前端: Playwright E2E测试 16 passed, 1 skipped - 基础设施: Docker Compose、Nginx反向代理、.env.example - 文档: 用户手册、管理员手册、发布检查清单
179 lines
6.5 KiB
Python
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"] == "技术部"
|