Files
2026-09-12 18:15:20 +08:00

81 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""事务与操作日志集成测试:db_session_middleware 的 commit/rollback 分支、operation_log 独立落库。
需真实 MySQL/PostgreSQL/SQL Server/SQLite + Redis 运行(默认标记 integration 跳过)。
"""
import pytest
from sqlalchemy import text
from core.config import DB_PREFIX
from core.database import engine
pytestmark = pytest.mark.integration
def _params(*values):
"""位置参数 → 命名参数(p1/p2/...),无参数返回 None。"""
return {f"p{i + 1}": v for i, v in enumerate(values)} if values else None
def _query_count(table, where, *values):
with engine.connect() as conn:
return conn.execute(
text(f"SELECT COUNT(*) FROM {table} WHERE {where}"), _params(*values)
).scalar()
def _execute(sql, *values):
with engine.begin() as conn:
conn.execute(text(sql), _params(*values))
def _table(name):
return DB_PREFIX + name
_VALID_DEPT = {"name": "测试部门T", "code": "TESTT", "type": 1, "parentId": 0, "sort": 99, "status": 1}
@pytest.mark.asyncio
async def test_write_commit_persists(client, admin_headers):
"""业务成功的写操作应被 db_session_middleware 提交。"""
r = await client.post("/api/v1/dept/add", headers=admin_headers, json=_VALID_DEPT)
assert r.json()["code"] == 0
assert _query_count(_table("dept"), "name=:p1", "测试部门T") >= 1
# 清理测试数据
_execute(f"DELETE FROM {_table('dept')} WHERE name=:p1", "测试部门T")
@pytest.mark.asyncio
async def test_write_rollback_on_validation_error(client, admin_headers):
"""参数校验失败(code!=0)时请求事务应回滚,不产生脏数据。"""
before = _query_count(_table("dept"), "name=:p1", "测试部门X")
r = await client.post("/api/v1/dept/add", headers=admin_headers, json={"name": "测试部门X"}) # 缺必填字段
assert r.json()["code"] != 0
assert _query_count(_table("dept"), "name=:p1", "测试部门X") == before
@pytest.mark.asyncio
async def test_operation_log_persists_on_success(client, admin_headers):
"""成功的写操作后,operation_log 应通过独立会话落库(标题正确)。"""
before = _query_count(_table("operation_log"), "1=1")
r = await client.post("/api/v1/dept/add", headers=admin_headers, json=_VALID_DEPT)
assert r.json()["code"] == 0
after = _query_count(_table("operation_log"), "1=1")
assert after >= before + 1, "operation_log 未新增记录"
_execute(f"DELETE FROM {_table('dept')} WHERE name=:p1", "测试部门T")
@pytest.mark.asyncio
async def test_operation_log_persists_on_failure(client, admin_headers):
"""业务失败的写操作也应记录 operation_log(status=1),且独立会话不受请求回滚影响。"""
# 构造一个通过参数校验但业务失败的操作:部门编码重复(唯一性校验)
await client.post("/api/v1/dept/add", headers=admin_headers, json=_VALID_DEPT)
before = _query_count(_table("operation_log"), "1=1")
# 再次添加同名同编码 → 业务失败
r = await client.post("/api/v1/dept/add", headers=admin_headers, json=_VALID_DEPT)
assert r.json()["code"] != 0
after = _query_count(_table("operation_log"), "1=1")
assert after >= before + 1, "业务失败的写操作未记录 operation_log"
_execute(f"DELETE FROM {_table('dept')} WHERE name=:p1", "测试部门T")