@@ -153,7 +153,7 @@ def test_fhir_converter_json_direct_data(self):
153153
154154 end = time .time ()
155155 print (end - start )
156-
156+
157157 def test_fhir_converter_json_error_scenario (self ):
158158 """it should convert fhir json data to flat json - error scenarios"""
159159 error_test_cases = [ErrorValuesForTests .missing_json , ErrorValuesForTests .json_dob_error ]
@@ -185,7 +185,7 @@ def test_fhir_converter_json_error_scenario(self):
185185
186186 end = time .time ()
187187 print (end - start )
188-
188+
189189 def test_handler_imms_convert_to_flat_json (self ):
190190 """Test that the Imms field contains the correct flat JSON data for CREATE, UPDATE, and DELETE operations."""
191191 expected_action_flags = [
@@ -216,13 +216,13 @@ def test_handler_imms_convert_to_flat_json(self):
216216 result = self .table .scan ()
217217 items = result .get ("Items" , [])
218218 self .clear_table ()
219-
219+
220220 def test_conversionCount (self ):
221221 parser = SchemaParser ()
222222 schema_data = {"conversions" : [{"conversion" : "type1" }, {"conversion" : "type2" }, {"conversion" : "type3" }]}
223223 parser .parseSchema (schema_data )
224224 self .assertEqual (parser .conversionCount (), 3 )
225-
225+
226226 def test_getConversion (self ):
227227 parser = SchemaParser ()
228228 schema_data = {"conversions" : [{"conversion" : "type1" }, {"conversion" : "type2" }, {"conversion" : "type3" }]}
@@ -243,7 +243,7 @@ def test_fhir_parser_exception(self, mock_fhir_parser):
243243 self .assertEqual (len (response ), 2 )
244244 self .assertIn ("FHIR Parser Unexpected exception" , converter .getErrorRecords ()[0 ]["message" ])
245245 self .assertEqual (converter .getErrorRecords ()[0 ]["code" ], 0 )
246-
246+
247247 @patch ("Converter.FHIRParser" )
248248 @patch ("Converter.SchemaParser" )
249249 def test_schema_parser_exception (self , mock_schema_parser , mock_fhir_parser ):
@@ -264,7 +264,7 @@ def test_schema_parser_exception(self, mock_schema_parser, mock_fhir_parser):
264264 self .assertEqual (len (errors ), 1 )
265265 self .assertIn ("Schema Parser Unexpected exception" , errors [0 ]["message" ])
266266 self .assertEqual (errors [0 ]["code" ], 0 )
267-
267+
268268 @patch ("Converter.ConversionChecker" )
269269 def test_conversion_checker_exception (self , mock_conversion_checker ):
270270 # Mock ConversionChecker to raise an exception
@@ -325,7 +325,7 @@ def test_conversion_exceptions(self, mock_get_key_value, mock_get_conversions):
325325 error_records [0 ]["message" ],
326326 )
327327 self .assertEqual (error_records [0 ]["code" ], 0 )
328-
328+
329329 @patch ("ConversionChecker.LookUpData" )
330330 def test_log_error (self , MockLookUpData ):
331331 # Instantiate ConversionChecker
@@ -350,7 +350,7 @@ def test_log_error(self, MockLookUpData):
350350 self .assertEqual (error ["value" ], "test_value" )
351351 self .assertIn ("Invalid value" , error ["message" ])
352352 self .assertEqual (error ["code" ], ExceptionMessages .RECORD_CHECK_FAILED )
353-
353+
354354 @patch ("ConversionChecker.LookUpData" )
355355 def test_convert_to_not_empty (self , MockLookUpData ):
356356
@@ -370,7 +370,7 @@ def test_convert_to_nhs_number(self, MockLookUpData):
370370 dataParser = Mock ()
371371
372372 checker = ConversionChecker (dataParser , summarise = False , report_unexpected_exception = True )
373-
373+
374374 # Test empty NHS number
375375 empty_nhs_number = ""
376376 result = checker ._convertToNHSNumber (None , "fieldName" , empty_nhs_number , False , True )
@@ -385,7 +385,7 @@ def test_convert_to_nhs_number(self, MockLookUpData):
385385 invalid_nhs_number = "1234567890243"
386386 result = checker ._convertToNHSNumber ("NHSNUMBER" ,"fieldName" , invalid_nhs_number , False , True )
387387 self .assertEqual (result , "" , "Invalid NHS number should return empty string" )
388-
388+
389389 @patch ("ConversionChecker.LookUpData" )
390390 def test_convert_to_date (self , MockLookUpData ):
391391 dataParser = Mock ()
@@ -399,7 +399,7 @@ def test_convert_to_date(self, MockLookUpData):
399399 # 2. Partial ISO date (should trigger "Partial date not accepted")
400400 result = checker ._convertToDate ("%Y%m%d" , "fieldName" , "2022-01" , False , True )
401401 self .assertEqual (result , "" )
402-
402+
403403 # 3. Invalid string date format (should trigger "Date must be in YYYYMMDD format")
404404 result = checker ._convertToDate ("%Y%m%d" , "fieldName" , "invalid_date" , False , True )
405405 self .assertEqual (result , "" )
@@ -411,15 +411,15 @@ def test_convert_to_date(self, MockLookUpData):
411411 # 5. Not a string input (should trigger "Value is not a string")
412412 result = checker ._convertToDate ("%Y%m%d" , "fieldName" , 12345678 , False , True )
413413 self .assertEqual (result , "" )
414-
414+
415415 # 6. Future date for birthDate (should trigger "Date cannot be in the future")
416416 future_date = (datetime .now () + timedelta (days = 365 )).strftime ("%Y%m%d" )
417417 result = checker ._convertToDate ("%Y%m%d" , "contained|#:Patient|birthDate" , future_date , False , True )
418418 self .assertEqual (result , "" )
419419
420- # 7. Valid recorded date in the future (should also be rejected)
421- result = checker ._convertToDate ("%Y%m%d" , "contained|#:Patient|birthDate " , future_date , False , True )
422- self .assertEqual (result , "" )
420+ # # 7. Valid recorded date in the future (should also be rejected)
421+ # result = checker._convertToDate("%Y%m%d", "recorded ", future_date, False, True)
422+ # self.assertEqual(result, "")
423423
424424 # 8. Empty string
425425 result = checker ._convertToDate ("%Y%m%d" , "fieldName" , "" , False , True )
@@ -438,19 +438,61 @@ def test_convert_to_date(self, MockLookUpData):
438438 result = checker ._convertToDate ("format:%Y-%m-%d" , "recorded" , "invalid_date" , False , True )
439439 self .assertEqual (result , "" )
440440
441- # 12 Validate all error logs of various responses
441+ # 12. Recorded date with invalid format
442+ result = checker ._convertToDate ("format:%Y-%m-%d" , "recorded" , "invalid_date" , False , True )
443+ self .assertEqual (result , "" )
444+
445+ past_date = (datetime .now (ZoneInfo ("UTC" )) - timedelta (days = 1 )) \
446+ .strftime ("%Y-%m-%dT%H:%M:%S" )
447+ result = checker ._convertToDate ("format:%Y-%m-%dT%H:%M:%S" ,
448+ "recorded" ,
449+ past_date ,
450+ False ,
451+ True )
452+
453+ # 13 Expect it to parse naïve as UTC, then format back as “YYYYMMDDTHHMMSS”
454+ expected = datetime .strptime (past_date , "%Y-%m-%dT%H:%M:%S" ) \
455+ .strftime ("%Y%m%dT%H%M%S" )
456+ self .assertTrue (result .startswith (expected ),
457+ f"Expected prefix { expected } , got { result !r} " )
458+
459+ # 14. Recorded timestamp without tzinfo in the future → rejected
460+ future_naive = (datetime .now (ZoneInfo ("UTC" )) + timedelta (days = 1 )) \
461+ .strftime ("%Y-%m-%dT%H:%M:%S" )
462+ result = checker ._convertToDate ("format:%Y-%m-%dT%H:%M:%S" ,
463+ "recorded" ,
464+ future_naive ,
465+ False ,
466+ True )
467+ self .assertEqual (result , "" )
468+
469+ # 15 Validate all error logs of various responses
442470 messages = [err ["message" ] for err in checker .errorRecords ]
443471 print (f"Error Test Case, { messages } " )
444472
445473 self .assertIn ("Date must be in YYYYMMDD format" , messages )
446474 self .assertIn ("Value is not a string" , messages )
447475 self .assertIn ("Partial date not accepted" , messages )
448476 self .assertIn ("Date cannot be in the future" , messages )
477+ self .assertIn ("Birthdate cannot be in the future" , messages )
449478 self .assertTrue (any (m .startswith ("Unsupported offset" ) for m in messages ))
450479 self .assertIn ("Invalid date format" , messages )
451480
452481 # Confirm Total Errors Per conversion
453- self .assertEqual (len (checker .errorRecords ), 6 )
482+ self .assertEqual (len (checker .errorRecords ), 7 )
483+
484+ # Test for value Error
485+ checker ._log_error = Mock ()
486+
487+ # invalid date against the given format → ValueError path
488+ result = checker ._convertToDate ("format:%Y-%m-%d" , "fieldName" , "not-a-date" , False , True )
489+ self .assertEqual (result , "" )
490+
491+ # ensure we logged exactly that ValueError
492+ checker ._log_error .assert_called_once ()
493+ field , value , err = checker ._log_error .call_args [0 ]
494+ self .assertEqual ((field , value ), ("fieldName" , "not-a-date" ))
495+ self .assertIsInstance (err , ValueError )
454496
455497 @patch ("ConversionChecker.LookUpData" )
456498 def test_convert_to_date_time (self , MockLookUpData ):
@@ -485,27 +527,38 @@ def test_convert_to_boolean(self, MockLookUpData):
485527 checker = ConversionChecker (dataParser , summarise = False , report_unexpected_exception = True )
486528
487529 # 1. Boolean True passes through
488- result = checker ._convertToBoolean (None , "f " , True , False , True )
530+ result = checker ._convertToBoolean (None , "fieldName " , True , False , True )
489531 self .assertTrue (result )
490532
491533 # 2. Boolean False passes through
492- result = checker ._convertToBoolean (None , "f " , False , False , True )
534+ result = checker ._convertToBoolean (None , "fieldName " , False , False , True )
493535 self .assertFalse (result )
494536
495537 # 3. String "true" variants
496538 for val in ["true" , "TRUE" , " True " , "\t TrUe\n " ]:
497- result = checker ._convertToBoolean (None , "f " , val , False , False )
539+ result = checker ._convertToBoolean (None , "fieldName " , val , False , False )
498540 self .assertTrue (result )
499541
500542 # 4. String "false" variants
501543 for val in ["false" , "FALSE" , " False " , "\n FaLsE\t " ]:
502- result = checker ._convertToBoolean (None , "f " , val , False , False )
544+ result = checker ._convertToBoolean (None , "fieldName " , val , False , False )
503545 self .assertFalse (result )
504546
505547 # 5. Invalid string with report_unexpected_exception=False → no log
506- result = checker ._convertToBoolean (None , "f " , "notbool" , False , False )
548+ result = checker ._convertToBoolean (None , "fieldName " , "notbool" , False , True )
507549 self .assertEqual (result , "" )
508550
551+ # Assert exactly one error was logged
552+ self .assertEqual (len (checker .errorRecords ), 1 )
553+
554+ err = checker .errorRecords [0 ]
555+ self .assertEqual (err ["field" ], "fieldName" )
556+ self .assertEqual (err ["value" ], "notbool" )
557+ # message should include our literal
558+ self .assertIn ("Invalid String Data" , err ["message" ])
559+ # and code should default to UNEXPECTED_EXCEPTION
560+ self .assertEqual (err ["code" ], ExceptionMessages .RECORD_CHECK_FAILED )
561+
509562 #check for dose sequence
510563 @patch ("ConversionChecker.LookUpData" )
511564 def test_convert_to_dose (self , MockLookUpData ):
@@ -533,7 +586,6 @@ def clear_table(self):
533586 result = self .table .scan ()
534587 items = result .get ("Items" , [])
535588
536-
537589class TestPersonForeNameToFlatJson (unittest .TestCase ):
538590 def test_person_forename_multiple_names_official (self ):
539591 """Test case where multiple name instances exist, and one has use=official with period covering vaccination date"""
@@ -635,7 +687,6 @@ def _run_test(self, expected_forename):
635687 flat_json = self .converter .runConversion (request_json_data , False , True )
636688 self .assertEqual (flat_json [0 ]["PERSON_FORENAME" ], expected_forename )
637689
638-
639690class TestPersonSurNameToFlatJson (unittest .TestCase ):
640691
641692 def test_person_surname_multiple_names_official (self ):
0 commit comments