默认同时只登一个号,失败两次跳下一个,号间留间隔,结束后可一键重试失败账号。 顺带拆分虎牙路由与斗鱼工作台页面,并补齐 raw_cookie 重建入口。 Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
4.0 KiB
Python
117 lines
4.0 KiB
Python
"""斗鱼写路径契约:锁单/支付编排走 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()
|