Merge branch 'codex/fix-running-task-timeout-from-current'

This commit is contained in:
meijiali
2026-07-03 20:11:17 +08:00
7 changed files with 157 additions and 1 deletions
+44
View File
@@ -1,5 +1,8 @@
from datetime import UTC, datetime, timedelta
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.pool import StaticPool
from app.db import Base, get_db_session
@@ -92,6 +95,47 @@ def test_create_task_returns_400_when_running_task_exists():
app.dependency_overrides.clear()
def test_create_task_recovers_stale_running_task_before_creating_new_one(monkeypatch):
client, engine = make_test_client()
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
monkeypatch.setattr("app.services.task_service.utc_now", lambda: now)
monkeypatch.setattr("app.services.task_service.task_executor.submit", lambda *_args, **_kwargs: None)
try:
with Session(engine) as session:
session.add(
Task(
id="stale-running",
platform="douyin",
status="running",
current_stage="ai_analysis",
last_progress_at=now - timedelta(minutes=11),
)
)
session.commit()
response = client.post(
"/api/tasks",
json={
"platform": "douyin",
"hotspot_limit": 5,
"item_limit_per_hotspot": 5,
"comment_limit_per_item": 50,
},
)
with Session(engine) as session:
stale_task = session.get(Task, "stale-running")
assert response.status_code == 201
assert stale_task.status == "failed"
assert stale_task.error_stage == "system"
assert stale_task.error_type == "stale_progress_timeout"
assert "超过 10 分钟没有进度更新" in stale_task.error_message
finally:
engine.dispose()
app.dependency_overrides.clear()
def test_task_list_and_detail_return_created_tasks():
client, engine = make_test_client()
try:
@@ -1,4 +1,6 @@
import json
import time
from types import SimpleNamespace
from sqlalchemy import select
from sqlalchemy.orm import Session
@@ -147,3 +149,43 @@ def test_xiaohongshu_task_flow_keeps_item_success_when_ai_request_raises(monkeyp
assert persisted.analysis_success_rate == 0.0
assert comment.ai_analysis_status == "failed"
assert comment.reason == "ai_parse_failed"
def test_xiaohongshu_task_flow_times_out_stalled_ai_request(monkeypatch):
with make_test_client() as (_client, engine):
monkeypatch.setattr("app.services.task_service.build_platform", lambda _platform: FakeXhsPlatform())
monkeypatch.setattr(
"app.services.task_service.get_settings",
lambda: SimpleNamespace(ai_max_retries=1, ai_timeout_seconds=0.01),
)
def stalled_requester(prompt):
time.sleep(0.2)
comments = json.loads(prompt[prompt.index("[") :])
return json.dumps(
[{"comment_id": comments[0]["comment_id"], "sentiment": "positive", "labels": ["超时后不应采用"], "reason": "late"}],
ensure_ascii=False,
)
monkeypatch.setattr("app.services.task_service.build_ai_dependencies", lambda: (stalled_requester, None))
with Session(engine) as session:
task = create_task(
session,
CreateTaskRequest(platform="xiaohongshu", hotspot_limit=1, item_limit_per_hotspot=1, comment_limit_per_item=10),
submit_background=False,
)
task_id = task.id
with Session(engine) as session:
run_task(task_id, session_factory=lambda: session)
persisted = session.get(Task, task_id)
comment = session.scalar(select(Comment).where(Comment.task_id == task_id))
assert persisted.status == "success"
assert persisted.finished_at is not None
assert persisted.analysis_status == "insufficient"
assert comment.ai_analysis_status == "failed"
assert comment.sentiment == "unknown"
assert comment.reason == "ai_parse_failed"
+14
View File
@@ -51,3 +51,17 @@ def test_tikhub_client_raises_structured_error_after_retries(monkeypatch):
assert exc_info.value.status_code == 429
assert "secret-token" not in str(exc_info.value)
assert sleeps == [1, 2, 4]
def test_tikhub_client_reports_401_as_auth_error_without_leaking_token():
transport = SequenceTransport([httpx.Response(401, json={"message": "Unauthorized"})])
http_client = httpx.Client(transport=httpx.MockTransport(transport))
client = TikHubClient(base_url="https://api.test", api_key="secret-token", http_client=http_client)
with pytest.raises(PlatformAPIError) as exc_info:
client.get("/demo")
assert exc_info.value.error_type == "auth_error"
assert exc_info.value.status_code == 401
assert str(exc_info.value) == "TikHub 鉴权失败,请检查 TIKHUB_API_KEY 是否有效"
assert "secret-token" not in str(exc_info.value)