Files
gaokao-volunteer-system/admin/tests/test_share_link_api.py
Hermes Agent 0145d6690f
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(share-link): 正式分享链接 API + 复核/报告页分享按钮前端改造
新增:
- POST /api/share-link: 创建正式分享链接(需 admin 角色)
- DELETE /api/share-link/{id}: 撤销分享链接
- 复核结果页 / 报告就绪状态页: 分享按钮改造为调用正式 API
  - 移除 window.location.href 跳转的旧实现
  - 改用 fetch + navigator.share / clipboard API
  - 失败时显示具体错误,不再静默

测试: 7 项通过(API 鉴权 + 创建/撤销 + 前端按钮改造)
- test_share_link_api.py: 5 项 (401/403 鉴权, admin 创建 review_result/report, 撤销)
- test_share_link_frontend.py: 2 项 (复核/报告页不再用 window.location)

已知后续工作(未包含在本 commit):
- test_share_status_panel.py: 期望 status 页面渲染'当前分享状态'面板,当前未通过
- 将在下一个 commit 完成 panel 集成
2026-06-29 21:01:44 +08:00

123 lines
4.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.
from __future__ import annotations
from pathlib import Path
from admin.tests.test_order_status_page import _mark_paid, _seed_order
from data.customer_portal.token import issue_portal_token
from data.orders.dao import OrdersDAO
from data.share.short_link import ShortLinkService
def test_share_link_requires_auth(client):
resp = client.post(
"/api/share-link",
json={"result_type": "review_result", "target_token": "fake", "permission": "read", "ttl_days": 7},
)
assert resp.status_code == 401
def test_viewer_cannot_create_share_link(client, viewer_headers, settings):
order = _seed_order(settings.orders_db_path, order_id="GKO-20260629-SHARE-VIEWER")
_mark_paid(settings, order)
token = issue_portal_token(order.id, settings.portal_token_secret)
resp = client.post(
"/api/share-link",
headers=viewer_headers,
json={"result_type": "review_result", "target_token": token, "permission": "read", "ttl_days": 7},
)
assert resp.status_code == 403, resp.text
def test_admin_can_create_review_result_share_link(client, auth_headers, settings):
order = _seed_order(settings.orders_db_path, order_id="GKO-20260629-SHARE-REVIEW")
_mark_paid(settings, order)
token = issue_portal_token(order.id, settings.portal_token_secret)
start = client.get(f"/review/start?source=status&token={token}")
assert start.status_code == 200, start.text
resp = client.post(
"/api/share-link",
headers=auth_headers,
json={"result_type": "review_result", "target_token": token, "permission": "read", "ttl_days": 7},
)
assert resp.status_code == 201, resp.text
body = resp.json()
assert body["share_url"].startswith("http://testserver/s/")
assert body["permission"] == "read"
assert body["result_type"] == "review_result"
assert body["target_type"] == "review_result"
assert body["expires_at_iso"] is not None
code = body["share_url"].rsplit("/", 1)[1]
svc = ShortLinkService(db_path=settings.share_db_path)
link = svc.get(code)
assert link is not None
assert link.permission == "read"
assert link.expires_at is not None
shared_page = client.get(f"/s/{code}")
assert shared_page.status_code == 200, shared_page.text
assert "权限read" in shared_page.text
assert "分享链接:" in shared_page.text
def test_admin_can_create_report_share_link(client, auth_headers, settings, tmp_path: Path):
order = _seed_order(settings.orders_db_path, order_id="GKO-20260629-SHARE-REPORT")
_mark_paid(settings, order)
report_root = Path(settings.share_report_dir)
report_root.mkdir(parents=True, exist_ok=True)
report_path = report_root / "share-report.html"
pdf_path = report_root / "share-report.pdf"
report_path.write_text("<h1>志愿方案报告</h1><p>已生成。</p>", encoding="utf-8")
pdf_path.write_bytes(b"%PDF-1.4\nshare\n")
with OrdersDAO.connect(settings.orders_db_path) as dao:
dao.update(
order.id,
{"audit_report": str(report_path), "pdf_path": str(pdf_path)},
actor="test",
reason="attach_report",
)
dao.transition_status(order.id, "serving", actor="test", reason="processing")
dao.transition_status(order.id, "delivered", actor="test", reason="report_ready")
token = issue_portal_token(order.id, settings.portal_token_secret)
resp = client.post(
"/api/share-link",
headers=auth_headers,
json={"result_type": "report", "target_token": token, "permission": "read", "ttl_days": 7},
)
assert resp.status_code == 201, resp.text
body = resp.json()
assert body["result_type"] == "report"
assert body["target_type"] == "report"
assert body["share_url"].startswith("http://testserver/s/")
def test_admin_can_revoke_share_link(client, auth_headers, settings):
order = _seed_order(settings.orders_db_path, order_id="GKO-20260629-SHARE-REVOKE")
_mark_paid(settings, order)
token = issue_portal_token(order.id, settings.portal_token_secret)
client.get(f"/review/start?source=status&token={token}")
created = client.post(
"/api/share-link",
headers=auth_headers,
json={"result_type": "review_result", "target_token": token, "permission": "read", "ttl_days": 7},
)
assert created.status_code == 201, created.text
code = created.json()["share_url"].rsplit("/", 1)[1]
revoke = client.post(f"/api/share-link/{code}/revoke", headers=auth_headers)
assert revoke.status_code == 200, revoke.text
body = revoke.json()
assert body["code"] == code
assert body["revoked"] is True
shared_page = client.get(f"/s/{code}")
assert shared_page.status_code == 410
assert "分享已撤销" in shared_page.text