@@ -643,5 +643,69 @@ def test_delete_history_api_deletes_selected_records(self, mock_auth) -> None:
643643 self .assertIsNotNone (session .query (AnalysisHistory ).filter (AnalysisHistory .id == record_id_2 ).first ())
644644
645645
646+ class HistoryItemSchemaNegativeSentimentTest (unittest .TestCase ):
647+ """Regression: HistoryItem / ReportSummary must accept out-of-range sentiment_score from DB rows."""
648+
649+ @classmethod
650+ def setUpClass (cls ) -> None :
651+ """Import schema classes once for all tests, skipping gracefully when deps are missing."""
652+ try :
653+ from api .v1 .schemas .history import HistoryItem , ReportSummary # type: ignore
654+ except ModuleNotFoundError :
655+ cls .HistoryItem = None
656+ cls .ReportSummary = None
657+ else :
658+ cls .HistoryItem = HistoryItem
659+ cls .ReportSummary = ReportSummary
660+
661+ def test_negative_sentiment_score_does_not_raise (self ) -> None :
662+ """Bug #942: sentiment_score=-22 in DB should not cause Pydantic ValidationError."""
663+ if self .HistoryItem is None :
664+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
665+
666+ item = self .HistoryItem (query_id = "q1" , stock_code = "600519" , sentiment_score = - 22 )
667+ self .assertEqual (item .sentiment_score , - 22 )
668+
669+ def test_out_of_range_high_sentiment_score_does_not_raise (self ) -> None :
670+ """HistoryItem should also accept scores above 100 from legacy data."""
671+ if self .HistoryItem is None :
672+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
673+
674+ item = self .HistoryItem (query_id = "q2" , stock_code = "600519" , sentiment_score = 150 )
675+ self .assertEqual (item .sentiment_score , 150 )
676+
677+ def test_none_sentiment_score_is_allowed (self ) -> None :
678+ """HistoryItem.sentiment_score=None should still be valid (optional field)."""
679+ if self .HistoryItem is None :
680+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
681+
682+ item = self .HistoryItem (query_id = "q3" , stock_code = "600519" , sentiment_score = None )
683+ self .assertIsNone (item .sentiment_score )
684+
685+ def test_report_summary_negative_sentiment_score_does_not_raise (self ) -> None :
686+ """ReportSummary.sentiment_score should also accept negative values from legacy DB rows."""
687+ if self .ReportSummary is None :
688+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
689+
690+ summary = self .ReportSummary (sentiment_score = - 22 )
691+ self .assertEqual (summary .sentiment_score , - 22 )
692+
693+ def test_report_summary_out_of_range_high_sentiment_score_does_not_raise (self ) -> None :
694+ """ReportSummary.sentiment_score should also accept scores above 100 from legacy data."""
695+ if self .ReportSummary is None :
696+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
697+
698+ summary = self .ReportSummary (sentiment_score = 150 )
699+ self .assertEqual (summary .sentiment_score , 150 )
700+
701+ def test_report_summary_none_sentiment_score_is_allowed (self ) -> None :
702+ """ReportSummary.sentiment_score=None should still be valid (optional field)."""
703+ if self .ReportSummary is None :
704+ self .skipTest ("fastapi / pydantic not installed in this test environment" )
705+
706+ summary = self .ReportSummary (sentiment_score = None )
707+ self .assertIsNone (summary .sentiment_score )
708+
709+
646710if __name__ == "__main__" :
647711 unittest .main ()
0 commit comments