105 lines
4.5 KiB
Python
105 lines
4.5 KiB
Python
from sqlalchemy.orm import Session
|
|
|
|
from app.models import ContentItem, Task
|
|
from app.platforms.base import PlatformAPIError
|
|
from app.schemas import CreateTaskRequest
|
|
from app.services.task_service import create_task, run_task
|
|
from tests.helpers import make_test_client
|
|
|
|
|
|
class FailingHotspotPlatform:
|
|
def fetch_hotspots(self, *, limit):
|
|
raise PlatformAPIError("hotspot failed", error_type="api_error", status_code=500)
|
|
|
|
|
|
class OneFailedOneSuccessfulItemPlatform:
|
|
def fetch_hotspots(self, *, limit):
|
|
from app.platforms.base import HotspotData
|
|
|
|
return [HotspotData(source_hot_id="h1", title="热点一", rank=1)]
|
|
|
|
def search_items_by_hotspot(self, keyword, *, limit):
|
|
from app.platforms.base import ContentItemData
|
|
|
|
return [
|
|
ContentItemData(source_item_id="bad-item", item_type="video", title="失败内容"),
|
|
ContentItemData(source_item_id="good-item", item_type="video", title="成功内容"),
|
|
]
|
|
|
|
def fetch_comments(self, source_item_id, *, limit):
|
|
from app.platforms.base import CommentData
|
|
|
|
if source_item_id == "bad-item":
|
|
raise PlatformAPIError("comments failed", error_type="rate_limited", status_code=429)
|
|
return [CommentData(source_comment_id="c1", content="继续成功")]
|
|
|
|
|
|
def test_hotspot_failure_marks_task_failed(monkeypatch):
|
|
with make_test_client() as (_client, engine):
|
|
monkeypatch.setattr("app.services.task_service.build_platform", lambda _platform: FailingHotspotPlatform())
|
|
with Session(engine) as session:
|
|
task = create_task(
|
|
session,
|
|
CreateTaskRequest(platform="douyin", hotspot_limit=1, item_limit_per_hotspot=1, comment_limit_per_item=10),
|
|
submit_background=False,
|
|
)
|
|
task_id = task.id
|
|
run_task(task_id, session_factory=lambda: session)
|
|
task = session.get(type(task), task_id)
|
|
|
|
assert task.status == "failed"
|
|
assert task.error_stage == "crawl_hotspots"
|
|
assert task.error_type == "api_error"
|
|
|
|
|
|
def test_unexpected_task_exception_marks_task_failed(monkeypatch):
|
|
def raise_unexpected_error(_platform):
|
|
raise RuntimeError("boom")
|
|
|
|
with make_test_client() as (_client, engine):
|
|
monkeypatch.setattr("app.services.task_service.build_platform", raise_unexpected_error)
|
|
with Session(engine) as session:
|
|
task = create_task(
|
|
session,
|
|
CreateTaskRequest(platform="douyin", hotspot_limit=1, item_limit_per_hotspot=1, comment_limit_per_item=10),
|
|
submit_background=False,
|
|
)
|
|
task_id = task.id
|
|
run_task(task_id, session_factory=lambda: session)
|
|
task = session.get(type(task), task_id)
|
|
|
|
assert task.status == "failed"
|
|
assert task.error_stage == "system"
|
|
assert task.error_type == "unexpected_error"
|
|
assert task.error_message == "boom"
|
|
|
|
|
|
def test_failed_content_item_is_recorded_and_following_item_continues(monkeypatch):
|
|
with make_test_client() as (_client, engine):
|
|
monkeypatch.setattr("app.services.task_service.build_platform", lambda _platform: OneFailedOneSuccessfulItemPlatform())
|
|
monkeypatch.setattr("app.services.task_service.build_ai_dependencies", lambda: (lambda _prompt: "[]", None))
|
|
monkeypatch.setattr("app.services.ai_service.time.sleep", lambda _seconds: None)
|
|
|
|
with Session(engine) as session:
|
|
task = create_task(
|
|
session,
|
|
CreateTaskRequest(platform="douyin", hotspot_limit=1, item_limit_per_hotspot=2, comment_limit_per_item=10),
|
|
submit_background=False,
|
|
)
|
|
task_id = task.id
|
|
run_task(task_id, session_factory=lambda: session)
|
|
|
|
persisted = session.get(Task, task_id)
|
|
failed_item = session.query(ContentItem).filter_by(task_id=task_id, source_item_id="bad-item").one()
|
|
successful_item = session.query(ContentItem).filter_by(task_id=task_id, source_item_id="good-item").one()
|
|
|
|
assert persisted.status == "success"
|
|
assert persisted.total_items_count == 2
|
|
assert persisted.processed_items_count == 2
|
|
assert persisted.successful_items_count == 1
|
|
assert persisted.failed_items_count == 1
|
|
assert failed_item.status == "failed"
|
|
assert failed_item.error_stage == "crawl_comments"
|
|
assert failed_item.error_type == "rate_limited"
|
|
assert successful_item.status == "success"
|