480 lines
14 KiB
Python
480 lines
14 KiB
Python
"""虎牙积分兑换新链路:直兑、购买积分包后再兑、前置条件。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from web.backend.models import HuyaAccount
|
|
from web.backend.services import huya_points_exchange as exchange
|
|
from web.backend.services.huya_runner import HuyaBatchRunner
|
|
from web.backend.services.huya_shop_flow import ShopOrderOutcome
|
|
|
|
|
|
class _Response:
|
|
def __init__(self, code=200, message="", payload=None, detail=None):
|
|
self.code = code
|
|
self.message = message
|
|
self.payload = payload or {}
|
|
self._detail = detail or {}
|
|
|
|
def value(self, *path):
|
|
node = self.payload
|
|
for key in path:
|
|
if not isinstance(node, dict):
|
|
return None
|
|
node = node.get(key)
|
|
return node
|
|
|
|
def to_dict(self):
|
|
return dict(self._detail or self.payload)
|
|
|
|
|
|
class _Client:
|
|
def __init__(self, states, *, goods_code=200):
|
|
self.states = list(states)
|
|
self.goods_code = goods_code
|
|
self.create_v6_calls = []
|
|
|
|
def get_redeem_need_goods(self, **_kwargs):
|
|
return self.states.pop(0)
|
|
|
|
def get_goods_info(self, **kwargs):
|
|
return _Response(
|
|
code=self.goods_code,
|
|
message="" if self.goods_code == 200 else "商品详情失败",
|
|
detail={
|
|
"sku_id": 7747527,
|
|
"name": "幸运币礼包(小)",
|
|
"price": 20,
|
|
"goods_type": 5,
|
|
"virtual_type": 507,
|
|
"spu_id": kwargs["spu_id"],
|
|
},
|
|
)
|
|
|
|
def create_order_v6(self, **kwargs):
|
|
self.create_v6_calls.append(kwargs)
|
|
return SimpleNamespace(
|
|
code=200,
|
|
message="",
|
|
orderId=12104642,
|
|
to_dict=lambda: {
|
|
"code": 200,
|
|
"order_id": 12104642,
|
|
"points": 20,
|
|
},
|
|
)
|
|
|
|
|
|
def _state(*, full: bool, precondition: bool = True, plans=None):
|
|
return _Response(
|
|
payload={
|
|
"tag2": 20,
|
|
"tag3": 20,
|
|
"tag4": 0 if full else 20,
|
|
"tag5": 1 if precondition else 0,
|
|
"tag6": 1 if full else 0,
|
|
"tag7": plans or [],
|
|
}
|
|
)
|
|
|
|
|
|
def test_points_enough_uses_create_order_v6_without_payment(monkeypatch):
|
|
client = _Client([_state(full=True)])
|
|
purchase_called = False
|
|
|
|
def fail_purchase(*_args, **_kwargs):
|
|
nonlocal purchase_called
|
|
purchase_called = True
|
|
raise AssertionError("积分足够时不应购买积分包")
|
|
|
|
monkeypatch.setattr(exchange, "run_open_flow", fail_purchase)
|
|
|
|
result = exchange.prepare_exchange(
|
|
client,
|
|
uid=1199669523011,
|
|
cookie="yyuid=1199669523011",
|
|
pid=1199650619883,
|
|
spu_id="hy-8315982",
|
|
)
|
|
|
|
assert result.status == exchange.EXCHANGE_STATUS_EXCHANGED
|
|
assert result.message == "兑换成功: 幸运币礼包(小)"
|
|
assert result.order["order_id"] == 12104642
|
|
assert purchase_called is False
|
|
assert client.create_v6_calls[0]["items"] == [
|
|
{
|
|
"spu_id": "hy-8315982",
|
|
"sku_id": 7747527,
|
|
"item_count": 1,
|
|
"goods_type": 5,
|
|
"virtual_type": 507,
|
|
}
|
|
]
|
|
|
|
|
|
def test_points_shortage_returns_points_package_payment(monkeypatch):
|
|
client = _Client(
|
|
[
|
|
_state(
|
|
full=False,
|
|
plans=[
|
|
{
|
|
"tag0": 8327356,
|
|
"tag1": 7759407,
|
|
"tag2": "精英币",
|
|
"tag5": 1,
|
|
"tag6": 10,
|
|
"tag7": 20,
|
|
"tag9": 2,
|
|
"tag21": 20,
|
|
}
|
|
],
|
|
)
|
|
]
|
|
)
|
|
calls = []
|
|
|
|
def fake_purchase(client_arg, **kwargs):
|
|
calls.append((client_arg, kwargs))
|
|
return ShopOrderOutcome(
|
|
ok=True,
|
|
message="已生成支付二维码",
|
|
order_id=12095620,
|
|
pay_url="https://openapi.alipay.com/gateway.do?probe=1",
|
|
amount=200,
|
|
product_name="精英币",
|
|
count=20,
|
|
)
|
|
|
|
monkeypatch.setattr(exchange, "run_open_flow", fake_purchase)
|
|
|
|
result = exchange.prepare_exchange(
|
|
client,
|
|
uid=1199669523011,
|
|
cookie="yyuid=1199669523011",
|
|
pid=1199650619883,
|
|
spu_id="hy-8315982",
|
|
pay_channel="Zfb",
|
|
)
|
|
|
|
assert result.status == exchange.EXCHANGE_STATUS_PAYMENT_REQUIRED
|
|
assert result.plan is not None
|
|
assert result.plan["spu_id"] == "hy-8327356"
|
|
assert result.plan["sku_id"] == 7759407
|
|
assert result.plan["count"] == 20
|
|
assert result.purchase is not None
|
|
assert result.purchase.order_id == 12095620
|
|
assert calls[0][1]["spu_id"] == "hy-8327356"
|
|
assert calls[0][1]["count"] == 20
|
|
assert client.create_v6_calls == []
|
|
|
|
|
|
def test_complete_after_payment_waits_for_points_then_uses_v6():
|
|
client = _Client([_state(full=False), _state(full=True)])
|
|
|
|
result = exchange.complete_exchange_after_payment(
|
|
client,
|
|
uid=1199669523011,
|
|
cookie="yyuid=1199669523011",
|
|
pid=1199650619883,
|
|
spu_id="hy-8315982",
|
|
attempts=2,
|
|
interval=0,
|
|
sleep=lambda _seconds: None,
|
|
)
|
|
|
|
assert result.status == exchange.EXCHANGE_STATUS_EXCHANGED
|
|
assert result.state is not None
|
|
assert result.state.user_score == 20
|
|
assert result.order["order_id"] == 12104642
|
|
assert len(client.create_v6_calls) == 1
|
|
|
|
|
|
def test_precondition_failure_does_not_create_order():
|
|
client = _Client([_state(full=True, precondition=False)])
|
|
|
|
result = exchange.prepare_exchange(
|
|
client,
|
|
uid=1199669523011,
|
|
cookie="yyuid=1199669523011",
|
|
pid=1199650619883,
|
|
spu_id="hy-8315982",
|
|
)
|
|
|
|
assert result.status == exchange.EXCHANGE_STATUS_FAILED
|
|
assert "前置条件" in result.message
|
|
assert client.create_v6_calls == []
|
|
|
|
|
|
def test_runner_uses_points_exchange_service_for_direct_redeem(monkeypatch):
|
|
client = _Client([_state(full=True)])
|
|
runner = HuyaBatchRunner(
|
|
db=None,
|
|
batch_id="exchange-direct",
|
|
task_type="exchange_goods",
|
|
payload={"product_id": "hy-8315982"},
|
|
)
|
|
marks = []
|
|
monkeypatch.setattr(runner, "_http_client", lambda: client)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_mark_task",
|
|
lambda _db, _task, status, message, result=None: marks.append(
|
|
(status, message, result)
|
|
),
|
|
)
|
|
account = HuyaAccount(uid="1199669523011", cookie="yyuid=1199669523011")
|
|
|
|
runner._execute_exchange_goods(
|
|
None,
|
|
SimpleNamespace(id=1),
|
|
account,
|
|
{"uid": 1199669523011, "cookie": "yyuid=1199669523011"},
|
|
{"room_pid": "1199650619883", "pay_channel": "Zfb"},
|
|
)
|
|
|
|
assert marks[-1][0] == "success"
|
|
assert marks[-1][2]["exchange_mode"] == "points_only"
|
|
assert marks[-1][2]["exchange_order"]["order_id"] == 12104642
|
|
assert account.status == "goods_exchanged"
|
|
assert len(client.create_v6_calls) == 1
|
|
|
|
|
|
def test_runner_waits_points_payment_then_redeems(monkeypatch):
|
|
client = _Client(
|
|
[
|
|
_state(
|
|
full=False,
|
|
plans=[
|
|
{
|
|
"tag0": 8327356,
|
|
"tag1": 7759407,
|
|
"tag2": "精英币",
|
|
"tag5": 1,
|
|
"tag6": 10,
|
|
"tag7": 20,
|
|
"tag9": 2,
|
|
}
|
|
],
|
|
),
|
|
_state(full=True),
|
|
]
|
|
)
|
|
runner = HuyaBatchRunner(
|
|
db=None,
|
|
batch_id="exchange-pay",
|
|
task_type="exchange_goods",
|
|
payload={"product_id": "hy-8315982"},
|
|
)
|
|
marks = []
|
|
progress = []
|
|
monkeypatch.setattr(runner, "_http_client", lambda: client)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_mark_task",
|
|
lambda _db, _task, status, message, result=None: marks.append(
|
|
(status, message, result)
|
|
),
|
|
)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_update_task_progress",
|
|
lambda _db, _task, status, message, result=None: progress.append(
|
|
(status, message, result)
|
|
),
|
|
)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_wait_recharge_payment",
|
|
lambda **_kwargs: ("paid", {"order_id": 12095620, "pay_time": 1}),
|
|
)
|
|
monkeypatch.setattr(
|
|
exchange,
|
|
"run_open_flow",
|
|
lambda *_args, **_kwargs: ShopOrderOutcome(
|
|
ok=True,
|
|
message="已生成支付二维码",
|
|
order_id=12095620,
|
|
pay_url="https://openapi.alipay.com/gateway.do?probe=1",
|
|
amount=200,
|
|
product_name="精英币",
|
|
count=20,
|
|
),
|
|
)
|
|
account = HuyaAccount(uid="1199669523011", cookie="yyuid=1199669523011")
|
|
|
|
runner._execute_exchange_goods(
|
|
None,
|
|
SimpleNamespace(id=2),
|
|
account,
|
|
{"uid": 1199669523011, "cookie": "yyuid=1199669523011"},
|
|
{
|
|
"room_pid": "1199650619883",
|
|
"pay_channel": "Zfb",
|
|
"sid": "2203",
|
|
},
|
|
)
|
|
|
|
assert progress[0][0] == "running"
|
|
assert progress[0][2]["exchange_mode"] == "buy_points_then_exchange"
|
|
assert progress[0][2]["points_purchase"]["order_id"] == 12095620
|
|
assert marks[-1][0] == "success"
|
|
assert marks[-1][2]["exchange_order"]["order_id"] == 12104642
|
|
assert account.points == 20
|
|
|
|
|
|
def test_runner_refresh_goods_uses_points_center_query(monkeypatch):
|
|
from web.backend.services import huya_collab_service
|
|
|
|
runner = HuyaBatchRunner(
|
|
db=None,
|
|
batch_id="refresh-points-goods",
|
|
task_type="refresh_goods",
|
|
)
|
|
marks = []
|
|
added = []
|
|
calls = []
|
|
|
|
class _Query:
|
|
def filter(self, *_args, **_kwargs):
|
|
return self
|
|
|
|
def delete(self, **_kwargs):
|
|
return 0
|
|
|
|
class _DB:
|
|
def query(self, *_args, **_kwargs):
|
|
return _Query()
|
|
|
|
def add(self, item):
|
|
added.append(item)
|
|
|
|
monkeypatch.setattr(
|
|
huya_collab_service,
|
|
"fetch_points_center_gifts",
|
|
lambda uid, cookie, **kwargs: (
|
|
calls.append((uid, cookie, kwargs))
|
|
or (
|
|
[
|
|
{
|
|
"product_id": "hy-8315982",
|
|
"name": "幸运币礼包(小)",
|
|
"price": 20,
|
|
"remain_text": "11197",
|
|
"stock": 11197,
|
|
}
|
|
],
|
|
"已拉取 1 个礼包",
|
|
)
|
|
),
|
|
)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_mark_task",
|
|
lambda _db, _task, status, message, result=None: marks.append(
|
|
(status, message, result)
|
|
),
|
|
)
|
|
account = SimpleNamespace(
|
|
id=8,
|
|
uid="1199669523011",
|
|
cookie="yyuid=1199669523011",
|
|
points=0,
|
|
status="",
|
|
updated_at=None,
|
|
)
|
|
|
|
runner._execute_refresh_goods(
|
|
_DB(),
|
|
SimpleNamespace(id=9, batch_id="refresh-points-goods", result=None),
|
|
account,
|
|
{"uid": 1199669523011, "cookie": "yyuid=1199669523011"},
|
|
{
|
|
"sid": "2203",
|
|
"points_page_code": "heping_baodian_credit_center_page",
|
|
"points_source_id": "yellowcarlist",
|
|
"points_scene": "7",
|
|
},
|
|
)
|
|
|
|
assert calls[0][2]["page_code"] == "heping_baodian_credit_center_page"
|
|
assert calls[0][2]["source_id"] == "yellowcarlist"
|
|
assert len(added) == 1
|
|
assert added[0].product_id == "hy-8315982"
|
|
assert marks[-1][2]["source"] == "PlayMallUI.queryAllocation"
|
|
|
|
|
|
def test_exchange_record_query_discovers_current_sid(monkeypatch):
|
|
record_calls = []
|
|
|
|
class _PointsClient:
|
|
def play_mall_query_allocation(self, **_kwargs):
|
|
return SimpleNamespace(
|
|
code=200,
|
|
payload={
|
|
"tag2": [
|
|
{
|
|
"tag2_2": {
|
|
"tag0": {
|
|
"tag7": 2277,
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
)
|
|
|
|
def get_user_prize_records(self, **kwargs):
|
|
record_calls.append(kwargs)
|
|
return SimpleNamespace(
|
|
status=200,
|
|
msg="请求成功",
|
|
to_dict=lambda: {
|
|
"status": 200,
|
|
"msg": "请求成功",
|
|
"records": [
|
|
{
|
|
"record_id": 9922423,
|
|
"prize_name": "幸运币礼包(小)",
|
|
"score": 20,
|
|
"exchange_time": 1794564026,
|
|
}
|
|
],
|
|
},
|
|
)
|
|
|
|
runner = HuyaBatchRunner(
|
|
db=None,
|
|
batch_id="exchange-records",
|
|
task_type="query_exchange_records",
|
|
)
|
|
marks = []
|
|
monkeypatch.setattr(runner, "_http_client", _PointsClient)
|
|
monkeypatch.setattr(
|
|
runner,
|
|
"_mark_task",
|
|
lambda _db, _task, status, message, result=None: marks.append(
|
|
(status, message, result)
|
|
),
|
|
)
|
|
account = SimpleNamespace(status="", updated_at=None)
|
|
|
|
runner._execute_query_exchange_records(
|
|
None,
|
|
SimpleNamespace(id=3),
|
|
account,
|
|
{"uid": 1199669523011, "cookie": "yyuid=1199669523011"},
|
|
{
|
|
"sid": "2203",
|
|
"points_page_code": "heping_baodian_credit_center_page",
|
|
"points_source_id": "yellowcarlist",
|
|
"points_scene": "7",
|
|
},
|
|
)
|
|
|
|
assert record_calls[0]["sid"] == 2277
|
|
assert marks[-1][0] == "success"
|
|
assert marks[-1][2]["sid"] == 2277
|
|
assert marks[-1][2]["sid_source"] == "queryAllocation"
|