diff --git a/backend/tests/test_property.py b/backend/tests/test_property.py index 0113d690..09594a40 100644 --- a/backend/tests/test_property.py +++ b/backend/tests/test_property.py @@ -9,6 +9,7 @@ from etl.epc_clean.EpcClean import EpcClean mock_epc_response = { "rows": [ { + "tenure": "rental (social)", "lmk-key": 1, "uprn": 1, "number-habitable-rooms": 5, @@ -17,7 +18,7 @@ mock_epc_response = { "inspection-date": "2023-06-01", 'lodgement-datetime': '2023-06-01 20:29:01', "some-other-key": "some-value", - "roof-description": "Roof Description", + "roof-description": "pitched, no insulation", "walls-description": "Walls Description", "windows-description": "Windows Description", "mainheat-description": "Main Heating Description", @@ -168,29 +169,54 @@ mock_epc_response_dupe = { class TestProperty: + @pytest.fixture(autouse=True) - def property_instance(self, mock_epc_client, mock_cleaner): - property_instance = Property(1, "AB12CD", "Test Address", epc_client=mock_epc_client) + def mock_photo_supply_lookup(self): + return pd.DataFrame( + [ + dict( + tenure="rental (social)", + built_form="Detached", + property_type="House", + construction_age_band="England and Wales: 1967-1975", + is_flat=False, + is_pitched=True, + is_roof_room=False, + floor_area_decile=2, + photo_supply_median=40 + ) + ] + ) + + @pytest.fixture(autouse=True) + def mock_floor_area_decile_thresholds(self): + return pd.DataFrame( + {"floor_area_decile_thresholds": [0, 10, 30, 50]} + ) + + @pytest.fixture(autouse=True) + def property_instance(self, mock_cleaner): + property_instance = Property(id=1, postcode="AB12CD", address="Test Address", data=mock_epc_response["rows"][0]) return property_instance @pytest.fixture(autouse=True) - def property_instance_dupe_data(self, mock_epc_client_dupe_data): - property_instance_dupe_data = Property(2, "AB12CD", "Test Address", epc_client=mock_epc_client_dupe_data) + def property_instance_dupe_data(self): + property_instance_dupe_data = Property(id=2, postcode="AB12CD", address="Test Address") return property_instance_dupe_data - @pytest.fixture - def mock_epc_client(self): - mock_epc_client = Mock(spec=EpcClient(auth_token="mocked_auth_token")) - mock_epc_client.domestic.search.return_value = mock_epc_response.copy() - mock_epc_client.auth_token = "mocked_auth_token" - return mock_epc_client - - @pytest.fixture - def mock_epc_client_dupe_data(self): - mock_epc_client_dupe_data = Mock(spec=EpcClient(auth_token="mocked_auth_token")) - mock_epc_client_dupe_data.domestic.search.return_value = mock_epc_response_dupe.copy() - mock_epc_client_dupe_data.auth_token = "mocked_auth_token" - return mock_epc_client_dupe_data + # @pytest.fixture + # def mock_epc_client(self): + # mock_epc_client = Mock(spec=EpcClient(auth_token="mocked_auth_token")) + # mock_epc_client.domestic.search.return_value = mock_epc_response.copy() + # mock_epc_client.auth_token = "mocked_auth_token" + # return mock_epc_client + # + # @pytest.fixture + # def mock_epc_client_dupe_data(self): + # mock_epc_client_dupe_data = Mock(spec=EpcClient(auth_token="mocked_auth_token")) + # mock_epc_client_dupe_data.domestic.search.return_value = mock_epc_response_dupe.copy() + # mock_epc_client_dupe_data.auth_token = "mocked_auth_token" + # return mock_epc_client_dupe_data @pytest.fixture def mock_cleaner(self): @@ -229,7 +255,11 @@ class TestProperty: } mock_cleaner.cleaned = { - "roof-description": [{"original_description": "Roof Description"}], + "roof-description": [ + {"original_description": "Roof Description"}, + {"original_description": "pitched, no insulation", "is_pitched": True, "is_flat": False, + "is_roof_room": False} + ], "walls-description": [walls_data], "windows-description": [{"original_description": "Windows Description"}], "mainheat-description": [{"original_description": "Main Heating Description"}], @@ -240,37 +270,32 @@ class TestProperty: } return mock_cleaner - def test_init(self, mock_epc_client): - inst1 = Property(0, "AB12CD", "Test Address", epc_client=mock_epc_client) - # Should be mocked auth token - assert inst1.epc_client.auth_token == "mocked_auth_token" + def test_init(self): + inst1 = Property(0, postcode="AB12CD", address="Test Address") - inst2 = Property(3, "AB12CD", "Test Address", epc_client=mock_epc_client) - assert inst2.epc_client.auth_token + assert inst1.data is None - inst3 = Property(4, "AB12CD", "Test Address", data={"some": "data"}, epc_client=mock_epc_client) - assert inst3.data == {"some": "data"} + inst2 = Property(3, "AB12CD", "Test Address") + assert inst2.id == 3 - data = inst3.search_address_epc() - assert data is None + inst3 = Property(4, "AB12CD", "Test Address", data={"some": "data", "uprn": 123}) + assert inst3.data == {"some": "data", "uprn": 123} - def test_search_address_epc(self, property_instance): - # Call the method to test - property_instance.search_address_epc() - - # Verify that the correct data is being returned - assert property_instance.data == mock_epc_response["rows"][0] - - def test_search_address_epc_multiple_results(self, property_instance_dupe_data, mock_epc_client_dupe_data): - with pytest.raises(Exception, match="More than one result found for this address - investigate me"): - property_instance_dupe_data.search_address_epc() - - def test_get_components(self, property_instance, mock_cleaner, mock_epc_client): - property_instance.search_address_epc() - property_instance.get_components(mock_cleaner.cleaned) + def test_get_components( + self, property_instance, mock_cleaner, mock_photo_supply_lookup, mock_floor_area_decile_thresholds + ): + property_instance.get_components( + mock_cleaner.cleaned, + photo_supply_lookup=mock_photo_supply_lookup, + floor_area_decile_thresholds=mock_floor_area_decile_thresholds + ) # Verify that the components are set correctly - assert property_instance.roof == {"original_description": "Roof Description"} + assert property_instance.roof == { + 'original_description': 'pitched, no insulation', 'is_pitched': True, + 'is_flat': False, 'is_roof_room': False + } + assert property_instance.walls == { "original_description": "Walls Description", "is_cavity_wall": True, @@ -294,24 +319,15 @@ class TestProperty: # Verify that ValueError is raised when EpcClean doesn't contain cleaned data with pytest.raises(ValueError, match="Cleaner does not contain cleaned data"): - property_instance.get_components(mock_cleaner.cleaned) + property_instance.get_components(mock_cleaner.cleaned, pd.DataFrame(), pd.DataFrame()) - def test_get_components_no_data(self, property_instance, mock_cleaner): + def test_get_components_no_attributes( + self, property_instance, mock_cleaner, mock_photo_supply_lookup, mock_floor_area_decile_thresholds + ): # Modify the mock cleaner to have no attributes for a specific description mock_cleaner.cleaned = { "roof-description": [] } - - # Verify that ValueError is raised when no attributes are found - with pytest.raises(ValueError, match="Property does not contain data"): - property_instance.get_components(mock_cleaner.cleaned) - - def test_get_components_no_attributes(self, property_instance, mock_cleaner): - # Modify the mock cleaner to have no attributes for a specific description - mock_cleaner.cleaned = { - "roof-description": [] - } - property_instance.search_address_epc() property_instance.data["roof-description"] = "Pitched, no insulation" property_instance.walls = { "original_description": "Walls Description", @@ -332,14 +348,17 @@ class TestProperty: } # Assert backup cleaning has been applied - property_instance.get_components(mock_cleaner.cleaned) + property_instance.get_components( + mock_cleaner.cleaned, mock_photo_supply_lookup, mock_floor_area_decile_thresholds + ) assert property_instance.roof["clean_description"] == "Pitched, no insulation" assert property_instance.roof["is_pitched"] - def test_get_components_multiple_attributes(self, property_instance, mock_cleaner): + def test_get_components_multiple_attributes( + self, property_instance, mock_cleaner, mock_photo_supply_lookup, mock_floor_area_decile_thresholds + ): # This shouldn't happen - it would mean a cleaning error - property_instance.search_address_epc() property_instance.data["roof-description"] = "Roof Description" cleaned = { "roof-description": [ @@ -350,10 +369,10 @@ class TestProperty: # Verify that ValueError is raised when multiple attributes are found with pytest.raises(ValueError, match="Either No attributes or multiple found for roof-description"): - property_instance.get_components(cleaned) + property_instance.get_components(cleaned, mock_photo_supply_lookup, mock_floor_area_decile_thresholds) - def test_set_spatial(self, mock_epc_client): - prop = Property(1, "AB12CD", "Test Address", mock_epc_client) + def test_set_spatial(self): + prop = Property(1, postcode="AB12CD", address="Test Address") spatial1 = pd.DataFrame([{ 'X_COORDINATE': 411143.0, 'Y_COORDINATE': 281701.0, 'LATITUDE': 52.4331896, 'LONGITUDE': -1.8375238, @@ -367,7 +386,7 @@ class TestProperty: assert prop.is_heritage assert prop.restricted_measures - prop2 = Property(1, "AB12CD", "Test Address", mock_epc_client) + prop2 = Property(1, "AB12CD", "Test Address") spatial2 = pd.DataFrame([{ 'X_COORDINATE': 411143.0, 'Y_COORDINATE': 281701.0, 'LATITUDE': 52.4331896, 'LONGITUDE': -1.8375238, @@ -381,10 +400,10 @@ class TestProperty: assert not prop2.is_heritage assert not prop2.restricted_measures - def test_set_floor_level(self, mock_epc_client): + def test_set_floor_level(self): # In this case, we have a flat which looks looks it's on the first floor, but it's actually on the ground # floor, so we should set floor_level to 0 - prop = Property(1, "AB12CD", "Test Address", mock_epc_client) + prop = Property(1, postcode="AB12CD", address="Test Address") prop.data = {'floor-level': '01', 'property-type': 'Flat'} prop.floor = { 'original_description': 'Solid, no insulation (assumed)', 'clean_description': 'Solid, no insulation', @@ -400,7 +419,7 @@ class TestProperty: # This property is labelled as being on the ground floor but actually has another property below # so we set floor level to 1 - prop2 = Property(1, "AB12CD", "Test Address", mock_epc_client) + prop2 = Property(1, postcode="AB12CD", address="Test Address") prop2.data = {'floor-level': 'Ground', 'property-type': 'Flat'} prop2.floor = { 'original_description': '(Another dwelling below)', 'clean_description': 'Solid, no insulation', @@ -415,7 +434,7 @@ class TestProperty: assert prop2.floor_level == 1 # this property is correctly labelled as being on the 2nd floor - prop3 = Property(1, "AB12CD", "Test Address", mock_epc_client) + prop3 = Property(1, postcode="AB12CD", address="Test Address") prop3.data = {'floor-level': '02', 'property-type': 'Flat'} prop3.floor = { 'original_description': '(Another dwelling below)', 'clean_description': 'Solid, no insulation', @@ -430,7 +449,7 @@ class TestProperty: assert prop3.floor_level == 2 # Example of a house - prop4 = Property(1, "AB12CD", "Test Address", mock_epc_client) + prop4 = Property(1, postcode="AB12CD", address="Test Address") prop4.data = {'floor-level': '', 'property-type': 'House'} prop4.floor = { 'original_description': '(Another dwelling below)', 'clean_description': 'Solid, no insulation',