PayrollManagementSystem/backend/tests/unit/test_tplus_worker.py

101 lines
3.4 KiB
Python
Raw Normal View History

"""排队任务要有实际执行者;忙锁、断连和进程中断不能被误记成成功。"""
from contextlib import contextmanager, nullcontext
from unittest.mock import Mock
from uuid import uuid4
from sqlalchemy import select
from app.core.tplus_lock import TPlusBusyError
from app.models import TPlusSyncError
from app.schemas.tplus import TPlusCreateIn
from app.services.tplus import task_service, worker
def _queued(session, audit=False):
return task_service.create_task(
session,
TPlusCreateIn(
request_key=uuid4(),
task_type="AUDIT" if audit else "READ",
entity_types=[] if audit else ["products"],
),
)
def _worker_setup(monkeypatch):
monkeypatch.setattr(worker, "sync_lock", lambda session: nullcontext())
monkeypatch.setattr(
"app.services.tplus.reader.TPlusReader.iter_entity",
lambda *a, **kw: iter([{"id": 1, "code": "A"}]),
)
return lambda: nullcontext(Mock())
def test_读取任务由worker处理并可观察重复数(tplus_session, monkeypatch):
source = _worker_setup(monkeypatch)
first = _queued(tplus_session)
assert worker.run_once(tplus_session, source_factory=source) == first.batch_no
assert first.status == "SUCCEEDED" and first.started_at is not None
second = _queued(tplus_session)
worker.run_once(tplus_session, source_factory=source)
assert second.staged_record_count == 0
assert second.skipped_record_count == 1
def test_忙锁时不领取任务也不恢复其他运行记录(tplus_session, monkeypatch):
source = _worker_setup(monkeypatch)
batch = _queued(tplus_session)
@contextmanager
def busy(_session):
raise TPlusBusyError("已有任务")
yield
monkeypatch.setattr(worker, "sync_lock", busy)
assert worker.run_once(tplus_session, source_factory=source) is None
assert batch.status == "QUEUED"
def test_源连接失败写入固定错误不泄漏凭据(tplus_session, monkeypatch):
_worker_setup(monkeypatch)
batch = _queued(tplus_session)
def unavailable():
raise RuntimeError("password=private-secret")
worker.run_once(tplus_session, source_factory=unavailable)
assert batch.status == "FAILED"
error = tplus_session.scalar(select(TPlusSyncError))
assert "private-secret" not in error.error_message
assert error.error_code == "EXECUTION_ERROR"
def test_取得锁后把遗留运行中任务记为中断失败(tplus_session, monkeypatch):
source = _worker_setup(monkeypatch)
batch = _queued(tplus_session)
batch.status = "RUNNING"
tplus_session.commit()
assert worker.run_once(tplus_session, source_factory=source) is None
assert batch.status == "FAILED"
assert tplus_session.scalar(select(TPlusSyncError.error_code)) == "INTERRUPTED"
def test_核验任务持久化报告但不修改业务数据(tplus_session, monkeypatch):
source = _worker_setup(monkeypatch)
batch = _queued(tplus_session, audit=True)
monkeypatch.setattr(
worker.TPlusRelationshipAudit,
"run",
lambda self: {
"relationship_status": "OBSERVED",
"receipt_kinds": [{"row_count": 10}],
"compatibility_status": "PENDING",
"is_target_engine": False,
},
)
worker.run_once(tplus_session, source_factory=source)
assert batch.status == "SUCCEEDED"
assert batch.audit_result_json["compatibility_status"] == "PENDING"
assert batch.staged_record_count == 0