- VideoAnalysis 组件性能优化:使用 memo/useMemo/useCallback,添加详情缓存和虚拟滚动 - 修复 Ant Design Modal/Descriptions/Table 内文字无法复制的问题 - 新增 AntdProvider 组件,解决 layout.tsx 不能加 'use client' 的问题 - 添加云图 API 参数测试,更新 CLAUDE.md 文档 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
317 lines
11 KiB
Python
317 lines
11 KiB
Python
"""
|
||
Tests for Yuntu API Parameter Format (T-027)
|
||
|
||
根据 doc/temp 的正确格式:
|
||
1. 日期格式: YYYYMMDD (如 20251014),不是 YYYY-MM-DD
|
||
2. Cookie 头: 直接使用 auth_token 完整值
|
||
3. industry_id: 字符串格式 ["20"],不是整数
|
||
4. Cookie 获取: 随机选取任意一组 aadvid/auth_token
|
||
"""
|
||
|
||
import pytest
|
||
from datetime import datetime, date
|
||
from unittest.mock import AsyncMock, patch, MagicMock
|
||
import httpx
|
||
|
||
|
||
class TestYuntuAPIParameterFormat:
|
||
"""验证 API 调用参数格式正确性 (T-027)"""
|
||
|
||
async def test_date_format_yyyymmdd(self):
|
||
"""日期格式必须为 YYYYMMDD,不是 YYYY-MM-DD"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {"a3_increase_cnt": "100"}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12",
|
||
aadvid="1648829117232140",
|
||
auth_token="sessionid=f9dfd57df6935afd1255bdc8f0dd0e4b",
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
json_data = call_args.kwargs["json"]
|
||
|
||
# 关键验证:日期格式是 YYYYMMDD
|
||
assert json_data["start_date"] == "20251014", f"Expected '20251014', got '{json_data['start_date']}'"
|
||
assert json_data["end_date"] == "20251113", f"Expected '20251113', got '{json_data['end_date']}'"
|
||
|
||
async def test_cookie_header_uses_auth_token_directly(self):
|
||
"""Cookie 头应直接使用 auth_token 完整值"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
auth_token = "sessionid=f9dfd57df6935afd1255bdc8f0dd0e4b"
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12",
|
||
aadvid="1648829117232140",
|
||
auth_token=auth_token,
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
headers = call_args.kwargs["headers"]
|
||
|
||
# 关键验证:Cookie 直接使用 auth_token 完整值
|
||
assert headers["Cookie"] == auth_token, f"Expected Cookie='{auth_token}', got '{headers['Cookie']}'"
|
||
|
||
async def test_industry_id_as_string_array(self):
|
||
"""industry_id_list 应为字符串数组 ["12"],不是整数"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12", # 字符串
|
||
aadvid="1648829117232140",
|
||
auth_token="sessionid=xxx",
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
json_data = call_args.kwargs["json"]
|
||
|
||
# 关键验证:industry_id_list 是字符串数组
|
||
assert json_data["industry_id_list"] == ["12"], f"Expected ['12'], got {json_data['industry_id_list']}"
|
||
|
||
async def test_url_contains_aadvid(self):
|
||
"""URL 必须包含 aadvid 参数"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
aadvid = "1648829117232140"
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12",
|
||
aadvid=aadvid,
|
||
auth_token="sessionid=xxx",
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
url = call_args.args[0]
|
||
|
||
# 关键验证:URL 包含 aadvid
|
||
assert f"aadvid={aadvid}" in url, f"URL should contain 'aadvid={aadvid}', got '{url}'"
|
||
|
||
async def test_fixed_parameters(self):
|
||
"""验证固定参数值正确"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12",
|
||
aadvid="1648829117232140",
|
||
auth_token="sessionid=xxx",
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
json_data = call_args.kwargs["json"]
|
||
|
||
# 验证固定参数
|
||
assert json_data["is_my_video"] == "0"
|
||
assert json_data["object_type"] == 2
|
||
assert json_data["assist_type"] == 3
|
||
assert json_data["assist_video_type"] == 3
|
||
assert json_data["trigger_point_id_list"] == ["610000", "610300", "610301"]
|
||
|
||
async def test_end_date_is_start_plus_30_days(self):
|
||
"""end_date 应为 start_date + 30 天"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {"status": 0, "data": {}}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
# 测试日期:2025-01-15,+30天 = 2025-02-14
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
await call_yuntu_api(
|
||
item_id="video_001",
|
||
publish_time=datetime(2025, 1, 15),
|
||
industry_id="12",
|
||
aadvid="123",
|
||
auth_token="sessionid=xxx",
|
||
)
|
||
|
||
call_args = mock_client.post.call_args
|
||
json_data = call_args.kwargs["json"]
|
||
|
||
assert json_data["start_date"] == "20250115"
|
||
assert json_data["end_date"] == "20250214"
|
||
|
||
async def test_parse_a3_metrics_as_strings(self):
|
||
"""API 返回的 A3 指标是字符串类型,需正确解析"""
|
||
from app.services.yuntu_api import parse_analysis_response
|
||
|
||
# 实际 API 响应示例(A3 是字符串)
|
||
response = {
|
||
"status": 0,
|
||
"msg": "ok",
|
||
"data": {
|
||
"object_id": "7560751618711457062",
|
||
"cost": 785000,
|
||
"ad_a3_increase_cnt": "36902",
|
||
"natural_a3_increase_cnt": "1652169",
|
||
"a3_increase_cnt": "1689071",
|
||
}
|
||
}
|
||
|
||
result = parse_analysis_response(response)
|
||
|
||
# 解析后应转为整数
|
||
assert result["a3_increase_cnt"] == 1689071
|
||
assert result["ad_a3_increase_cnt"] == 36902
|
||
assert result["natural_a3_increase_cnt"] == 1652169
|
||
assert result["cost"] == 785000
|
||
|
||
|
||
class TestSessionPoolRandomSelection:
|
||
"""验证 Cookie 池随机选取逻辑 (T-027)"""
|
||
|
||
async def test_get_random_config(self):
|
||
"""应随机选取任意一组配置,不按 brand_id 匹配"""
|
||
from app.services.session_pool import SessionPool, CookieConfig
|
||
|
||
pool = SessionPool()
|
||
|
||
# 模拟刷新后的数据
|
||
pool._configs = [
|
||
CookieConfig(
|
||
brand_id="533661",
|
||
aadvid="1648829117232140",
|
||
auth_token="sessionid=aaa",
|
||
industry_id=20,
|
||
brand_name="Test1",
|
||
),
|
||
CookieConfig(
|
||
brand_id="10186612",
|
||
aadvid="1234567890",
|
||
auth_token="sessionid=bbb",
|
||
industry_id=30,
|
||
brand_name="Test2",
|
||
),
|
||
]
|
||
|
||
# 调用随机获取
|
||
config = pool.get_random_config()
|
||
|
||
# 应返回一个有效配置
|
||
assert config is not None
|
||
assert "aadvid" in config
|
||
assert "auth_token" in config
|
||
|
||
|
||
class TestIntegrationWithRealFormat:
|
||
"""集成测试:验证完整调用流程"""
|
||
|
||
async def test_full_api_call_format(self):
|
||
"""完整验证 API 调用格式"""
|
||
from app.services.yuntu_api import call_yuntu_api
|
||
|
||
mock_response = MagicMock()
|
||
mock_response.status_code = 200
|
||
mock_response.json.return_value = {
|
||
"status": 0,
|
||
"msg": "ok",
|
||
"data": {
|
||
"object_id": "7560751618711457062",
|
||
"cost": 785000,
|
||
"ad_a3_increase_cnt": "36902",
|
||
"natural_a3_increase_cnt": "1652169",
|
||
"a3_increase_cnt": "1689071",
|
||
}
|
||
}
|
||
|
||
mock_client = AsyncMock()
|
||
mock_client.post.return_value = mock_response
|
||
mock_client.__aenter__.return_value = mock_client
|
||
mock_client.__aexit__.return_value = None
|
||
|
||
with patch("httpx.AsyncClient", return_value=mock_client):
|
||
result = await call_yuntu_api(
|
||
item_id="7560751618711457062",
|
||
publish_time=datetime(2025, 10, 14),
|
||
industry_id="12",
|
||
aadvid="1648829117232140",
|
||
auth_token="sessionid=f9dfd57df6935afd1255bdc8f0dd0e4b",
|
||
)
|
||
|
||
# 验证调用参数
|
||
call_args = mock_client.post.call_args
|
||
url = call_args.args[0]
|
||
json_data = call_args.kwargs["json"]
|
||
headers = call_args.kwargs["headers"]
|
||
|
||
# 1. URL 包含 aadvid
|
||
assert "aadvid=1648829117232140" in url
|
||
|
||
# 2. 日期格式 YYYYMMDD
|
||
assert json_data["start_date"] == "20251014"
|
||
assert json_data["end_date"] == "20251113"
|
||
|
||
# 3. industry_id 字符串数组
|
||
assert json_data["industry_id_list"] == ["12"]
|
||
|
||
# 4. Cookie 直接使用 auth_token
|
||
assert headers["Cookie"] == "sessionid=f9dfd57df6935afd1255bdc8f0dd0e4b"
|
||
|
||
# 验证返回结果
|
||
assert result["status"] == 0
|
||
assert result["data"]["a3_increase_cnt"] == "1689071"
|