PayrollManagementSystem/backend/tests/unit/test_mysql_month_lock.py
liuwenxin 1e53792ad6 feat(T+): 完成数据贯通与自动接入闭环
打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。
新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。
补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
2026-11-05 18:47:13 +08:00

63 lines
2.3 KiB
Python

"""在显式指定的临时 MySQL 库中验证工资月份行锁。"""
import os
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from datetime import datetime
from threading import Event
from types import SimpleNamespace
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from app.models import PayrollMonth
from app.services.payroll_months import lock_month, lock_month_row
@pytest.mark.skipif(not os.environ.get("PAYROLL_LOCK_TEST_URL"), reason="需要独立的临时 MySQL 测试库")
def test_同步等待工资事务提交并读取最新生成状态(monkeypatch):
from app.services import payroll_months
engine = create_engine(os.environ["PAYROLL_LOCK_TEST_URL"])
PayrollMonth.__table__.create(engine)
monkeypatch.setattr(payroll_months, "ensure_payroll_writer", lambda _operator: None)
actor = SimpleNamespace(id=1, username="test")
started = Event()
def check_sync():
with Session(engine) as session:
started.set()
control = lock_month_row(session, "2026-03", actor)
return control.generated_at
try:
with Session(engine) as generation, ThreadPoolExecutor(max_workers=1) as pool:
control = lock_month(generation, "2026-03", actor)
result = pool.submit(check_sync)
assert started.wait(5)
with pytest.raises(TimeoutError):
result.result(timeout=0.25)
control.generated_at = datetime(2026, 3, 31)
generation.commit()
assert result.result(timeout=10) == datetime(2026, 3, 31)
started.clear()
def generate():
with Session(engine) as session:
started.set()
control = lock_month(session, "2026-04", actor)
control.generated_at = datetime(2026, 4, 30)
session.commit()
with Session(engine) as sync, ThreadPoolExecutor(max_workers=1) as pool:
lock_month_row(sync, "2026-04", actor)
result = pool.submit(generate)
assert started.wait(5)
with pytest.raises(TimeoutError):
result.result(timeout=0.25)
sync.commit()
result.result(timeout=10)
finally:
engine.dispose()