"""斗鱼写路径契约:锁单/支付编排走 mock 客户端,不打真实兑换。""" from unittest.mock import Mock from sqlalchemy import create_engine, event from sqlalchemy.orm import sessionmaker from web.backend.database import Base from web.backend.models import Account, DouyuGoodsSnapshot, DouyuTask, User from web.backend.services.douyu_runner import DouyuBatchRunner class TestDouyuWritePathContracts: def setup_method(self): self.engine = create_engine("sqlite://") @event.listens_for(self.engine, "connect") def enable_foreign_keys(connection, _): connection.execute("PRAGMA foreign_keys=ON") Base.metadata.create_all(self.engine) self.session = sessionmaker(bind=self.engine)() self.user = User(username="operator", password_hash="hash", role="super_admin") self.account = Account( username="dyuser", password="pw", email="a@example.com", email_password="em", ) self.session.add_all([self.user, self.account]) self.session.commit() self.session.add( DouyuGoodsSnapshot( commodity_id="bag10", name="套装-兔兔白日梦", raw={"name": "套装-兔兔白日梦"}, ) ) self.session.commit() def teardown_method(self): self.session.close() Base.metadata.drop_all(self.engine) self.engine.dispose() def _runner(self, task_type: str) -> DouyuBatchRunner: return DouyuBatchRunner(db=self.session, batch_id="batch1", task_type=task_type) def _task(self, task_type: str, payload: dict) -> DouyuTask: task = DouyuTask( batch_id="batch1", account_id=self.account.id, task_type=task_type, handbook_scope="elite", status="running", result={"payload": payload}, created_by=self.user.id, ) self.session.add(task) self.session.commit() return task def test_lock_goods_records_order_without_paying(self): task = self._task("lock_goods", {"commodity_id": "bag10", "num": 1}) runner = self._runner("lock_goods") client = Mock() client.create_exchange_order.return_value = { "order_id": "5874", "expire_seconds": "300", } runner._client = Mock(return_value=client) runner._execute_lock_goods( self.session, task, self.account, "acf_uid=100", {"manual_id": "G4KA4Qnz4LDp7", "rid": "9263298"}, ) self.session.refresh(task) self.session.refresh(self.account) assert task.status == "success" assert task.result["order_id"] == "5874" assert self.account.bind_status == "goods_locked" client.create_exchange_order.assert_called_once() client.pay_exchange_order.assert_not_called() def test_pay_locked_order_uses_existing_order_id(self): task = self._task( "pay_locked_order", {"order_id": "5874", "commodity_id": "bag10"}, ) runner = self._runner("pay_locked_order") client = Mock() client.pay_exchange_order.return_value = { "exchange_id": "11098933", "commodity_image": "https://example.test/item.png", "exchange_num": 1, } runner._client = Mock(return_value=client) runner._refresh_account_points = Mock(return_value={"points": 1280}) runner._execute_pay_locked_order( self.session, task, self.account, "acf_uid=100", {"manual_id": "G4KA4Qnz4LDp7", "rid": "9263298"}, ) self.session.refresh(task) assert task.status == "success" assert task.result["exchange_id"] == "11098933" client.pay_exchange_order.assert_called_once() client.create_exchange_order.assert_not_called()