from backend.ecmk_fetcher.xml_processor import ( SapPropertyDetails, flatten_sap_property, parse_rdsap, ) SAMPLE_XML = """
1 Fake Avenue Random AB24 5CD
0 1 Main Dwelling C 7 4 2 100mm 4 4 25.31 2.46 43.61 0 0 26.16 2.44 42.33 1 0 2 Extension C 8 7 AB 3 4 6.85 2.24 4.46 0 0
""" NO_ROOF_XML = """
5 Somewhere XY1 2AB
0 Main Dwelling 10.0 2.5 50.0 0 3.0
""" def test_parse_rdsap_contract(): # arrange + act result: SapPropertyDetails = parse_rdsap(SAMPLE_XML) # assert assert result == { "reference": "1AB245CD", "address": "1, Fake Avenue, Random, AB24 5CD", "property_type": "House", "building_parts": [ { "identifier": "Main Dwelling", "floors": [ { "area_m2": 43.61, "height_m": 2.46, "heat_loss_perimeter_m": 25.31, "party_wall_length_m": 0.0, }, { "area_m2": 42.33, "height_m": 2.44, "heat_loss_perimeter_m": 26.16, "party_wall_length_m": 0.0, }, ], "roof": { "construction": 4, "insulation_location": 2, "insulation_thickness_mm": 100.0, }, }, { "identifier": "Extension", "floors": [ { "area_m2": 4.46, "height_m": 2.24, "heat_loss_perimeter_m": 6.85, "party_wall_length_m": 0.0, } ], "roof": { "construction": 8, "insulation_location": 7, }, }, ], } ND_THICKNESS_XML = """
1 Somewhere AB1 2CD
0 Main Dwelling 4 2 ND 10.0 2.5 50.0 0 0
""" ND_INSULATION_LOCATION_XML = """
1 Somewhere AB1 2CD
0 Main Dwelling 4 ND 250 10.0 2.5 50.0 0 0
""" def test_parse_rdsap_nd_thickness(): # 'ND' (not determined) is a valid value in the wild for Roof-Insulation-Thickness # — it should be retained as-is rather than raising # arrange + act result: SapPropertyDetails = parse_rdsap(ND_THICKNESS_XML) # assert assert result["building_parts"][0]["roof"] == { "construction": 4, "insulation_location": 2, "insulation_thickness_mm": "ND", } def test_parse_rdsap_nd_location(): # 'ND' (not determined) is a valid value in the wild for Roof-Insulation-Location # — it should be retained as-is rather than raising # arrange + act result: SapPropertyDetails = parse_rdsap(ND_INSULATION_LOCATION_XML) # assert assert result["building_parts"][0]["roof"] == { "construction": 4, "insulation_location": "ND", "insulation_thickness_mm": 250, } def test_flatten_full(): # Two building parts; Main Dwelling has two floors + full roof, # Extension has one floor + partial roof (no thickness) # arrange details: SapPropertyDetails = parse_rdsap(SAMPLE_XML) # act result = flatten_sap_property(details) # assert assert result == { "reference": "1AB245CD", "address": "1, Fake Avenue, Random, AB24 5CD", "property_type": "House", "main_dwelling_floor_1_area_m2": 43.61, "main_dwelling_floor_1_height_m": 2.46, "main_dwelling_floor_1_heat_loss_perimeter_m": 25.31, "main_dwelling_floor_1_party_wall_length_m": 0.0, "main_dwelling_floor_2_area_m2": 42.33, "main_dwelling_floor_2_height_m": 2.44, "main_dwelling_floor_2_heat_loss_perimeter_m": 26.16, "main_dwelling_floor_2_party_wall_length_m": 0.0, "main_dwelling_roof_construction": 4, "main_dwelling_roof_insulation_location": 2, "main_dwelling_roof_insulation_thickness_mm": 100.0, "extension_floor_1_area_m2": 4.46, "extension_floor_1_height_m": 2.24, "extension_floor_1_heat_loss_perimeter_m": 6.85, "extension_floor_1_party_wall_length_m": 0.0, "extension_roof_construction": 8, "extension_roof_insulation_location": 7, } def test_flatten_no_roof(): # Single building part with no roof — roof keys must be absent entirely # arrange details: SapPropertyDetails = parse_rdsap(NO_ROOF_XML) # act result = flatten_sap_property(details) # assert assert result == { "reference": "5XY12AB", "address": "5, Somewhere, XY1 2AB", "property_type": "House", "main_dwelling_floor_1_area_m2": 50.0, "main_dwelling_floor_1_height_m": 2.5, "main_dwelling_floor_1_heat_loss_perimeter_m": 10.0, "main_dwelling_floor_1_party_wall_length_m": 3.0, }