80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
|
|
"""采集与业务接入分别记录结果。"""
|
||
|
|
|
||
|
|
from contextlib import nullcontext
|
||
|
|
from datetime import date
|
||
|
|
from types import SimpleNamespace
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from sqlalchemy.exc import IntegrityError
|
||
|
|
|
||
|
|
from app.core.exceptions import BadRequestError, ConflictError
|
||
|
|
from app.services.tplus import business_sync_control, worker
|
||
|
|
|
||
|
|
|
||
|
|
def test_自动接入排队失败不改变成功采集状态(monkeypatch):
|
||
|
|
batch = SimpleNamespace(requested_by=None, source_database="test_source", sync_mode="FULL", status="RUNNING")
|
||
|
|
session = SimpleNamespace(rollback=lambda: None)
|
||
|
|
failures = []
|
||
|
|
schedule_failures = []
|
||
|
|
monkeypatch.setattr(worker.task_service, "get_settings", lambda: object())
|
||
|
|
monkeypatch.setattr(worker, "resolve_tplus_settings", lambda _: SimpleNamespace(t_plus_database="test_source"))
|
||
|
|
monkeypatch.setattr(worker.task_service, "fail_batch", lambda *args: failures.append(args))
|
||
|
|
|
||
|
|
class SyncService:
|
||
|
|
def __init__(self, source, target):
|
||
|
|
pass
|
||
|
|
|
||
|
|
def execute_claimed(self, task):
|
||
|
|
task.status = "SUCCEEDED"
|
||
|
|
|
||
|
|
def fail_schedule(session):
|
||
|
|
raise RuntimeError("排队失败")
|
||
|
|
|
||
|
|
monkeypatch.setattr(worker, "TPlusSyncService", SyncService)
|
||
|
|
monkeypatch.setattr(business_sync_control, "schedule", fail_schedule)
|
||
|
|
monkeypatch.setattr(business_sync_control, "record_schedule_failure", lambda session, source: schedule_failures.append(source))
|
||
|
|
worker._execute(session, batch, lambda: nullcontext(object()))
|
||
|
|
assert batch.status == "SUCCEEDED"
|
||
|
|
assert failures == []
|
||
|
|
assert schedule_failures == ["test_source"]
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("race", ["same", "different", "unrelated"])
|
||
|
|
def test_业务接入排队完整性错误只核对一次既有任务(monkeypatch, race):
|
||
|
|
found = SimpleNamespace(kind="BUSINESS_SYNC", requested_by=1, source_database="test_source",
|
||
|
|
input_json={"start_date": "2026-03-01"})
|
||
|
|
if race == "different":
|
||
|
|
found.requested_by = 2
|
||
|
|
results = iter([None, found if race != "unrelated" else None])
|
||
|
|
rollbacks = []
|
||
|
|
|
||
|
|
class Session:
|
||
|
|
def get(self, *_args):
|
||
|
|
return object()
|
||
|
|
|
||
|
|
def scalar(self, _query):
|
||
|
|
return next(results)
|
||
|
|
|
||
|
|
def begin_nested(self):
|
||
|
|
return nullcontext()
|
||
|
|
|
||
|
|
def add(self, _row):
|
||
|
|
pass
|
||
|
|
|
||
|
|
def flush(self):
|
||
|
|
raise IntegrityError("insert", {}, Exception())
|
||
|
|
|
||
|
|
def rollback(self):
|
||
|
|
rollbacks.append(True)
|
||
|
|
|
||
|
|
monkeypatch.setattr(business_sync_control, "require_permission", lambda *_args: None)
|
||
|
|
monkeypatch.setattr(business_sync_control, "history_source_database", lambda _session: "test_source")
|
||
|
|
monkeypatch.setattr(business_sync_control, "work_out", lambda row: row)
|
||
|
|
args = (Session(), 1, date(2026, 3, 1), "same-key")
|
||
|
|
if race == "same":
|
||
|
|
assert business_sync_control.queue(*args) is found
|
||
|
|
else:
|
||
|
|
with pytest.raises(BadRequestError if race == "different" else ConflictError):
|
||
|
|
business_sync_control.queue(*args)
|
||
|
|
assert rollbacks == [True]
|