import unittest from unittest.mock import Mock import requests from core.douyu.activity_client import DouyuActivityClient, DouyuActivityError class EliteLockExchangeTests(unittest.TestCase): def setUp(self): self.client = DouyuActivityClient("acf_uid=100") self.client.csrf_token = Mock(side_effect=["csrf-create", "csrf-pay"]) @staticmethod def multipart_values(files): return {key: value[1] for key, value in files.items()} def test_exchange_locks_then_pays_with_fresh_csrf_tokens(self): self.client._request_json = Mock(side_effect=[ { "error": 0, "msg": "请求正常", "data": { "orderId": "5874", "commodityName": "套装-兔兔白日梦", "commodityImage": "https://example.test/item.png", "score": 1280, "num": 1, "expireSeconds": "300", }, }, { "error": 0, "msg": "请求正常", "data": { "orderId": "5874", "exchangeId": "11098933", "commodityType": 3, "commodityImage": "https://example.test/item.png", "exchangeNum": 1, "sType": 1, "gType": 2, }, }, ]) result = self.client.exchange_goods( manual_id="G4KA4Qnz4LDp7", rid="9263298", commodity_id="20260120QYOOB_bag10", ) self.assertEqual(result["order_id"], "5874") self.assertEqual(result["exchange_id"], "11098933") self.assertEqual(self.client.csrf_token.call_count, 2) create_call, pay_call = self.client._request_json.call_args_list self.assertEqual(create_call.args[:3], ( "post", self.client.CREATE_EXCHANGE_ORDER_API, "锁定兑换商品", )) self.assertEqual(self.multipart_values(create_call.kwargs["files"]), { "manualID": "G4KA4Qnz4LDp7", "rid": "9263298", "commodityID": "20260120QYOOB_bag10", "num": "1", "csrfToken": "csrf-create", }) self.assertNotIn("Content-Type", create_call.kwargs["headers"]) self.assertEqual(pay_call.args[:3], ( "post", self.client.PAY_EXCHANGE_ORDER_API, "支付锁单", )) self.assertEqual(self.multipart_values(pay_call.kwargs["files"]), { "orderID": "5874", "manualID": "G4KA4Qnz4LDp7", "csrfToken": "csrf-pay", }) def test_lock_requires_order_id_in_success_response(self): self.client._request_json = Mock(return_value={"error": 0, "data": {}}) with self.assertRaisesRegex(DouyuActivityError, "没有 orderId"): self.client.create_exchange_order( manual_id="manual", rid="9263298", commodity_id="goods", ) def test_locked_order_list_uses_captured_query_shape(self): self.client._request_json = Mock(return_value={ "error": 0, "data": {"total": 1, "list": [{"orderId": "5874"}]}, }) result = self.client.exchange_orders(manual_id="manual", page=2, page_size=20) self.assertEqual(result["total"], 1) self.assertEqual(result["orders"], [{"orderId": "5874"}]) call = self.client._request_json.call_args self.assertEqual(call.args[:3], ( "get", self.client.EXCHANGE_ORDER_LIST_API, "查询锁单", )) self.assertEqual(call.kwargs["params"], { "page": 2, "pageSize": 20, "manualID": "manual", }) def test_redirect_is_not_followed_and_is_reported_without_query_values(self): response = requests.Response() response.status_code = 302 response.url = "https://www.douyu.com/japi/example?request_secret=hidden" response.headers["Location"] = "https://www.douyu.com/member/login?ticket=secret" self.client.session.request = Mock(return_value=response) self.client.logger = Mock() with self.assertRaisesRegex(DouyuActivityError, "Cookie 已失效,请重新登录") as caught: self.client._request( "get", "https://www.douyu.com/japi/example?request_secret=hidden", source="测试活动接口", ) self.client.session.request.assert_called_once() self.assertFalse(self.client.session.request.call_args.kwargs["allow_redirects"]) message = str(caught.exception) self.assertIn("method=GET", message) self.assertIn("url=https://www.douyu.com/japi/example", message) self.assertIn("location=https://www.douyu.com/member/login", message) self.assertNotIn("request_secret", message) self.assertNotIn("ticket=secret", message) self.assertIn("已拦截重定向", self.client.logger.call_args.args[0]) if __name__ == "__main__": unittest.main()