按 2026-11-02 需求补齐计划交付后端能力: - 角色授权:权限目录化,接口统一准入校验,未登记默认拒绝; 管理员判断改为按数据库角色重新判定,防止权限码持有者绕过 - 系统监控:单进程采集资源、请求与异常指标,分钟级聚合, 阈值配置化;敏感配置增加 repr 保护,Redis 密码 URL 转义 - 试用期计提:按回款日判定并写入快照,年度结清与调整单联动; 人员模型补试用期、离职日期与工资版本历史字段 - 计提模板:公式骨架受控,页面维护名称与参数绑定 - T+ 采集:自动化配置表与进度查询 内置角色禁停用保护对齐模块注释;核算规则版本提为常量并 补充批量核算失败堆栈日志。 影响范围:后端全部模块、数据库迁移 V22-V28 验证:uv run ruff check 通过;922 个单元测试全部通过; 集成测试因本机 MySQL 数据卷被其他容器锁定暂未运行
81 lines
3.2 KiB
Python
81 lines
3.2 KiB
Python
"""真实分页进度在每页提交后可读;总量未知或失败时不伪造成功。"""
|
|
from datetime import datetime
|
|
from unittest.mock import Mock
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.models import TPlusSyncBatch
|
|
from app.services.tplus import progress
|
|
from app.services.tplus.sync_service import TPlusSyncService
|
|
from tests.unit.test_tplus_sync import target as target
|
|
|
|
|
|
def test_每页提交真实已读数及类别(target):
|
|
service = TPlusSyncService(Mock(), target)
|
|
service.reader = Mock()
|
|
service.reader.count_entities.return_value = {"products": 2}
|
|
|
|
def rows(*args, **kwargs):
|
|
yield {"id": 1}
|
|
batch = target.scalar(select(TPlusSyncBatch))
|
|
item = progress.public(batch)
|
|
assert item["processed_records"] == 1 and item["total_records"] == 2
|
|
assert item["percentage"] == 50.0 and item["active_entity"] == "products"
|
|
yield {"id": 2}
|
|
|
|
service.reader.iter_entity.side_effect = rows
|
|
batch = service.run(entity_types=["products"], batch_size=1)
|
|
item = progress.public(batch)
|
|
assert batch.status == "SUCCEEDED" and item["percentage"] == 100.0
|
|
assert item["entities"][0]["status"] == "COMPLETED"
|
|
|
|
|
|
def test_总量统计失败仍可读且执行中总量未知(target):
|
|
source = Mock()
|
|
service = TPlusSyncService(source, target)
|
|
service.reader = Mock()
|
|
service.reader.count_entities.side_effect = RuntimeError("不得公开的数据库详情")
|
|
|
|
def rows(*args, **kwargs):
|
|
batch = target.scalar(select(TPlusSyncBatch))
|
|
assert progress.public(batch)["percentage"] is None
|
|
yield {"id": 1}
|
|
|
|
service.reader.iter_entity.side_effect = rows
|
|
batch = service.run(entity_types=["products"])
|
|
assert batch.status == "SUCCEEDED"
|
|
source.rollback.assert_called_once()
|
|
|
|
|
|
def test_读取中断保留进度及错误终态(target):
|
|
service = TPlusSyncService(Mock(), target)
|
|
service.reader = Mock()
|
|
service.reader.count_entities.return_value = {"products": 4}
|
|
|
|
def rows(*args, **kwargs):
|
|
yield {"id": 1}
|
|
raise TimeoutError("敏感详情")
|
|
|
|
service.reader.iter_entity.side_effect = rows
|
|
batch = service.run(entity_types=["products"], batch_size=1)
|
|
item = progress.public(batch)
|
|
assert batch.status == "FAILED" and item["percentage"] == 25.0
|
|
assert item["phase"] == "FAILED" and item["entities"][0]["status"] == "FAILED"
|
|
|
|
|
|
def test_旧批次未知总量不显示编造百分比():
|
|
batch = TPlusSyncBatch(batch_no="old", sync_mode="FULL", status="RUNNING",
|
|
source_record_count=1000, entity_types_json=["personnel"], started_at=datetime.now())
|
|
item = progress.public(batch)
|
|
assert item["percentage"] is None and item["total_records"] is None
|
|
assert item["processed_records"] == 1000
|
|
|
|
|
|
def test_读到预统计总量仍等终态才显示百分之百():
|
|
batch = TPlusSyncBatch(batch_no="b", sync_mode="FULL", status="RUNNING",
|
|
source_record_count=10, entity_types_json=["products"], started_at=datetime.now(),
|
|
audit_result_json={"progress": {"total_records": 10}})
|
|
assert progress.public(batch)["percentage"] == 99.9
|
|
batch.status = "SUCCEEDED"
|
|
assert progress.public(batch)["percentage"] == 100.0
|