11"""Unit tests for passive voice detection with mocked OpenAI responses."""
22
3- import json
43import unittest
54from unittest .mock import Mock , patch , MagicMock
65from pathlib import Path
@@ -15,30 +14,29 @@ def setUp(self):
1514 """Set up test fixtures."""
1615 self .mock_client = Mock ()
1716
18- def _create_mock_response (self , results_data ):
19- """Create a mock OpenAI response with the given results data .
17+ def _create_mock_chat_response (self , content ):
18+ """Create a mock OpenAI chat completion response .
2019
2120 Args:
22- results_data: List of dicts with 'sentence' and 'fragments' keys
21+ content: The content string ("passive" or "active")
2322
2423 Returns:
25- Mock response object that mimics OpenAI's response structure
24+ Mock response object that mimics OpenAI's chat completion structure
2625 """
2726 mock_response = Mock ()
28- mock_response .output_text = json .dumps ({"results" : results_data })
27+ mock_choice = Mock ()
28+ mock_message = Mock ()
29+ mock_message .content = content
30+ mock_choice .message = mock_message
31+ mock_response .choices = [mock_choice ]
2932 return mock_response
3033
3134 def test_detect_passive_voice_basic_passive (self ):
3235 """Test detection of basic passive voice constructions."""
3336 # Mock response for passive voice sentence
34- mock_response = self ._create_mock_response ([
35- {
36- "sentence" : "The ball was thrown by John." ,
37- "fragments" : ["was thrown" ]
38- }
39- ])
37+ mock_response = self ._create_mock_chat_response ("passive" )
4038
41- self .mock_client .responses .create .return_value = mock_response
39+ self .mock_client .chat . completions .create .return_value = mock_response
4240
4341 result = detect_passive_voice_segments (
4442 "The ball was thrown by John." ,
@@ -48,24 +46,19 @@ def test_detect_passive_voice_basic_passive(self):
4846 self .assertEqual (len (result ), 1 )
4947 sentence , fragments = result [0 ]
5048 self .assertEqual (sentence , "The ball was thrown by John." )
51- self .assertEqual (fragments , ["was thrown" ])
49+ self .assertEqual (fragments , ["The ball was thrown by John. " ]) # Now returns full sentence
5250
5351 # Verify the API was called correctly
54- self .mock_client .responses .create .assert_called_once ()
55- call_args = self .mock_client .responses .create .call_args
52+ self .mock_client .chat . completions .create .assert_called_once ()
53+ call_args = self .mock_client .chat . completions .create .call_args
5654 self .assertEqual (call_args [1 ]['model' ], 'gpt-5-nano' )
5755
5856 def test_detect_passive_voice_active_sentence (self ):
5957 """Test that active voice sentences return empty fragments."""
6058 # Mock response for active voice sentence
61- mock_response = self ._create_mock_response ([
62- {
63- "sentence" : "John threw the ball." ,
64- "fragments" : []
65- }
66- ])
59+ mock_response = self ._create_mock_chat_response ("active" )
6760
68- self .mock_client .responses .create .return_value = mock_response
61+ self .mock_client .chat . completions .create .return_value = mock_response
6962
7063 result = detect_passive_voice_segments (
7164 "John threw the ball." ,
@@ -79,23 +72,14 @@ def test_detect_passive_voice_active_sentence(self):
7972
8073 def test_detect_passive_voice_multiple_sentences (self ):
8174 """Test detection with multiple sentences."""
82- # Mock response for multiple sentences
83- mock_response = self ._create_mock_response ([
84- {
85- "sentence" : "The report was completed yesterday." ,
86- "fragments" : ["was completed" ]
87- },
88- {
89- "sentence" : "The team finished the project." ,
90- "fragments" : []
91- },
92- {
93- "sentence" : "The documents were reviewed by the manager." ,
94- "fragments" : ["were reviewed" ]
95- }
96- ])
97-
98- self .mock_client .responses .create .return_value = mock_response
75+ # Mock responses for each sentence call
76+ responses = [
77+ self ._create_mock_chat_response ("passive" ), # "The report was completed yesterday."
78+ self ._create_mock_chat_response ("active" ), # "The team finished the project."
79+ self ._create_mock_chat_response ("passive" ) # "The documents were reviewed by the manager."
80+ ]
81+
82+ self .mock_client .chat .completions .create .side_effect = responses
9983
10084 text = "The report was completed yesterday. The team finished the project. The documents were reviewed by the manager."
10185 result = detect_passive_voice_segments (text , openai_client = self .mock_client )
@@ -105,7 +89,7 @@ def test_detect_passive_voice_multiple_sentences(self):
10589 # Check first sentence (passive)
10690 sentence1 , fragments1 = result [0 ]
10791 self .assertEqual (sentence1 , "The report was completed yesterday." )
108- self .assertEqual (fragments1 , ["was completed" ])
92+ self .assertEqual (fragments1 , ["The report was completed yesterday. " ])
10993
11094 # Check second sentence (active)
11195 sentence2 , fragments2 = result [1 ]
@@ -115,27 +99,18 @@ def test_detect_passive_voice_multiple_sentences(self):
11599 # Check third sentence (passive)
116100 sentence3 , fragments3 = result [2 ]
117101 self .assertEqual (sentence3 , "The documents were reviewed by the manager." )
118- self .assertEqual (fragments3 , ["were reviewed" ])
102+ self .assertEqual (fragments3 , ["The documents were reviewed by the manager. " ])
119103
120104 def test_detect_passive_voice_edge_cases (self ):
121105 """Test edge cases that should not be flagged as passive."""
122- # Mock response for edge cases that are not passive
123- mock_response = self ._create_mock_response ([
124- {
125- "sentence" : "Business is very well known career field." ,
126- "fragments" : []
127- },
128- {
129- "sentence" : "I am immersing myself in my culture." ,
130- "fragments" : []
131- },
132- {
133- "sentence" : "The president was satisfied with the results." ,
134- "fragments" : []
135- }
136- ])
137-
138- self .mock_client .responses .create .return_value = mock_response
106+ # Mock responses for edge cases that are not passive
107+ responses = [
108+ self ._create_mock_chat_response ("active" ),
109+ self ._create_mock_chat_response ("active" ),
110+ self ._create_mock_chat_response ("active" )
111+ ]
112+
113+ self .mock_client .chat .completions .create .side_effect = responses
139114
140115 sentences = [
141116 "Business is very well known career field." ,
@@ -156,39 +131,32 @@ def test_detect_passive_voice_sequence_input(self):
156131 "She baked the cake this morning."
157132 ]
158133
159- mock_response = self ._create_mock_response ([
160- {
161- "sentence" : "The cake was baked this morning." ,
162- "fragments" : ["was baked" ]
163- },
164- {
165- "sentence" : "She baked the cake this morning." ,
166- "fragments" : []
167- }
168- ])
134+ responses = [
135+ self ._create_mock_chat_response ("passive" ),
136+ self ._create_mock_chat_response ("active" )
137+ ]
169138
170- self .mock_client .responses . create .return_value = mock_response
139+ self .mock_client .chat . completions . create .side_effect = responses
171140
172141 result = detect_passive_voice_segments (sentences , openai_client = self .mock_client )
173142
174143 self .assertEqual (len (result ), 2 )
175- self .assertEqual (result [0 ][1 ], ["was baked" ]) # First should have fragments
144+ self .assertEqual (result [0 ][1 ], ["The cake was baked this morning. " ]) # First should have full sentence
176145 self .assertEqual (result [1 ][1 ], []) # Second should have no fragments
177146
178- def test_detect_passive_voice_invalid_json_response (self ):
179- """Test handling of invalid JSON responses."""
180- # Mock response with invalid JSON
181- mock_response = Mock ()
182- mock_response .output_text = "Invalid JSON response"
147+ def test_detect_passive_voice_invalid_response (self ):
148+ """Test handling of invalid responses."""
149+ # Mock response with unrecognized classification
150+ mock_response = self ._create_mock_chat_response ("invalid_classification" )
183151
184- self .mock_client .responses .create .return_value = mock_response
152+ self .mock_client .chat . completions .create .return_value = mock_response
185153
186154 result = detect_passive_voice_segments (
187155 "The ball was thrown." ,
188156 openai_client = self .mock_client
189157 )
190158
191- # Should return empty fragments for all sentences when JSON is invalid
159+ # Should return empty fragments for all sentences when response is invalid
192160 self .assertEqual (len (result ), 1 )
193161 sentence , fragments = result [0 ]
194162 self .assertEqual (sentence , "The ball was thrown." )
@@ -197,12 +165,9 @@ def test_detect_passive_voice_invalid_json_response(self):
197165 def test_detect_passive_voice_empty_response (self ):
198166 """Test handling of empty responses."""
199167 # Mock response with empty content
200- mock_response = Mock ()
201- mock_response .output_text = ""
202- mock_response .output = []
203- mock_response .data = []
168+ mock_response = self ._create_mock_chat_response ("" )
204169
205- self .mock_client .responses .create .return_value = mock_response
170+ self .mock_client .chat . completions .create .return_value = mock_response
206171
207172 result = detect_passive_voice_segments (
208173 "The ball was thrown." ,
@@ -215,23 +180,41 @@ def test_detect_passive_voice_empty_response(self):
215180 self .assertEqual (sentence , "The ball was thrown." )
216181 self .assertEqual (fragments , [])
217182
218- def test_detect_passive_voice_partial_json_response (self ):
219- """Test handling of partial JSON in response with extra text."""
220- # Mock response with JSON embedded in extra text
221- mock_response = Mock ()
222- mock_response .output_text = 'Here is the analysis: {"results": [{"sentence": "The door was opened.", "fragments": ["was opened"]}]} Hope this helps!'
183+ def test_detect_passive_voice_response_with_extra_text (self ):
184+ """Test handling of response with extra text around the classification."""
185+ # Mock response with extra text around the main classification
186+ mock_response = self ._create_mock_chat_response ("The sentence is: passive" )
187+
188+ self .mock_client .chat .completions .create .return_value = mock_response
189+
190+ result = detect_passive_voice_segments (
191+ "The door was opened." ,
192+ openai_client = self .mock_client
193+ )
194+
195+ # Current implementation requires exact match, so extra text causes it to be treated as active
196+ self .assertEqual (len (result ), 1 )
197+ sentence , fragments = result [0 ]
198+ self .assertEqual (sentence , "The door was opened." )
199+ self .assertEqual (fragments , []) # Should be empty due to inexact match
223200
224- self .mock_client .responses .create .return_value = mock_response
201+ def test_detect_passive_voice_exact_passive_response (self ):
202+ """Test that exact 'passive' responses are handled correctly."""
203+ # Mock response with exact "passive" classification
204+ mock_response = self ._create_mock_chat_response ("passive" )
205+
206+ self .mock_client .chat .completions .create .return_value = mock_response
225207
226208 result = detect_passive_voice_segments (
227209 "The door was opened." ,
228210 openai_client = self .mock_client
229211 )
230212
213+ # Should return full sentence as fragment for exact "passive" match
231214 self .assertEqual (len (result ), 1 )
232215 sentence , fragments = result [0 ]
233216 self .assertEqual (sentence , "The door was opened." )
234- self .assertEqual (fragments , ["was opened" ])
217+ self .assertEqual (fragments , ["The door was opened. " ])
235218
236219 def test_detect_passive_voice_short_sentences_filtered (self ):
237220 """Test that sentences with 2 or fewer words are filtered out."""
@@ -242,11 +225,9 @@ def test_detect_passive_voice_short_sentences_filtered(self):
242225
243226 def test_detect_passive_voice_custom_model (self ):
244227 """Test that custom model parameter is passed correctly."""
245- mock_response = self ._create_mock_response ([
246- {"sentence" : "Test sentence here." , "fragments" : []}
247- ])
228+ mock_response = self ._create_mock_chat_response ("active" )
248229
249- self .mock_client .responses .create .return_value = mock_response
230+ self .mock_client .chat . completions .create .return_value = mock_response
250231
251232 detect_passive_voice_segments (
252233 "Test sentence here." ,
@@ -255,19 +236,17 @@ def test_detect_passive_voice_custom_model(self):
255236 )
256237
257238 # Verify the custom model was used
258- call_args = self .mock_client .responses .create .call_args
239+ call_args = self .mock_client .chat . completions .create .call_args
259240 self .assertEqual (call_args [1 ]['model' ], 'gpt-4' )
260241
261242 @patch ('formfyxer.passive_voice_detection._load_prompt' )
262243 def test_prompt_loading (self , mock_load_prompt ):
263244 """Test that the prompt is loaded correctly."""
264245 mock_load_prompt .return_value = "Test prompt content"
265246
266- mock_response = self ._create_mock_response ([
267- {"sentence" : "Test sentence with more words." , "fragments" : []}
268- ])
247+ mock_response = self ._create_mock_chat_response ("active" )
269248
270- self .mock_client .responses .create .return_value = mock_response
249+ self .mock_client .chat . completions .create .return_value = mock_response
271250
272251 detect_passive_voice_segments (
273252 "Test sentence with more words." ,
@@ -278,20 +257,20 @@ def test_prompt_loading(self, mock_load_prompt):
278257 mock_load_prompt .assert_called_once ()
279258
280259 # Verify the loaded prompt was used in the API call
281- call_args = self .mock_client .responses .create .call_args
282- input_items = call_args [1 ]['input ' ]
283- system_message = input_items [ 0 ]
284- self .assertEqual ( system_message [ ' content' ] [0 ]['text' ], "Test prompt content" )
260+ call_args = self .mock_client .chat . completions .create .call_args
261+ messages = call_args [1 ]['messages ' ]
262+ self . assertEqual ( len ( messages ), 1 )
263+ self .assertIn ( "Test prompt content" , messages [0 ]['content' ] )
285264
286265 def test_sentence_splitting (self ):
287266 """Test that text is properly split into sentences."""
288- mock_response = self . _create_mock_response ( [
289- { "sentence" : "First sentence here." , "fragments" : []} ,
290- { "sentence" : "Second sentence here." , "fragments" : []} ,
291- { "sentence" : "Third sentence here!" , "fragments" : []}
292- ])
267+ responses = [
268+ self . _create_mock_chat_response ( "active" ) ,
269+ self . _create_mock_chat_response ( "active" ) ,
270+ self . _create_mock_chat_response ( "active" )
271+ ]
293272
294- self .mock_client .responses . create .return_value = mock_response
273+ self .mock_client .chat . completions . create .side_effect = responses
295274
296275 text = "First sentence here. Second sentence here. Third sentence here!"
297276 result = detect_passive_voice_segments (text , openai_client = self .mock_client )
0 commit comments