Files
gaokao-volunteer-system/admin/tests/test_web_public.py
Hermes Agent c03ed7a4ba
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
feat(web): tighten portal privacy and cleanup flows
2026-06-15 21:24:55 +08:00

274 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""用户端 Web 自助入口页面测试T12.2/T12.3)。"""
from __future__ import annotations
from fastapi.testclient import TestClient
from data.orders.dao import OrdersDAO
def test_public_landing_page_served(client):
resp = client.get("/")
assert resp.status_code == 200
assert "text/html" in resp.headers["content-type"]
body = resp.text
assert "高考志愿填报智能系统" in body
assert "用户端 Web 自助服务" in body
assert 'href="/pricing"' in body
assert "49元 AI方案审核" in body
def test_public_pricing_page_served(client):
resp = client.get("/pricing")
assert resp.status_code == 200
assert "text/html" in resp.headers["content-type"]
body = resp.text
assert "服务套餐" in body
assert "49元 AI方案审核" in body
assert "99元 完整志愿方案" in body
assert "199元 深度辅导版" in body
assert 'data-package="audit"' in body
assert 'data-package="standard"' in body
assert 'data-package="premium"' in body
assert "支付接入建设中" in body
def test_public_create_order_endpoint(client):
resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 9900,
"customer_name": "张家长",
"customer_phone": "13800138000",
"customer_email": "parent@example.com",
"candidate_name": "张三",
"candidate_province": "湖南",
},
)
assert resp.status_code == 201, resp.text
body = resp.json()
assert body["order_id"].startswith("GKO-")
assert body["source"] == "web"
assert body["status"] == "pending"
assert body["service_version"] == "standard"
assert body["amount_cents"] == 9900
assert body["next_step"] == "payment"
assert body["checkout_url"].startswith("/pay/mock/")
assert "/portal/" in body["portal_status_url"]
with OrdersDAO.connect(client.app.state.settings.orders_db_path) as dao:
created = dao.get(body["order_id"])
assert created.customer_email == "parent@example.com"
def test_public_create_order_rejects_missing_contact(client):
resp = client.post(
"/api/public/orders",
json={
"service_version": "audit",
"amount_cents": 4900,
"customer_name": "李家长",
"candidate_province": "广东",
},
)
assert resp.status_code == 422
body = resp.json()
assert body["message"] == "请求数据未通过校验"
assert (
body["detail"]["fields"][0]["reason"]
== "Value error, customer_phone / customer_wechat 至少填写一个"
)
def test_public_create_order_rejects_price_tampering(client):
resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 1,
"customer_name": "张家长",
"customer_phone": "13800138000",
"customer_email": "parent@example.com",
"candidate_province": "湖南",
},
)
assert resp.status_code == 422
body = resp.json()
assert body["message"] == "请求数据未通过校验"
assert (
body["detail"]["fields"][0]["reason"]
== "Value error, amount_cents 与套餐价格不一致"
)
def test_mock_payment_complete_rejects_wrong_portal_token(client):
create_resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 9900,
"customer_name": "张家长",
"customer_phone": "13800138000",
"customer_email": "parent@example.com",
"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]
resp = client.post(f"/pay/mock/{payment_id}/complete?token=wrong-token")
assert resp.status_code == 401 or resp.status_code == 403
def test_payment_return_redirects_to_portal_status(client):
create_resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 9900,
"customer_name": "张家长",
"customer_phone": "13800138000",
"customer_email": "parent@example.com",
"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"/portal/payment-return?token={token}", follow_redirects=False)
assert resp.status_code == 303, resp.text
assert resp.headers["location"] == f"/portal/{token}/status"
def test_public_pages_include_privacy_and_deletion_links(client):
landing = client.get("/")
assert landing.status_code == 200, landing.text
assert 'href="/privacy"' in landing.text
assert 'href="/service-terms"' in landing.text
assert 'href="/deletion-policy"' in landing.text
pricing = client.get("/pricing")
assert pricing.status_code == 200, pricing.text
assert 'href="/privacy"' in pricing.text
assert 'href="/service-terms"' in pricing.text
assert 'href="/deletion-policy"' in pricing.text
def test_privacy_and_deletion_pages_are_served(client):
privacy = client.get("/privacy")
assert privacy.status_code == 200, privacy.text
assert "隐私政策" in privacy.text
terms = client.get("/service-terms")
assert terms.status_code == 200, terms.text
assert "服务说明与免责声明" in terms.text
deletion = client.get("/deletion-policy")
assert deletion.status_code == 200, deletion.text
assert "删除申请" in deletion.text
def test_public_create_order_returns_503_without_creating_orphan_order_when_provider_unavailable(
tmp_path, monkeypatch
):
admin_db = tmp_path / "admin.db"
orders_db = tmp_path / "orders.db"
share_db = tmp_path / "share.db"
share_reports = tmp_path / "share_reports"
share_reports.mkdir()
monkeypatch.setenv("GAOKAO_ENV", "dev")
monkeypatch.setenv("GAOKAO_DB_PATH", str(admin_db))
monkeypatch.setenv("GAOKAO_ORDERS_DB_PATH", str(orders_db))
monkeypatch.setenv("GAOKAO_SHARE_DB_PATH", str(share_db))
monkeypatch.setenv("GAOKAO_SHARE_REPORT_DIR", str(share_reports))
monkeypatch.setenv("GAOKAO_ORDERS_FERNET_KEY", "test-secret-for-web-self-service")
monkeypatch.setenv("GAOKAO_JWT_SECRET", "x" * 64)
monkeypatch.setenv("GAOKAO_JWT_EXP_MIN", "5")
monkeypatch.setenv("GAOKAO_ADMIN_USER", "admin")
monkeypatch.setenv("GAOKAO_ADMIN_PASS", "test-pass-123")
monkeypatch.setenv("GAOKAO_PAYMENT_PROVIDER", "alipay")
monkeypatch.setenv("GAOKAO_PAYMENT_BASE_URL", "http://testserver")
monkeypatch.setenv("GAOKAO_PAYMENT_WEBHOOK_SECRET", "missing-real-provider-env")
from admin.app import create_app
from admin.config import load_settings
settings = load_settings()
app = create_app(settings)
with TestClient(app) as client:
resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 9900,
"customer_name": "张家长",
"customer_phone": "13800138000",
"candidate_province": "湖南",
},
)
assert resp.status_code == 503, resp.text
assert "payment provider unavailable" in resp.text
with OrdersDAO.connect(settings.orders_db_path) as dao:
assert dao.count() == 0
def test_public_create_order_returns_503_without_creating_orphan_order_when_checkout_fails(
tmp_path, monkeypatch
):
admin_db = tmp_path / "admin.db"
orders_db = tmp_path / "orders.db"
share_db = tmp_path / "share.db"
share_reports = tmp_path / "share_reports"
share_reports.mkdir()
monkeypatch.setenv("GAOKAO_ENV", "dev")
monkeypatch.setenv("GAOKAO_DB_PATH", str(admin_db))
monkeypatch.setenv("GAOKAO_ORDERS_DB_PATH", str(orders_db))
monkeypatch.setenv("GAOKAO_SHARE_DB_PATH", str(share_db))
monkeypatch.setenv("GAOKAO_SHARE_REPORT_DIR", str(share_reports))
monkeypatch.setenv("GAOKAO_ORDERS_FERNET_KEY", "test-secret-for-web-self-service")
monkeypatch.setenv("GAOKAO_JWT_SECRET", "x" * 64)
monkeypatch.setenv("GAOKAO_JWT_EXP_MIN", "5")
monkeypatch.setenv("GAOKAO_ADMIN_USER", "admin")
monkeypatch.setenv("GAOKAO_ADMIN_PASS", "test-pass-123")
monkeypatch.setenv("GAOKAO_PAYMENT_PROVIDER", "mock")
monkeypatch.setenv("GAOKAO_PAYMENT_BASE_URL", "http://testserver")
monkeypatch.setenv("GAOKAO_PAYMENT_WEBHOOK_SECRET", "test-payment-secret")
from admin.app import create_app
from admin.config import load_settings
from data.payments.service import PaymentError, PaymentService
original = PaymentService.create_checkout
def _boom(self, order_id: str, *, portal_token: str):
raise PaymentError("checkout transport failed")
monkeypatch.setattr(PaymentService, "create_checkout", _boom)
settings = load_settings()
app = create_app(settings)
with TestClient(app) as client:
resp = client.post(
"/api/public/orders",
json={
"service_version": "standard",
"amount_cents": 9900,
"customer_name": "张家长",
"customer_phone": "13800138000",
"candidate_province": "湖南",
},
)
monkeypatch.setattr(PaymentService, "create_checkout", original)
assert resp.status_code == 503, resp.text
assert "payment checkout unavailable" in resp.text
with OrdersDAO.connect(settings.orders_db_path) as dao:
assert dao.count() == 0