diff --git a/datatypes/epc/domain/epc_property_data.py b/datatypes/epc/domain/epc_property_data.py index a49010d3..17437f6f 100644 --- a/datatypes/epc/domain/epc_property_data.py +++ b/datatypes/epc/domain/epc_property_data.py @@ -35,15 +35,18 @@ class BuildingPartIdentifier(Enum): OTHER = "other" @classmethod - def from_api_string(cls, api_identifier: Optional[str]) -> "BuildingPartIdentifier": + def from_api_string( + cls, api_identifier: Optional[object] + ) -> "BuildingPartIdentifier": """Map a gov-EPC API `BuildingPart.identifier` to its canonical member. "Main Dwelling" → MAIN; "Extension N" → EXTENSION_N - (for N in 1..4). `None` (permitted by the 21_0_1 schema) and - anything unrecognised fall to OTHER. + (for N in 1..4). `None` (permitted by the 21_0_1 schema), a non-string + identifier (some SAP-16.x certs lodge a bare int), and anything else + unrecognised fall to OTHER. """ if api_identifier == "Main Dwelling": return cls.MAIN - if api_identifier is not None: + if isinstance(api_identifier, str): match = _API_EXTENSION.match(api_identifier) if match is not None: return cls.extension(int(match.group(1))) diff --git a/tests/datatypes/epc/domain/test_building_part_identifier.py b/tests/datatypes/epc/domain/test_building_part_identifier.py new file mode 100644 index 00000000..d792f45a --- /dev/null +++ b/tests/datatypes/epc/domain/test_building_part_identifier.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from datatypes.epc.domain.epc_property_data import BuildingPartIdentifier + + +def test_main_dwelling_maps_to_main() -> None: + assert BuildingPartIdentifier.from_api_string("Main Dwelling") == ( + BuildingPartIdentifier.MAIN + ) + + +def test_extension_string_maps_to_numbered_extension() -> None: + assert BuildingPartIdentifier.from_api_string("Extension 2") == ( + BuildingPartIdentifier.EXTENSION_2 + ) + + +def test_none_falls_to_other() -> None: + assert BuildingPartIdentifier.from_api_string(None) == BuildingPartIdentifier.OTHER + + +def test_non_string_identifier_falls_to_other_not_crash() -> None: + # Some SAP-16.x certs lodge a bare int building-part identifier (e.g. the + # second part as `1` after "Main Dwelling"). This previously raised + # `TypeError: expected string or bytes-like object, got 'int'` and failed + # the whole property — now it falls to OTHER like any unrecognised value. + assert BuildingPartIdentifier.from_api_string(1) == BuildingPartIdentifier.OTHER