from sqlalchemy import select from sqlalchemy.orm import Session from app.models import Comment, ContentItem, Hotspot, Report, Task from app.schemas import CreateTaskRequest from app.services.task_service import create_task, run_task from tests.helpers import make_test_client class FakeXhsPlatform: def fetch_hotspots(self, *, limit): from app.platforms.base import HotspotData return [HotspotData(source_hot_id="h1", title="热点一", rank=1, heat_value="100", raw_data={"id": "h1"})] def search_items_by_hotspot(self, keyword, *, limit): from app.platforms.base import ContentItemData return [ ContentItemData(source_item_id="n1", item_type="note", title=f"{keyword} 笔记", raw_data={"id": "n1"}) ] def fetch_comments(self, source_item_id, *, limit): from app.platforms.base import CommentData return [CommentData(source_comment_id="c1", content="评论一", like_count=5, raw_data={"id": "c1"})] def test_xiaohongshu_task_flow_persists_hotspots_items_and_comments(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.build_ai_requester", lambda: ( lambda prompt: ( '[{"comment_id":"' + ("c1" if "c1" in prompt else prompt.split('"comment_id": "')[1].split('"')[0]) + '","sentiment":"positive","labels":["认可"],"reason":"喜欢"}]' ) ), ) 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) assert persisted.status == "success" assert persisted.processed_items_count == 1 assert persisted.successful_items_count == 1 assert persisted.analysis_status == "normal" assert session.scalar(select(Hotspot).where(Hotspot.task_id == task_id)).title == "热点一" assert session.scalar(select(ContentItem).where(ContentItem.task_id == task_id)).source_item_id == "n1" comment = session.scalar(select(Comment).where(Comment.task_id == task_id)) assert comment.content == "评论一" assert comment.ai_analysis_status == "success" assert comment.sentiment == "positive" assert comment.labels == '["认可"]' assert comment.reason == "喜欢" assert session.scalar(select(Report).where(Report.task_id == task_id, Report.report_type == "item")) is not None assert session.scalar(select(Report).where(Report.task_id == task_id, Report.report_type == "hotspot")) is not None def test_xiaohongshu_task_flow_marks_comments_failed_when_ai_parse_fails(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.build_ai_requester", lambda: (lambda _prompt: "not-json")) monkeypatch.setattr("app.services.ai_service.time.sleep", lambda _seconds: 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.analysis_status == "insufficient" assert persisted.analysis_success_rate == 0.0 assert comment.ai_analysis_status == "failed" assert comment.sentiment == "unknown" assert comment.labels == "[]" assert comment.reason == "ai_parse_failed" def test_xiaohongshu_task_flow_keeps_item_success_when_ai_request_raises(monkeypatch): with make_test_client() as (_client, engine): monkeypatch.setattr("app.services.task_service.build_platform", lambda _platform: FakeXhsPlatform()) monkeypatch.setattr("app.services.ai_service.time.sleep", lambda _seconds: None) def failing_requester(_prompt): raise RuntimeError("ai unauthorized") monkeypatch.setattr("app.services.task_service.build_ai_requester", lambda: failing_requester) 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.successful_items_count == 1 assert persisted.failed_items_count == 0 assert persisted.analysis_status == "insufficient" assert persisted.analysis_success_rate == 0.0 assert comment.ai_analysis_status == "failed" assert comment.reason == "ai_parse_failed"