130 lines
5.0 KiB
Python
130 lines
5.0 KiB
Python
"""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
|