"""Tests for splitting comma-separated device identifiers into separate rows.""" from __future__ import annotations import os import unittest from sfda_parser.sfda_scraper.detail_pdf import ( DeviceRow, _expand_device_row, _expand_device_rows, _split_multi_values, parse_affected_device_list, ) class TestSplitMultiValues(unittest.TestCase): def test_splits_comma_separated_values(self): self.assertEqual( _split_multi_values("24B0729, 25B0770, 25H0171, 26B0542"), ["24B0729", "25B0770", "25H0171", "26B0542"], ) def test_single_value_unchanged(self): self.assertEqual(_split_multi_values("24B0729"), ["24B0729"]) def test_splits_semicolon_separated_values(self): self.assertEqual(_split_multi_values("A1; A2; A3"), ["A1", "A2", "A3"]) def test_splits_space_separated_numeric_ids(self): value = ( "0231547054 0231591827 0231758880 0231758898 0231758932 " "0231792414 0231792421 0231912903 0232670901 0232709219 " "0232709254 0232744928 0232784925 0232784989 0232805458 0232837110" ) parts = _split_multi_values(value) self.assertEqual(len(parts), 16) self.assertEqual(parts[0], "0231547054") self.assertEqual(parts[-1], "0232837110") def test_does_not_split_prose_with_spaces(self): self.assertEqual( _split_multi_values("Surgical Instrument Kit"), ["Surgical Instrument Kit"], ) class TestExpandDeviceRow(unittest.TestCase): def _row(self, **kwargs) -> DeviceRow: base = { "material": "Test Device", "material_description": "Description", "catalog_number": "", "udi": "", "serial_no": "", "gstn": "", "batch": "", } base.update(kwargs) return DeviceRow(**base) def test_expands_serial_numbers_into_separate_rows(self): rows = _expand_device_row( self._row(serial_no="24B0729, 25B0770, 25H0171, 26B0542") ) self.assertEqual(len(rows), 4) self.assertEqual([row.serial_no for row in rows], [ "24B0729", "25B0770", "25H0171", "26B0542", ]) for row in rows: self.assertEqual(row.material, "Test Device") self.assertEqual(row.material_description, "Description") def test_expands_catalog_numbers_into_separate_rows(self): rows = _expand_device_row(self._row(catalog_number="CAT-1, CAT-2, CAT-3")) self.assertEqual(len(rows), 3) self.assertEqual([row.catalog_number for row in rows], ["CAT-1", "CAT-2", "CAT-3"]) def test_expands_batch_values_into_separate_rows(self): rows = _expand_device_row(self._row(batch="LOT1, LOT2")) self.assertEqual(len(rows), 2) self.assertEqual([row.batch for row in rows], ["LOT1", "LOT2"]) def test_zips_serial_and_catalog_when_counts_match(self): rows = _expand_device_row( self._row(serial_no="S1, S2", catalog_number="C1, C2") ) self.assertEqual(len(rows), 2) self.assertEqual(rows[0].serial_no, "S1") self.assertEqual(rows[0].catalog_number, "C1") self.assertEqual(rows[1].serial_no, "S2") self.assertEqual(rows[1].catalog_number, "C2") def test_single_value_row_unchanged(self): row = self._row(serial_no="ONLY-ONE", catalog_number="CAT-9") self.assertEqual(_expand_device_row(row), [row]) def test_expand_device_rows_flattens_list(self): devices = [ self._row(serial_no="A, B"), self._row(catalog_number="X, Y, Z"), ] expanded = _expand_device_rows(devices) self.assertEqual(len(expanded), 5) def test_expands_space_separated_udi_values(self): udi_value = "0231547054 0231591827 0231758880" rows = _expand_device_row(self._row(udi=udi_value, material="Monitor")) self.assertEqual(len(rows), 3) self.assertEqual(rows[1].udi, "0231591827") self.assertEqual(rows[2].material, "Monitor") class TestParseAffectedDeviceListExpansion(unittest.TestCase): def test_parse_expands_comma_separated_serials_from_table_like_input(self): # Minimal synthetic check via direct expansion path used by parser output. devices = _expand_device_rows([ DeviceRow( material="Pump", material_description="", catalog_number="REF-100", udi="", serial_no="24B0729, 25B0770, 25H0171, 26B0542", gstn="", batch="", ) ]) self.assertEqual(len(devices), 4) self.assertEqual(devices[0].catalog_number, "REF-100") self.assertEqual(devices[-1].serial_no, "26B0542") class TestSparseTablePdfExpansion(unittest.TestCase): def test_medtronic_pdf_expands_sparse_table_lot_lists(self): pdf_path = os.path.abspath( os.path.join( os.path.dirname(__file__), "../../../../sites/erp.seeraunified.com/public/files/" "SA-28-05-26-1408-ade-detailad786ead786e.pdf", ) ) if not os.path.isfile(pdf_path): self.skipTest(f"fixture PDF not found: {pdf_path}") with open(pdf_path, "rb") as pdf_file: devices = parse_affected_device_list(pdf_file.read()) self.assertGreaterEqual(len(devices), 40) serial_numbers = [device.serial_no for device in devices if device.serial_no] self.assertIn("0012508285", serial_numbers) self.assertIn("0013272796", serial_numbers) if __name__ == "__main__": unittest.main()