PayrollManagementSystem/scripts/run_isolated_tests.py
liuwenxin 1461a8d113 feat(后端): 添加授权体系、系统监控与试用期计提
按 2026-11-02 需求补齐计划交付后端能力:
- 角色授权:权限目录化,接口统一准入校验,未登记默认拒绝;
  管理员判断改为按数据库角色重新判定,防止权限码持有者绕过
- 系统监控:单进程采集资源、请求与异常指标,分钟级聚合,
  阈值配置化;敏感配置增加 repr 保护,Redis 密码 URL 转义
- 试用期计提:按回款日判定并写入快照,年度结清与调整单联动;
  人员模型补试用期、离职日期与工资版本历史字段
- 计提模板:公式骨架受控,页面维护名称与参数绑定
- T+ 采集:自动化配置表与进度查询

内置角色禁停用保护对齐模块注释;核算规则版本提为常量并
补充批量核算失败堆栈日志。

影响范围:后端全部模块、数据库迁移 V22-V28
验证:uv run ruff check 通过;922 个单元测试全部通过;
集成测试因本机 MySQL 数据卷被其他容器锁定暂未运行
2026-11-03 11:20:55 +08:00

53 lines
2.3 KiB
Python

"""使用唯一的本地测试库运行迁移和集成测试,绝不清理原业务库。"""
import os
import argparse
import re
import secrets
import subprocess
import sys
from pathlib import Path
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--reuse-database')
parser.add_argument('--migration-roundtrip', action='store_true')
parsed, test_args = parser.parse_known_args()
if parsed.reuse_database and not re.fullmatch(r'payroll_test_[0-9a-f]{12}', parsed.reuse_database):
raise SystemExit('只允许复用本运行器创建的隔离测试库')
root = Path(__file__).resolve().parents[1]
python = root / "backend/.venv-workbench/Scripts/python.exe"
if not python.exists():
python = Path(sys.executable)
suffix = secrets.token_hex(6)
database, username = parsed.reuse_database or "payroll_test_" + suffix, "pytest_" + suffix
password = secrets.token_urlsafe(30)
sql = ("" if parsed.reuse_database else f"CREATE DATABASE `{database}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ") + f"CREATE USER '{username}'@'%' IDENTIFIED BY '{password}'; GRANT ALL PRIVILEGES ON `{database}`.* TO '{username}'@'%';"
setup = subprocess.run(
["docker", "exec", "-i", "ys-mysql-dev", "sh", "-c", 'export MYSQL_PWD="$MYSQL_ROOT_PASSWORD"; exec mysql -uroot'],
input=sql.encode(), capture_output=True,
)
if setup.returncode:
raise SystemExit("隔离库创建失败,未执行迁移或测试")
env = os.environ | {
"MYSQL_DATABASE": database, "MYSQL_USER": username, "MYSQL_PASSWORD": password,
"PAYROLL_TEST_DATABASE": database, "REDIS_DB": "15", "PYTHONIOENCODING": "utf-8",
"UPLOAD_DIR": str(root / ".ai" / "isolated-uploads" / database),
}
print("独立测试库:" + database, flush=True)
commands = [["-m", "alembic", "upgrade", "head"]]
if parsed.migration_roundtrip:
commands += [["-m", "alembic", "downgrade", "07b8c9d0e1f2"], ["-m", "alembic", "upgrade", "head"]]
args = test_args
commands.append(["-m", "pytest", *(args or ["tests/integration"]), "-q", "--tb=short"])
for args in commands:
result = subprocess.run([str(python), *args], cwd=root / "backend", env=env)
if result.returncode:
return result.returncode
return 0
if __name__ == "__main__":
raise SystemExit(main())