改版主线: - /review/start: 从系统运营页改成用户结果页(不再暴露 JSON/go_step1) - 首页:省份改下拉,表单压缩为 3 字段并移出 hero 核心区 - hero: 副文案压缩为一句,去掉三张说明卡与了解流程链接 - trust strip: 4→3,右侧判断块压缩为一句风险+上传能力 - browser_vision 复验确认:说明书感已显著下降,结构已从功能堆叠过渡到决策引导 测试基线同步: - test_web_public.py: 首页副文案/流程编号/说明卡断言更新 - test_web_public_review_flow.py: 复核页断言改为用户视角文案 文档: - docs/FRONTEND_PRODUCTION_REMEDIATION_PLAN_2026-06-26.md 记录整改真相源
1020 lines
36 KiB
Python
1020 lines
36 KiB
Python
"""用户端复核、冲稳保、报告与工作台链路测试。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from data.orders.dao import OrdersDAO
|
|
|
|
|
|
def test_landing_page_provides_review_entry_source_link(client):
|
|
resp = client.get("/")
|
|
assert resp.status_code == 200, resp.text
|
|
assert 'href="/review/start?source=home"' in resp.text
|
|
|
|
|
|
def test_landing_page_review_consult_form_targets_review_start(client):
|
|
resp = client.get("/")
|
|
assert resp.status_code == 200, resp.text
|
|
assert 'form action="/review/start" method="get"' in resp.text
|
|
assert 'name="source" value="home"' in resp.text
|
|
|
|
|
|
def test_landing_page_uses_review_as_workspace_primary_action_when_step1_complete(
|
|
client, settings
|
|
):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="submit",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
consent_version="portal-v1",
|
|
consent_scope="step1",
|
|
privacy_accepted=True,
|
|
service_terms_accepted=True,
|
|
guardian_confirmed=True,
|
|
),
|
|
settings,
|
|
)
|
|
|
|
landing = client.get(f"/?token={token}")
|
|
assert landing.status_code == 200, landing.text
|
|
assert "工作台主动作" in landing.text
|
|
assert "开始方案复核" in landing.text
|
|
assert f"/review/start?source=home&token={token}" in landing.text
|
|
|
|
|
|
def test_review_start_page_renders_user_facing_result_not_ops_process_page(
|
|
client, settings
|
|
):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
existing_plan_summary="已有一版志愿方案,想先看有没有明显风险",
|
|
),
|
|
settings,
|
|
)
|
|
|
|
resp = client.get(f"/review/start?source=status&token={token}")
|
|
assert resp.status_code == 200, resp.text
|
|
# 新口径:这是用户结果页,不是系统入口/流程说明页
|
|
assert "复核结果" in resp.text or "初步评估结果" in resp.text
|
|
assert "方案复核入口" not in resp.text
|
|
assert "审核输入" not in resp.text
|
|
assert "最小约束" not in resp.text
|
|
assert "审核输出摘要" not in resp.text
|
|
assert "下一步分流" not in resp.text
|
|
# 必须保留用户可理解的结果与下一步
|
|
assert "风险等级" in resp.text
|
|
assert "核心问题" in resp.text
|
|
assert "下一步建议" in resp.text
|
|
assert "已有一版志愿方案,想先看有没有明显风险" in resp.text
|
|
assert "物理 / 化学 / 生物" in resp.text
|
|
# 不得暴露内部 JSON / review_result_id / action slug
|
|
assert "review_result_id" not in resp.text
|
|
assert '"risk_level"' not in resp.text
|
|
assert "go_step1" not in resp.text
|
|
assert "go_cwb" not in resp.text
|
|
assert "go_full_plan" not in resp.text
|
|
|
|
|
|
def test_status_page_provides_review_entry_source_link(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
resp = client.get(f"/portal/{token}/status")
|
|
assert resp.status_code == 200, resp.text
|
|
assert f"/review/start?source=status&token={token}" in resp.text
|
|
|
|
|
|
def test_report_page_provides_review_entry_source_link(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
dao.transition_status(
|
|
body["order_id"], "serving", actor="test", reason="seed_report_serving"
|
|
)
|
|
dao.transition_status(
|
|
body["order_id"], "delivered", actor="test", reason="seed_report_ready"
|
|
)
|
|
dao.update(
|
|
body["order_id"],
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="seed_report_ready_paths",
|
|
)
|
|
|
|
resp = client.get(f"/portal/{token}/report")
|
|
assert resp.status_code == 200, resp.text
|
|
assert "/review/start?source=report&token=" in resp.text
|
|
|
|
|
|
def test_review_start_persists_result_contract_and_source(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
resp = client.get(f"/review/start?source=status&token={token}")
|
|
assert resp.status_code == 200, resp.text
|
|
# 页面已改为用户结果页,不再暴露内部 JSON 字段
|
|
assert "复核结果" in resp.text or "初步评估结果" in resp.text
|
|
assert "风险等级" in resp.text
|
|
assert "核心问题" in resp.text
|
|
assert "下一步建议" in resp.text
|
|
# 不再暴露内部 action slug
|
|
assert "go_step1" not in resp.text
|
|
assert "go_cwb" not in resp.text
|
|
assert "go_full_plan" not in resp.text
|
|
|
|
|
|
def test_review_action_updates_followup_action(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=status&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "step1"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
assert action.headers["location"].endswith(f"/portal/{token}/info")
|
|
|
|
|
|
def test_review_action_cwb_redirects_to_real_cwb_page(route_client, client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=status&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "cwb"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
assert action.headers["location"].endswith(f"/portal/{token}/cwb")
|
|
cwb = client.get(action.headers["location"])
|
|
assert cwb.status_code == 200, cwb.text
|
|
assert "冲稳保建议页" in cwb.text
|
|
assert "当前建议" in cwb.text
|
|
assert "冲刺建议" in cwb.text
|
|
|
|
|
|
def test_cwb_page_is_first_class_three_column_workspace(route_client, client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=status&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "cwb"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
|
|
cwb = client.get(f"/portal/{token}/cwb")
|
|
assert cwb.status_code == 200, cwb.text
|
|
assert "冲稳保建议页" in cwb.text
|
|
assert "冲刺建议" in cwb.text
|
|
assert "稳妥建议" in cwb.text
|
|
assert "保底建议" in cwb.text
|
|
assert "当前建议" in cwb.text
|
|
|
|
|
|
def test_cwb_page_exposes_review_summary_and_next_actions(
|
|
route_client, client, settings
|
|
):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
client.get(f"/review/start?source=report&token={token}")
|
|
client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "cwb"},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
cwb = client.get(f"/portal/{token}/cwb")
|
|
assert cwb.status_code == 200, cwb.text
|
|
assert "当前复核摘要" in cwb.text
|
|
assert "下一步建议" in cwb.text
|
|
assert f"/portal/{token}/full-plan" in cwb.text
|
|
assert f"/portal/{token}/status" in cwb.text
|
|
|
|
|
|
def test_cwb_page_no_longer_claims_placeholder_future_work(
|
|
route_client, client, settings
|
|
):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
client.get(f"/review/start?source=status&token={token}")
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "cwb"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
|
|
cwb = client.get(action.headers["location"])
|
|
assert cwb.status_code == 200, cwb.text
|
|
assert "后续再接真实推荐结果" not in cwb.text
|
|
assert "建议冲刺" in cwb.text or "冲刺建议" in cwb.text
|
|
|
|
|
|
def test_cwb_page_links_policy_same_score_and_auxiliary_factors(client, settings):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
payment_id = create_resp.json()["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
target_cities=["长沙", "深圳"],
|
|
family_background="家长更希望省内优先",
|
|
interest_assessment_type="holland",
|
|
interest_assessment_result="R型+I型",
|
|
interest_assessment_notes="只作辅助,不作唯一判断",
|
|
),
|
|
settings,
|
|
)
|
|
client.get(f"/review/start?source=status&token={token}")
|
|
client.post(
|
|
"/review/action", data={"token": token, "action": "cwb"}, follow_redirects=False
|
|
)
|
|
|
|
cwb = client.get(f"/portal/{token}/cwb")
|
|
assert cwb.status_code == 200, cwb.text
|
|
assert "/policy-center?province=湖南" in cwb.text
|
|
assert "/same-score-reference?province=湖南" in cwb.text and "score=578" in cwb.text
|
|
assert "辅助判断因子" in cwb.text
|
|
assert "R型+I型" in cwb.text
|
|
assert "家长更希望省内优先" in cwb.text
|
|
|
|
|
|
def test_review_action_full_plan_redirects_to_real_full_plan_page(
|
|
route_client, client, settings
|
|
):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=status&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "full_plan"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
assert action.headers["location"].endswith(f"/portal/{token}/full-plan")
|
|
|
|
full_plan = client.get(action.headers["location"])
|
|
assert full_plan.status_code == 200, full_plan.text
|
|
assert "完整规划建议页" in full_plan.text
|
|
assert "方案优先级" in full_plan.text
|
|
assert "版本历史" in full_plan.text
|
|
|
|
|
|
def test_full_plan_page_shows_profile_versions_and_assessment_context(
|
|
route_client, client, settings
|
|
):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
payment_id = create_resp.json()["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
family_background="家长更希望省内优先",
|
|
interest_assessment_type="mbti",
|
|
interest_assessment_result="INTJ",
|
|
interest_assessment_notes="只作辅助,不作唯一判断",
|
|
),
|
|
settings,
|
|
)
|
|
client.get(f"/review/start?source=status&token={token}")
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "full_plan"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
|
|
full_plan = client.get(action.headers["location"])
|
|
assert full_plan.status_code == 200, full_plan.text
|
|
assert "完整规划建议页" in full_plan.text
|
|
assert "方案优先级" in full_plan.text
|
|
assert "版本历史" in full_plan.text
|
|
assert "初始档案方案" in full_plan.text
|
|
assert "辅助判断因子" in full_plan.text
|
|
assert "INTJ" in full_plan.text
|
|
|
|
|
|
def test_full_plan_page_no_longer_claims_entry_only_placeholder(
|
|
route_client, client, settings
|
|
):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
payment_id = create_resp.json()["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
),
|
|
settings,
|
|
)
|
|
client.get(f"/review/start?source=status&token={token}")
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "full_plan"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
|
|
full_plan = client.get(action.headers["location"])
|
|
assert full_plan.status_code == 200, full_plan.text
|
|
assert "当前从 review 分流进入完整规划入口" not in full_plan.text
|
|
assert "方案优先级" in full_plan.text or "规划建议" in full_plan.text
|
|
|
|
|
|
def test_report_page_shows_version_summary_and_next_actions(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
client.get(f"/review/start?source=report&token={token}")
|
|
client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "full_plan"},
|
|
follow_redirects=False,
|
|
)
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
dao.transition_status(
|
|
body["order_id"], "serving", actor="test", reason="report_ready_serving"
|
|
)
|
|
dao.transition_status(
|
|
body["order_id"], "delivered", actor="test", reason="report_ready_delivered"
|
|
)
|
|
dao.update(
|
|
body["order_id"],
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="report_ready_paths",
|
|
)
|
|
|
|
report_page = client.get(f"/portal/{token}/report")
|
|
assert report_page.status_code == 200, report_page.text
|
|
assert "报告版本" in report_page.text
|
|
assert "基于哪个档案版本" in report_page.text
|
|
assert "当前复核摘要" in report_page.text
|
|
assert "下一步建议" in report_page.text
|
|
assert "/portal/" in report_page.text and "/full-plan" in report_page.text
|
|
|
|
|
|
def test_report_page_routes_followup_step1_to_info(client, settings):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
payment_id = create_resp.json()["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
),
|
|
settings,
|
|
)
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
order_id = create_resp.json()["order_id"]
|
|
dao.transition_status(
|
|
order_id, "serving", actor="test", reason="report_ready_serving"
|
|
)
|
|
dao.transition_status(
|
|
order_id, "delivered", actor="test", reason="report_ready_delivered"
|
|
)
|
|
dao.update(
|
|
order_id,
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="report_ready_paths",
|
|
)
|
|
|
|
client.get(f"/review/start?source=report&token={token}")
|
|
action = client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "step1"},
|
|
follow_redirects=False,
|
|
)
|
|
assert action.status_code == 303, action.text
|
|
|
|
report = client.get(f"/portal/{token}/report")
|
|
assert report.status_code == 200, report.text
|
|
assert f"/portal/{token}/info" in report.text
|
|
|
|
|
|
def test_review_result_is_saved_as_independent_lightweight_object(client, settings):
|
|
from data.orders.intake_store import IntakeStore
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
resp = client.get(f"/review/start?source=home&token={token}")
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
intake_store = IntakeStore.for_db(settings.orders_db_path)
|
|
try:
|
|
intake = intake_store.get(body["order_id"])
|
|
finally:
|
|
intake_store.close()
|
|
|
|
assert intake is not None
|
|
assert "review_result_contract" not in intake.payload
|
|
assert "latest_review_result_id" in intake.payload
|
|
assert intake.payload["latest_review_result_id"].startswith("rvw_")
|
|
|
|
|
|
def test_review_result_has_version_anchor_and_history_summary(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
first = client.get(f"/review/start?source=home&token={token}")
|
|
assert first.status_code == 200, first.text
|
|
|
|
second = client.get(f"/review/start?source=status&token={token}")
|
|
assert second.status_code == 200, second.text
|
|
|
|
landing = client.get(f"/?token={token}")
|
|
assert landing.status_code == 200, landing.text
|
|
assert "最近一次复核结果" in landing.text
|
|
assert "最新版本" in landing.text or "版本" in landing.text
|
|
|
|
|
|
def test_report_page_shows_profile_and_review_versions(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
client.get(f"/review/start?source=home&token={token}")
|
|
client.get(f"/review/start?source=status&token={token}")
|
|
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
dao.transition_status(
|
|
body["order_id"], "serving", actor="test", reason="report_ready_serving"
|
|
)
|
|
dao.transition_status(
|
|
body["order_id"], "delivered", actor="test", reason="report_ready_delivered"
|
|
)
|
|
dao.update(
|
|
body["order_id"],
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="report_ready_paths",
|
|
)
|
|
|
|
report_page = client.get(f"/portal/{token}/report")
|
|
assert report_page.status_code == 200, report_page.text
|
|
assert "报告版本" in report_page.text
|
|
assert "基于哪个档案版本" in report_page.text
|
|
assert "当前复核摘要" in report_page.text
|
|
assert "最新版本" in report_page.text or "版本历史" in report_page.text
|
|
|
|
|
|
def test_report_page_shows_latest_profile_state_and_helper_links(client, settings):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
target_cities=["长沙", "深圳"],
|
|
interest_assessment_type="mbti",
|
|
interest_assessment_result="INTJ",
|
|
interest_assessment_notes="只作辅助,不作唯一判断",
|
|
),
|
|
settings,
|
|
)
|
|
client.get(f"/review/start?source=report&token={token}")
|
|
client.post(
|
|
"/review/action",
|
|
data={"token": token, "action": "full_plan"},
|
|
follow_redirects=False,
|
|
)
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
dao.transition_status(
|
|
body["order_id"], "serving", actor="test", reason="report_ready_serving"
|
|
)
|
|
dao.transition_status(
|
|
body["order_id"], "delivered", actor="test", reason="report_ready_delivered"
|
|
)
|
|
dao.update(
|
|
body["order_id"],
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="report_ready_paths",
|
|
)
|
|
|
|
report_page = client.get(f"/portal/{token}/report")
|
|
assert report_page.status_code == 200, report_page.text
|
|
assert "基于最新档案生成" in report_page.text
|
|
assert "/policy-center?province=湖南" in report_page.text
|
|
assert (
|
|
"/same-score-reference?province=湖南" in report_page.text
|
|
and "score=578" in report_page.text
|
|
)
|
|
assert "辅助判断因子" in report_page.text
|
|
assert "INTJ" in report_page.text
|
|
|
|
|
|
def test_report_page_warns_when_based_on_historical_profile_version(client, settings):
|
|
from admin.routes.web_public import submit_order_info
|
|
from data.orders.intake_schema import IntakePayload
|
|
from data.orders.intake_store import IntakeStore
|
|
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
body = create_resp.json()
|
|
payment_id = body["checkout_url"].split("/pay/mock/")[1].split("?")[0]
|
|
token = body["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
paid = client.post(f"/pay/mock/{payment_id}/complete", follow_redirects=False)
|
|
assert paid.status_code == 303, paid.text
|
|
|
|
submit_order_info(
|
|
token,
|
|
IntakePayload(
|
|
mode="draft",
|
|
candidate_province="湖南",
|
|
candidate_subjects=["物理", "化学", "生物"],
|
|
candidate_score=578,
|
|
candidate_rank=12345,
|
|
),
|
|
settings,
|
|
)
|
|
with OrdersDAO.connect(settings.orders_db_path) as dao:
|
|
dao.transition_status(
|
|
body["order_id"], "serving", actor="test", reason="report_ready_serving"
|
|
)
|
|
dao.transition_status(
|
|
body["order_id"], "delivered", actor="test", reason="report_ready_delivered"
|
|
)
|
|
dao.update(
|
|
body["order_id"],
|
|
{
|
|
"audit_report": "data/examples/gaokao_report_李明_20260611_122443.html",
|
|
"pdf_path": "data/examples/gaokao_report_李明_20260611_122443.pdf",
|
|
},
|
|
actor="test",
|
|
reason="report_ready_paths",
|
|
)
|
|
|
|
first = client.get(f"/portal/{token}/report")
|
|
assert first.status_code == 200, first.text
|
|
|
|
intake_store = IntakeStore.for_db(settings.orders_db_path)
|
|
try:
|
|
intake = intake_store.get(body["order_id"])
|
|
assert intake is not None
|
|
payload = dict(intake.payload)
|
|
payload["candidate_score"] = 579
|
|
payload["candidate_rank"] = 12001
|
|
payload["profile_versions"] = [
|
|
{
|
|
"profile_version_id": "profile_v1",
|
|
"stage_label": "初始档案方案",
|
|
"snapshot_payload": {
|
|
"candidate_province": "湖南",
|
|
"candidate_subjects": ["物理", "化学", "生物"],
|
|
"candidate_score": 578,
|
|
"candidate_rank": 12345,
|
|
},
|
|
"created_at": "2026-06-23T00:00:00+00:00",
|
|
"source": "portal",
|
|
},
|
|
{
|
|
"profile_version_id": "profile_v2",
|
|
"stage_label": "查分后校准方案",
|
|
"snapshot_payload": {
|
|
"candidate_province": "湖南",
|
|
"candidate_subjects": ["物理", "化学", "生物"],
|
|
"candidate_score": 579,
|
|
"candidate_rank": 12001,
|
|
},
|
|
"created_at": "2026-06-23T00:00:00+00:00",
|
|
"source": "admin",
|
|
},
|
|
]
|
|
payload["latest_profile_version_id"] = "profile_v2"
|
|
intake_store.save(order_id=body["order_id"], payload=payload, submit=True)
|
|
finally:
|
|
intake_store.close()
|
|
|
|
report_page = client.get(f"/portal/{token}/report")
|
|
assert report_page.status_code == 200, report_page.text
|
|
assert "基于历史档案版本生成,建议刷新" in report_page.text
|
|
|
|
|
|
def test_landing_page_shows_recent_review_result_entry_when_present(client, settings):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=home&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
|
|
landing = client.get(f"/?token={token}")
|
|
assert landing.status_code == 200, landing.text
|
|
assert "最近一次复核结果" in landing.text
|
|
assert f"/review/start?source=home&token={token}" in landing.text
|
|
|
|
|
|
def test_landing_page_shows_workspace_primary_action_for_incomplete_step1(client):
|
|
resp = client.get("/?token=test-token")
|
|
assert resp.status_code == 200, resp.text
|
|
assert "工作台主动作" in resp.text
|
|
assert "继续补充 Step 1" in resp.text
|
|
|
|
|
|
def test_landing_page_shows_workspace_primary_action_for_recent_review_result(
|
|
route_client, client, settings
|
|
):
|
|
create_resp = client.post(
|
|
"/api/public/orders",
|
|
json={
|
|
"service_version": "standard",
|
|
"amount_cents": 9900,
|
|
"customer_phone": "13800138000",
|
|
"candidate_name": "张三",
|
|
"candidate_province": "湖南",
|
|
},
|
|
)
|
|
assert create_resp.status_code == 201, create_resp.text
|
|
token = (
|
|
create_resp.json()["portal_status_url"].split("/portal/")[1].split("/status")[0]
|
|
)
|
|
|
|
start = client.get(f"/review/start?source=home&token={token}")
|
|
assert start.status_code == 200, start.text
|
|
|
|
landing = client.get(f"/?token={token}")
|
|
assert landing.status_code == 200, landing.text
|
|
assert "工作台主动作" in landing.text
|
|
assert "继续查看最近一次复核" in landing.text
|
|
assert f"/review/start?source=home&token={token}" in landing.text
|