test: CMA自动化测试补覆盖 226→452用例, 覆盖率36%→60%
- 新增8个测试文件(bot_bridge/kpi_causality/cash/predict/reports/tax_compliance/expenses/probe_cost) - 增强 budget/auth/users + conftest账套模式适配 - 测试驱动修复: bot_bridge导入batch_id→source_batch; cash_forecast extra空dict - 全量: 451 passed, 1 xfailed; 报告 docs/cma-test-coverage-report.md
This commit is contained in:
@@ -35,6 +35,38 @@ TEST_ENGINE = create_engine(
|
||||
)
|
||||
TEST_SESSION_LOCAL = sessionmaker(autocommit=False, autoflush=False, bind=TEST_ENGINE)
|
||||
|
||||
|
||||
# MySQL-only 的 date_format() 在 SQLite 下注册等价实现(仅测试库)
|
||||
# 生产用 MySQL 原生函数;此处仅为让测试能跑通 expenses 月度累计校验/stats 统计
|
||||
def _sqlite_date_format(dt_val, fmt):
|
||||
if dt_val is None:
|
||||
return None
|
||||
import datetime as _dt
|
||||
if isinstance(dt_val, str):
|
||||
for f in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d", "%Y-%m"):
|
||||
try:
|
||||
dt_val = _dt.datetime.strptime(str(dt_val)[:19], f)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
return None
|
||||
if isinstance(dt_val, _dt.datetime):
|
||||
d = dt_val
|
||||
elif isinstance(dt_val, _dt.date):
|
||||
d = _dt.datetime(dt_val.year, dt_val.month, dt_val.day)
|
||||
else:
|
||||
return None
|
||||
return {
|
||||
"%Y": f"{d.year:04d}",
|
||||
"%Y-%m": f"{d.year:04d}-{d.month:02d}",
|
||||
"%Y-%m-%d": f"{d.year:04d}-{d.month:02d}-{d.day:02d}",
|
||||
}.get(fmt)
|
||||
|
||||
|
||||
from sqlalchemy import event # noqa: E402
|
||||
event.listen(TEST_ENGINE, "connect", lambda dbapi_conn, rec: dbapi_conn.create_function("date_format", 2, _sqlite_date_format))
|
||||
|
||||
# 替换 database 模块的全局引擎
|
||||
db_module._engine = TEST_ENGINE
|
||||
db_module._SessionLocal = TEST_SESSION_LOCAL
|
||||
@@ -69,6 +101,13 @@ def db() -> Generator[Session, None, None]:
|
||||
def client(db) -> Generator[TestClient, None, None]:
|
||||
"""提供测试 HTTP 客户端"""
|
||||
from app.main import app
|
||||
from app.models import Entity
|
||||
|
||||
# 账套模式:确保测试库存在 entity_id=1 的active实体
|
||||
ent = db.query(Entity).filter(Entity.id == 1).first()
|
||||
if not ent:
|
||||
db.add(Entity(id=1, name="测试企业", short_name="测试", status="active"))
|
||||
db.commit()
|
||||
|
||||
# 重写依赖,使用测试数据库
|
||||
app.dependency_overrides[db_module.get_db] = lambda: db
|
||||
@@ -98,12 +137,16 @@ def create_test_user(db: Session, **kwargs) -> User:
|
||||
|
||||
|
||||
def get_token_for_user(client: TestClient, username: str = "testadmin", password: str = "admin123") -> str:
|
||||
"""获取测试用户的token"""
|
||||
"""获取测试用户的token(账套模式:需entity_id)"""
|
||||
resp = client.post("/api/cma/auth/login", json={
|
||||
"username": username,
|
||||
"password": password,
|
||||
"entity_id": 1,
|
||||
})
|
||||
return resp.json()["token"]
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError(f"登录失败: {resp.status_code} {resp.text[:300]}")
|
||||
data = resp.json()
|
||||
return data.get("token") or data.get("access_token")
|
||||
|
||||
|
||||
def auth_header(token: str) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user