"""Test GPT-OSS client with mocked responses.""" from __future__ import annotations from unittest.mock import MagicMock, patch import pytest from src.gpt_oss_client import GptOssClient from src.models import Bill @pytest.fixture def gpt_client() -> GptOssClient: return GptOssClient("http://localhost:4000/v1", "test-gpt-key") @patch.object(GptOssClient, "_request") def test_generate_summary(mock_request: MagicMock, gpt_client: GptOssClient): mock_request.return_value = { "choices": [ { "message": { "content": ( "SUMMARY: This bill establishes new regulations for data privacy.\n" "KEY MEASURES:\n" "- Requires companies to obtain user consent\n" "- Mandates data breach notifications\n" ) } } ] } bill = Bill(bill_id="B100", title="Data Privacy Act", text="Full bill text about data privacy...") summary = gpt_client.generate_summary(bill) assert summary.bill_id == "B100" assert "data privacy" in summary.summary_text.lower() assert len(summary.key_measures) == 2 assert "Requires companies to obtain user consent" in summary.key_measures assert "Mandates data breach notifications" in summary.key_measures assert summary.model_name == "gpt-oss" @patch.object(GptOssClient, "_request") def test_generate_summary_no_text(mock_request: MagicMock, gpt_client: GptOssClient): """When text is empty and title exists, fallback text is used to call API.""" mock_request.return_value = {} bill = Bill(bill_id="B100", title="Empty Bill", text="") summary = gpt_client.generate_summary(bill) assert summary.summary_text == "Summary generation failed." assert summary.key_measures == ["Summary generation failed."] mock_request.assert_called_once() @patch.object(GptOssClient, "_request") def test_generate_summary_no_text_uses_title(mock_request: MagicMock, gpt_client: GptOssClient): """When text is empty but title/subject exist, uses those for summary prompt.""" mock_request.return_value = { "choices": [{"message": {"content": "SUMMARY: Some summary.\nKEY MEASURES:\n- Measure 1\n"}}] } bill = Bill(bill_id="B100", title="Data Privacy Act", subject="Privacy", text="") summary = gpt_client.generate_summary(bill) assert "Some summary" in summary.summary_text assert summary.key_measures == ["Measure 1"] mock_request.assert_called_once() call_args = mock_request.call_args messages = call_args[0][0] assert "Data Privacy Act: Privacy" in messages[1]["content"] @patch.object(GptOssClient, "_request") def test_generate_summary_failure(mock_request: MagicMock, gpt_client: GptOssClient): mock_request.return_value = {} bill = Bill(bill_id="B100", title="Test Bill", text="Some text") summary = gpt_client.generate_summary(bill) assert summary.summary_text == "Summary generation failed." assert summary.key_measures == ["Summary generation failed."] @patch.object(GptOssClient, "_request") def test_generate_summary_truncates_long_text(mock_request: MagicMock, gpt_client: GptOssClient): """Text longer than 6000 chars is truncated before sending.""" mock_request.return_value = {"choices": [{"message": {"content": "SUMMARY: Short.\nKEY MEASURES:\n- M1\n"}}]} bill = Bill(bill_id="B100", title="Long Bill", text="x" * 10000) gpt_client.generate_summary(bill) call_args = mock_request.call_args messages = call_args[0][0] user_content = messages[1]["content"] assert len(user_content) < 6100 def test_extract_key_measures(): text = ( "SUMMARY: This bill does important things.\n" "KEY MEASURES:\n" "- First measure is important\n" "- Second measure matters too\n" "Additional context here." ) measures = GptOssClient._extract_key_measures(text) assert len(measures) == 2 assert "First measure is important" in measures assert "Second measure matters too" in measures def test_extract_key_measures_empty(): """Returns summary text fallback when no measures found.""" text = "This is just a regular paragraph with no key measures section." measures = GptOssClient._extract_key_measures(text) assert len(measures) == 1 assert measures[0] == "This is just a regular paragraph with no key measures section." def test_extract_key_measures_partial(): """Handles measures section followed by non-bullet text.""" text = "SUMMARY: Overview.\nKEY MEASURES:\n- First measure\nSome other text\n- This should not be included\n" measures = GptOssClient._extract_key_measures(text) assert len(measures) == 1 assert measures[0] == "First measure" def test_extract_key_measures_fallback_truncated(): """Fallback summary is truncated to 200 chars.""" text = "x" * 300 measures = GptOssClient._extract_key_measures(text) assert len(measures) == 1 assert len(measures[0]) == 200