签入版本
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""事务与操作日志集成测试: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")
|
||||
Reference in New Issue
Block a user