Files
live-hub-py/tests/test_huya_points_exchange.py
T

504 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 import huya_runner_goods
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 _mock_live_goods(
monkeypatch,
*,
product_id: str = "hy-8315982",
name: str = "幸运币礼包(小)",
):
monkeypatch.setattr(
huya_runner_goods,
"fetch_points_center_catalog",
lambda *_args, **_kwargs: SimpleNamespace(
goods=[
{
"product_id": product_id,
"name": name,
"price": 20,
"remain_text": "100",
}
],
message="已拉取 1 个礼包",
),
)
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)])
_mock_live_goods(monkeypatch)
runner = HuyaBatchRunner(
db=None,
batch_id="exchange-direct",
task_type="exchange_goods",
payload={"product_id": "hy-8315982"},
)
marks = []
monkeypatch.setattr(runner, "_http_client", lambda **_kwargs: 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),
]
)
_mock_live_goods(monkeypatch)
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 **_kwargs: 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):
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_runner_goods,
"fetch_points_center_catalog",
lambda uid, cookie, **kwargs: (
calls.append((uid, cookie, kwargs))
or SimpleNamespace(
goods=[
{
"product_id": "hy-8315982",
"name": "幸运币礼包(小)",
"price": 20,
"remain_text": "11197",
"stock": 11197,
}
],
message="已拉取 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"