from __future__ import annotations from datetime import datetime from types import SimpleNamespace import pytest from pydantic import ValidationError from api.schemas.demand_feedback import CreateDemandFeedbackBody from api.services import demand_feedback as feedback_service from supply_infra.db.models.demand_feedback import DemandFeedback def _body(**changes) -> CreateDemandFeedbackBody: values = { "client_request_id": "request-123", "target_type": "demand", "demand_grade_id": 10, "feedback_action": "support", } values.update(changes) return CreateDemandFeedbackBody(**values) def test_feedback_body_validates_target_fields() -> None: with pytest.raises(ValidationError): _body(target_type="video") with pytest.raises(ValidationError): _body( target_type="hit_content", video_id="vid-1", ) with pytest.raises(ValidationError): _body(video_id="vid-1") body = _body(target_type="video", video_id="vid-1") assert body.video_id == "vid-1" def test_feedback_body_validates_action_content_and_reason() -> None: with pytest.raises(ValidationError): _body(feedback_action="oppose") with pytest.raises(ValidationError): _body(feedback_action="correct") with pytest.raises(ValidationError): _body(reason_code="irrelevant") body = _body( feedback_action="correct", reason_code="wrong_reason", content=" 正确原因应引用后验样本。 ", ) assert body.content == "正确原因应引用后验样本。" def test_feedback_model_has_person_but_no_review_or_boolean_state() -> None: columns = DemandFeedback.__table__.columns assert "feedback_user_id" in columns assert "feedback_user_name_snapshot" in columns assert "feedback_username_snapshot" in columns assert "review_status" not in columns assert "has_feedback" not in columns assert "is_feedback" not in columns def test_create_feedback_uses_server_target_and_current_user(monkeypatch) -> None: grade = SimpleNamespace( id=10, biz_dt="20260730", demand_name="老年人智能手机使用", grade="A", reason="需求有明确内容意图", video_list=None, ) expansion = SimpleNamespace( id=30, biz_dt="20260730", source_demand_grade_id=10, video_id="vid-1", point_type="purpose", expanded_text="降低老人使用智能设备的门槛", point_desc="解释基础操作", reason="覆盖当前需求", ) detail = SimpleNamespace(title="教父母使用手机") saved: list[DemandFeedback] = [] class Session: def refresh(self, row) -> None: row.id = 99 row.created_at = datetime(2026, 7, 30, 15, 20) def rollback(self) -> None: raise AssertionError("unexpected rollback") class SessionContext: def __enter__(self): return Session() def __exit__(self, *_args): return False class GradeRepo: def __init__(self, _session): pass def get_by_id(self, grade_id): return grade if grade_id == 10 else None class ExpansionRepo: def __init__(self, _session): pass def has_video(self, **kwargs): return kwargs["video_id"] == "vid-1" def get_active_by_id(self, expansion_id): return expansion if expansion_id == 30 else None class DetailRepo: def __init__(self, _session): pass def list_by_vids(self, _video_ids): return {"vid-1": detail} class FeedbackRepo: def __init__(self, _session): pass def get_by_client_request_id(self, _request_id): return None def add(self, row): saved.append(row) return row def list_for_target(self, **_kwargs): return saved, len(saved) monkeypatch.setattr(feedback_service, "get_session", lambda: SessionContext()) monkeypatch.setattr(feedback_service, "DemandGradeRepository", GradeRepo) monkeypatch.setattr(feedback_service, "DemandVideoExpansionRepository", ExpansionRepo) monkeypatch.setattr(feedback_service, "MultiDemandVideoDetailRepository", DetailRepo) monkeypatch.setattr(feedback_service, "DemandFeedbackRepository", FeedbackRepo) result = feedback_service.create_demand_feedback( _body( target_type="hit_content", video_id="vid-1", demand_video_expansion_id=30, feedback_action="oppose", reason_code="not_hit", content="没有覆盖老人学习使用的需求。", ), { "id": 7, "username": "zhangsan", "display_name": "张三", }, ) assert result["feedback_summary"] == {"count": 1} assert result["item"]["feedback_user"] == { "id": 7, "username": "zhangsan", "display_name": "张三", } assert result["item"]["target_snapshot"]["expanded_text"] == "降低老人使用智能设备的门槛" assert saved[0].feedback_user_id == 7 assert saved[0].feedback_user_name_snapshot == "张三" assert saved[0].feedback_username_snapshot == "zhangsan"