test: 补充 WO-11 页面错误兜底测试
This commit is contained in:
@@ -978,6 +978,26 @@ fix: 修复页面刷新和按钮点击的 500 错误
|
||||
|
||||
- 用户需要提供或复现最容易触发 500 的页面 URL 与按钮名称;如果无法提供,则开发时先从当前浏览器打开的任务详情页开始排查。
|
||||
|
||||
完成记录:
|
||||
|
||||
- 完成日期:2026-07-03
|
||||
- 相关改动:
|
||||
- 补充路由回归测试,覆盖任务详情页、热点报告页、内容详情页、导出接口在数据库 I/O error 下的 503 兜底。
|
||||
- 补充缺失任务、缺失热点、缺失内容页面的 404 兜底测试,确保不是裸 500。
|
||||
- 核查当前实现已有 `OperationalError` / `SQLAlchemyError` 全局 handler:API 返回 503 JSON,HTML 页面返回数据库不可用友好页。
|
||||
- 验证命令:
|
||||
- `docker cp tests/. hot-comments-tool-app-1:/app/tests/`
|
||||
- `docker exec hot-comments-tool-app-1 sh -lc 'python -m pytest /app/tests/integration/test_routes.py -q'`
|
||||
- 验证结果:
|
||||
- `19 passed, 1 warning`
|
||||
- 验收结论:
|
||||
- 已知高频页面路由和导出入口在数据库 I/O error 下不会返回裸 `Internal Server Error`。
|
||||
- 缺失任务、热点、内容不会返回 500。
|
||||
- 服务端日志仍保留异常信息,页面/API 返回可理解错误。
|
||||
- 遗留问题:
|
||||
- 轮询接口偶发 503 时,前端当前只是静默返回,用户看不到“最近一次同步失败”提示;归入 WO-19 / WO-13 前端提示优化。
|
||||
- 当前本机 Python `.venv` 仍未完成依赖安装,本轮测试通过运行容器执行。
|
||||
|
||||
### WO-12 导出 Markdown / CSV 点击失效修复
|
||||
|
||||
优先级:P0
|
||||
|
||||
@@ -384,6 +384,102 @@ def test_index_page_shows_database_error_state_when_database_has_io_error(monkey
|
||||
assert "请先保留 data 目录" in response.text
|
||||
|
||||
|
||||
def test_task_detail_page_shows_database_error_state_when_database_has_io_error(monkeypatch):
|
||||
def broken_get_task(_session, _task_id):
|
||||
raise OperationalError("SELECT 1", {}, Exception("disk I/O error"))
|
||||
|
||||
monkeypatch.setattr("app.main.get_task", broken_get_task)
|
||||
|
||||
with make_test_client() as (client, _engine):
|
||||
response = client.get("/tasks/task-io-error")
|
||||
|
||||
assert response.status_code == 503
|
||||
assert "数据库暂时不可用" in response.text
|
||||
assert "请先保留 data 目录" in response.text
|
||||
|
||||
|
||||
def test_hotspot_report_page_shows_database_error_state_when_database_has_io_error(monkeypatch):
|
||||
def broken_get_task(_session, _task_id):
|
||||
raise OperationalError("SELECT 1", {}, Exception("disk I/O error"))
|
||||
|
||||
monkeypatch.setattr("app.main.get_task", broken_get_task)
|
||||
|
||||
with make_test_client() as (client, engine):
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
with Session(engine) as session:
|
||||
session.add(Task(id="task-hot-io", platform="xiaohongshu", status="success"))
|
||||
session.add(Hotspot(id="hot-io", task_id="task-hot-io", platform="xiaohongshu", title="热点", rank=1, raw_data="{}"))
|
||||
session.commit()
|
||||
|
||||
response = client.get("/hotspots/hot-io/report")
|
||||
|
||||
assert response.status_code == 503
|
||||
assert "数据库暂时不可用" in response.text
|
||||
|
||||
|
||||
def test_item_detail_page_shows_database_error_state_when_database_has_io_error(monkeypatch):
|
||||
def broken_get_task(_session, _task_id):
|
||||
raise OperationalError("SELECT 1", {}, Exception("disk I/O error"))
|
||||
|
||||
monkeypatch.setattr("app.main.get_task", broken_get_task)
|
||||
|
||||
with make_test_client() as (client, engine):
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
with Session(engine) as session:
|
||||
session.add(Task(id="task-item-io", platform="xiaohongshu", status="success"))
|
||||
session.add(Hotspot(id="hot-item-io", task_id="task-item-io", platform="xiaohongshu", title="热点", rank=1, raw_data="{}"))
|
||||
session.add(
|
||||
ContentItem(
|
||||
id="item-io",
|
||||
task_id="task-item-io",
|
||||
hotspot_id="hot-item-io",
|
||||
platform="xiaohongshu",
|
||||
source_item_id="item-io-source",
|
||||
item_type="note",
|
||||
status="success",
|
||||
raw_data="{}",
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
response = client.get("/items/item-io")
|
||||
|
||||
assert response.status_code == 503
|
||||
assert "数据库暂时不可用" in response.text
|
||||
|
||||
|
||||
def test_export_route_returns_service_unavailable_when_database_has_io_error(monkeypatch):
|
||||
def broken_export(_session, _item_id):
|
||||
raise OperationalError("SELECT 1", {}, Exception("disk I/O error"))
|
||||
|
||||
monkeypatch.setattr("app.main.export_item_comments_csv", broken_export)
|
||||
|
||||
with make_test_client() as (client, _engine):
|
||||
response = client.get("/api/export/items/item-io/comments.csv")
|
||||
|
||||
assert response.status_code == 503
|
||||
assert response.json()["detail"] == "数据库暂时不可用,请稍后重试或联系维护者恢复数据。"
|
||||
|
||||
|
||||
def test_missing_html_pages_render_friendly_not_found_page():
|
||||
with make_test_client() as (client, _engine):
|
||||
task_response = client.get("/tasks/missing-task")
|
||||
hotspot_response = client.get("/hotspots/missing-hotspot/report")
|
||||
item_response = client.get("/items/missing-item")
|
||||
|
||||
assert task_response.status_code == 404
|
||||
assert "任务不存在" in task_response.text
|
||||
assert "Internal Server Error" not in task_response.text
|
||||
assert hotspot_response.status_code == 404
|
||||
assert "热点不存在" in hotspot_response.text
|
||||
assert "Internal Server Error" not in hotspot_response.text
|
||||
assert item_response.status_code == 404
|
||||
assert "内容条目不存在" in item_response.text
|
||||
assert "Internal Server Error" not in item_response.text
|
||||
|
||||
|
||||
def test_task_api_includes_progress_counts_stage_and_demo_flag():
|
||||
with make_test_client() as (client, engine):
|
||||
seed_result_data(engine)
|
||||
|
||||
Reference in New Issue
Block a user