11from unittest .mock import MagicMock
22
3- import pytest
3+ from hamcrest import assert_that , calling , equal_to , is_ , raises , same_instance
44
55from eligibility_signposting_api .services .processors .derived_values import (
66 AddDaysHandler ,
@@ -26,7 +26,7 @@ def test_calculate_adds_default_days_to_date(self):
2626 result = handler .calculate (context )
2727
2828 # 2025-01-01 + 91 days = 2025-04-02
29- assert result == "20250402"
29+ assert_that ( result , is_ ( equal_to ( "20250402" )))
3030
3131 def test_calculate_with_function_args_override (self ):
3232 """Test that function args override default days."""
@@ -42,7 +42,7 @@ def test_calculate_with_function_args_override(self):
4242 result = handler .calculate (context )
4343
4444 # 2025-01-01 + 30 days = 2025-01-31
45- assert result == "20250131"
45+ assert_that ( result , is_ ( equal_to ( "20250131" )))
4646
4747 def test_calculate_with_vaccine_specific_days (self ):
4848 """Test that vaccine-specific days are used when configured."""
@@ -61,7 +61,7 @@ def test_calculate_with_vaccine_specific_days(self):
6161 result = handler .calculate (context )
6262
6363 # 2025-01-01 + 365 days = 2026-01-01
64- assert result == "20260101"
64+ assert_that ( result , is_ ( equal_to ( "20260101" )))
6565
6666 def test_calculate_with_date_format (self ):
6767 """Test that date format is applied to output."""
@@ -76,7 +76,7 @@ def test_calculate_with_date_format(self):
7676
7777 result = handler .calculate (context )
7878
79- assert result == "02 April 2025"
79+ assert_that ( result , is_ ( equal_to ( "02 April 2025" )))
8080
8181 def test_calculate_returns_empty_when_source_not_found (self ):
8282 """Test that empty string is returned when source date not found."""
@@ -91,7 +91,7 @@ def test_calculate_returns_empty_when_source_not_found(self):
9191
9292 result = handler .calculate (context )
9393
94- assert result == ""
94+ assert_that ( result , is_ ( equal_to ( "" )))
9595
9696 def test_calculate_returns_empty_when_vaccine_not_found (self ):
9797 """Test that empty string is returned when vaccine type not found."""
@@ -106,7 +106,7 @@ def test_calculate_returns_empty_when_vaccine_not_found(self):
106106
107107 result = handler .calculate (context )
108108
109- assert result == ""
109+ assert_that ( result , is_ ( equal_to ( "" )))
110110
111111 def test_calculate_with_invalid_date_raises_error (self ):
112112 """Test that invalid date format raises ValueError."""
@@ -119,20 +119,38 @@ def test_calculate_with_invalid_date_raises_error(self):
119119 date_format = None ,
120120 )
121121
122- with pytest .raises (ValueError , match = "Invalid date format" ):
123- handler .calculate (context )
122+ assert_that (
123+ calling (handler .calculate ).with_args (context ),
124+ raises (ValueError , pattern = "Invalid date format" ),
125+ )
126+
127+ def test_calculate_with_invalid_function_args_raises_error (self ):
128+ """Test that non-integer function args raises ValueError."""
129+ handler = AddDaysHandler (default_days = 91 )
130+ context = DerivedValueContext (
131+ person_data = [{"ATTRIBUTE_TYPE" : "COVID" , "LAST_SUCCESSFUL_DATE" : "20250101" }],
132+ attribute_name = "COVID" ,
133+ source_attribute = "LAST_SUCCESSFUL_DATE" ,
134+ function_args = "abc" , # Invalid: not an integer
135+ date_format = None ,
136+ )
137+
138+ assert_that (
139+ calling (handler .calculate ).with_args (context ),
140+ raises (ValueError , pattern = "Invalid days argument 'abc' for ADD_DAYS function" ),
141+ )
124142
125143 def test_get_source_attribute_maps_derived_to_source (self ):
126144 """Test that get_source_attribute maps derived attributes correctly."""
127145 handler = AddDaysHandler ()
128146
129- assert handler .get_source_attribute ("NEXT_DOSE_DUE" ) == "LAST_SUCCESSFUL_DATE"
147+ assert_that ( handler .get_source_attribute ("NEXT_DOSE_DUE" ), is_ ( equal_to ( "LAST_SUCCESSFUL_DATE" )))
130148
131149 def test_get_source_attribute_returns_original_if_not_mapped (self ):
132150 """Test that unmapped attributes return themselves."""
133151 handler = AddDaysHandler ()
134152
135- assert handler .get_source_attribute ("UNKNOWN_ATTR" ) == "UNKNOWN_ATTR"
153+ assert_that ( handler .get_source_attribute ("UNKNOWN_ATTR" ), is_ ( equal_to ( "UNKNOWN_ATTR" )))
136154
137155 def test_function_args_priority_over_vaccine_config (self ):
138156 """Test that function args take priority over vaccine-specific config."""
@@ -151,7 +169,7 @@ def test_function_args_priority_over_vaccine_config(self):
151169 result = handler .calculate (context )
152170
153171 # 2025-01-01 + 30 days = 2025-01-31
154- assert result == "20250131"
172+ assert_that ( result , is_ ( equal_to ( "20250131" )))
155173
156174
157175class TestDerivedValueRegistry :
@@ -165,29 +183,29 @@ def test_register_and_get_handler(self):
165183
166184 retrieved = registry .get_handler ("ADD_DAYS" )
167185
168- assert retrieved is handler
186+ assert_that ( retrieved , same_instance ( handler )) # type: ignore[call-overload]
169187
170188 def test_get_handler_case_insensitive (self ):
171189 """Test that handler lookup is case insensitive."""
172190 registry = DerivedValueRegistry ()
173191 handler = AddDaysHandler ()
174192 registry .register (handler )
175193
176- assert registry .get_handler ("add_days" ) is handler
177- assert registry .get_handler ("Add_Days" ) is handler
194+ assert_that ( registry .get_handler ("add_days" ), same_instance ( handler )) # type: ignore[call-overload]
195+ assert_that ( registry .get_handler ("Add_Days" ), same_instance ( handler )) # type: ignore[call-overload]
178196
179197 def test_has_handler_returns_true_when_exists (self ):
180198 """Test has_handler returns True for registered handlers."""
181199 registry = DerivedValueRegistry ()
182200 registry .register (AddDaysHandler ())
183201
184- assert registry .has_handler ("ADD_DAYS" ) is True
202+ assert_that ( registry .has_handler ("ADD_DAYS" ), is_ ( True ))
185203
186204 def test_has_handler_returns_false_when_not_exists (self ):
187205 """Test has_handler returns False for unregistered handlers."""
188206 registry = DerivedValueRegistry ()
189207
190- assert registry .has_handler ("UNKNOWN" ) is False
208+ assert_that ( registry .has_handler ("UNKNOWN" ), is_ ( False ))
191209
192210 def test_calculate_delegates_to_correct_handler (self ):
193211 """Test that calculate delegates to the correct handler."""
@@ -215,7 +233,7 @@ def test_calculate_delegates_to_correct_handler(self):
215233
216234 # Verify the mock handler was called with the context
217235 mock_handler .calculate .assert_called_once_with (context )
218- assert result == "mock_result"
236+ assert_that ( result , is_ ( equal_to ( "mock_result" )))
219237
220238 def test_calculate_raises_for_unknown_function (self ):
221239 """Test that calculate raises ValueError for unknown functions."""
@@ -229,32 +247,31 @@ def test_calculate_raises_for_unknown_function(self):
229247 date_format = None ,
230248 )
231249
232- with pytest .raises (ValueError , match = "No handler registered" ):
233- registry .calculate (
234- function_name = "UNKNOWN" ,
235- context = context ,
236- )
250+ assert_that (
251+ calling (registry .calculate ).with_args (function_name = "UNKNOWN" , context = context ),
252+ raises (ValueError , pattern = "No handler registered" ),
253+ )
237254
238255 def test_is_derived_attribute_returns_true_for_derived (self ):
239256 """Test is_derived_attribute for known derived attributes."""
240257 registry = DerivedValueRegistry ()
241258 registry .register (AddDaysHandler ())
242259
243- assert registry .is_derived_attribute ("NEXT_DOSE_DUE" ) is True
260+ assert_that ( registry .is_derived_attribute ("NEXT_DOSE_DUE" ), is_ ( True ))
244261
245262 def test_is_derived_attribute_returns_false_for_non_derived (self ):
246263 """Test is_derived_attribute for non-derived attributes."""
247264 registry = DerivedValueRegistry ()
248265 registry .register (AddDaysHandler ())
249266
250- assert registry .is_derived_attribute ("LAST_SUCCESSFUL_DATE" ) is False
267+ assert_that ( registry .is_derived_attribute ("LAST_SUCCESSFUL_DATE" ), is_ ( False ))
251268
252269 def test_default_handlers_are_registered (self ):
253270 """Test that default handlers from the module are registered."""
254271 registry = DerivedValueRegistry ()
255272
256273 # The default ADD_DAYS handler should be registered via __init__.py
257- assert registry .has_handler ("ADD_DAYS" )
274+ assert_that ( registry .has_handler ("ADD_DAYS" ), is_ ( True ) )
258275
259276 def test_clear_defaults_removes_default_handlers (self ):
260277 """Test that clear_defaults removes all default handlers."""
@@ -266,7 +283,7 @@ def test_clear_defaults_removes_default_handlers(self):
266283
267284 # New registry should have no handlers
268285 registry = DerivedValueRegistry ()
269- assert not registry .has_handler ("ADD_DAYS" )
286+ assert_that ( registry .has_handler ("ADD_DAYS" ), is_ ( False ) )
270287 finally :
271288 # Restore defaults for other tests using public method
272289 DerivedValueRegistry .set_default_handlers (saved_defaults )
0 commit comments