Seera-Unified-SFDA/sfda_parser/sfda_scraper/test_detail_pdf_expand.py

159 lines
5.7 KiB
Python

"""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()