2026-09-09 22:05:00 +08:00
|
|
|
|
"""
|
2026-09-18 13:50:19 +08:00
|
|
|
|
首页 E2E(INV-017 八入口固定 / INV-016 Banner 禁外链 / 首页结构固定)。
|
2026-09-18 11:49:18 +08:00
|
|
|
|
|
2026-09-18 13:50:19 +08:00
|
|
|
|
这些断言打的是**真实活栈**:`GET /api/v1/home` 的真实响应,不是常量、不是自造字面量。
|
2026-09-09 22:05:00 +08:00
|
|
|
|
"""
|
2026-09-18 13:50:19 +08:00
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
pytestmark = pytest.mark.e2e
|
|
|
|
|
|
|
|
|
|
|
|
# AGENTS.md 硬规则 #14/#15:首页 8 核心入口固定,顺序不可更改、运营不可重排。
|
|
|
|
|
|
EXPECTED_QUICK_ENTRIES = [
|
|
|
|
|
|
"find_object", # 第一排
|
|
|
|
|
|
"matchmaker",
|
|
|
|
|
|
"activities",
|
|
|
|
|
|
"counseling",
|
|
|
|
|
|
"classroom", # 第二排
|
|
|
|
|
|
"premarital",
|
|
|
|
|
|
"anti_fraud",
|
|
|
|
|
|
"service_station",
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# INV-016:Banner 仅内部跳转,target_type 白名单(与生产 HomeBannerOperatorController 一致)。
|
|
|
|
|
|
ALLOWED_BANNER_TARGETS = {
|
|
|
|
|
|
"activity", "matchmaker", "counseling", "premarital",
|
|
|
|
|
|
"service_location", "article", "internal_page", "none",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_home_returns_fixed_sections(api, user_token):
|
|
|
|
|
|
"""规则 #14:首页区块齐备(缺一个即结构被改)。"""
|
|
|
|
|
|
data = api.ok("GET", "/home", token=user_token)
|
|
|
|
|
|
for key in ("quickEntries", "banners", "userState", "capabilities", "safetyNotice"):
|
|
|
|
|
|
assert key in data, f"首页缺少区块 {key},实际 keys={sorted(data.keys())}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_quick_entries_fixed_order(api, user_token):
|
|
|
|
|
|
"""INV-017:8 个核心入口固定且顺序不可更改。"""
|
|
|
|
|
|
data = api.ok("GET", "/home", token=user_token)
|
|
|
|
|
|
codes = [e.get("code") for e in data["quickEntries"]]
|
|
|
|
|
|
assert codes == EXPECTED_QUICK_ENTRIES, f"首页入口顺序被改动:{codes}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_banner_target_types_are_internal_only(api, user_token):
|
|
|
|
|
|
"""INV-016:Banner 不允许外链(target_type 必须落在白名单内)。"""
|
|
|
|
|
|
data = api.ok("GET", "/home", token=user_token)
|
|
|
|
|
|
banners = data.get("banners") or []
|
|
|
|
|
|
for b in banners:
|
|
|
|
|
|
target = b.get("targetType")
|
|
|
|
|
|
assert target in ALLOWED_BANNER_TARGETS, f"Banner 出现非白名单 target_type={target!r}"
|