import pytest import pandas as pd from unittest.mock import Mock from epc_api.client import EpcClient from backend.Property import Property from open_uprn.OpenUprnClient import OpenUprnClient from model_data.EpcClean import EpcClean # Define some test data mock_epc_response = { "rows": [ { "inspection-date": "2023-06-01", "some-other-key": "some-value", "roof-description": "Roof Description", "walls-description": "Walls Description", "windows-description": "Windows Description", "mainheat-description": "Main Heating Description", "hotwater-description": "Hot Water Description", "transaction-type": "rental", "lighting-description": "Good Lighting Efficiency", "energy-consumption-current": "50", "co2-emissions-current": "123", "mechanical-ventilation": "natural", 'photo-supply': 0, "solar-water-heating-flag": "N", "wind-turbine-count": 0, "extension-count": 0, "heat-loss-corridor": "no corridor", "unheated-corridor-length": 0, "mains-gas-flag": "Y", "floor-height": 2.5, "total-floor-area": 100 }, { "inspection-date": "2023-05-01", "some-other-key": "some-other-value", "roof-description": "Roof Description", "walls-description": "Walls Description", "windows-description": "Windows Description", "mainheat-description": "Main Heating Description", "hotwater-description": "Hot Water Description", "transaction-type": "rental", "lighting-description": "Good Lighting Efficiency", "energy-consumption-current": "50", "co2-emissions-current": "123", "mechanical-ventilation": "natural", 'photo-supply': 0, "solar-water-heating-flag": "N", "wind-turbine-count": 0, "extension-count": 0, "heat-loss-corridor": "no corridor", "unheated-corridor-length": 0, "mains-gas-flag": "Y", "floor-height": 2.5, "total-floor-area": 100 } ] } mock_epc_response_dupe = { 'rows': [ { 'inspection-date': '2023-06-01', 'some-other-key': 'some-value', 'roof-description': 'Roof Description', 'walls-description': 'Walls Description', 'windows-description': 'Windows Description', 'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description', "transaction-type": "rental", "lighting-description": "Good Lighting Efficiency", "energy-consumption-current": "50", "co2-emissions-current": "123", "mechanical-ventilation": "natural", 'photo-supply': 0, "solar-water-heating-flag": "N", "wind-turbine-count": 0, "extension-count": 0, "heat-loss-corridor": "no corridor", "unheated-corridor-length": 0, "mains-gas-flag": "Y", "floor-height": 2.5, "total-floor-area": 100 }, { 'inspection-date': '2023-05-01', 'some-other-key': 'some-other-value', 'roof-description': 'Roof Description', 'walls-description': 'Walls Description', 'windows-description': 'Windows Description', 'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description', "transaction-type": "rental", "lighting-description": "Good Lighting Efficiency", "energy-consumption-current": "50", "co2-emissions-current": "123", "mechanical-ventilation": "natural", 'photo-supply': 0, "solar-water-heating-flag": "N", "wind-turbine-count": 0, "extension-count": 0, "heat-loss-corridor": "no corridor", "unheated-corridor-length": 0, "mains-gas-flag": "Y", "floor-height": 2.5, "total-floor-area": 100 }, { 'inspection-date': '2023-06-01', 'some-other-key': 'duplicate-date', 'roof-description': 'Roof Description', 'walls-description': 'Walls Description', 'windows-description': 'Windows Description', 'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description', "transaction-type": "rental", "lighting-description": "Good Lighting Efficiency", "energy-consumption-current": "50", "co2-emissions-current": "123", "mechanical-ventilation": "natural", 'photo-supply': 0, "solar-water-heating-flag": "N", "wind-turbine-count": 0, "extension-count": 0, "heat-loss-corridor": "no corridor", "unheated-corridor-length": 0, "mains-gas-flag": "Y", "floor-height": 2.5, "total-floor-area": 100 } ] } class TestProperty: @pytest.fixture(autouse=True) def property_instance(self, mock_epc_client, mock_open_uprn_client, mock_cleaner): return Property(1, "AB12CD", "Test Address", epc_client=mock_epc_client) @pytest.fixture(autouse=True) def property_instance_dupe_data(self, mock_epc_client_dupe_data): return Property(2, "AB12CD", "Test Address", epc_client=mock_epc_client_dupe_data) @pytest.fixture def mock_epc_client(self): mock_epc_client = Mock(spec=EpcClient()) 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()) 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_open_uprn_client(self): mock_open_uprn_client = Mock(spec=OpenUprnClient(path=None, uprns=[12345])) mock_open_uprn_client.data = pd.DataFrame( [ {"UPRN": 12345, "longitude": 1.2345, "latitude": 2.3456}, {"UPRN": 12346, "longitude": 3.4567, "latitude": 4.5678} ] ) return mock_open_uprn_client @pytest.fixture def mock_cleaner(self): lighting_averages = [ {'lighting-description': 'good lighting efficiency', 'low-energy-lighting': 99.26666666666667}, {'lighting-description': 'excellent lighting efficiency', 'low-energy-lighting': 100.0}, {'lighting-description': 'below average lighting efficiency', 'low-energy-lighting': 0.0} ] cleaner_spec = EpcClean( data=[ {"roof-description": "Roof Description"}, {"walls-description": "Walls Description"}, {"windows-description": "Windows Description"}, {"mainheat-description": "Main Heating Description"}, {"hotwater-description": "Hot Water Description"}, {"lighting-description": "Good Lighting Efficiency"}, {"low-energy-lighting": 0} ], lighting_averages=lighting_averages ) mock_cleaner = Mock(spec=cleaner_spec) mock_cleaner.cleaned = { "roof-description": [{"original_description": "Roof Description"}], "walls-description": [{"original_description": "Walls Description"}], "windows-description": [{"original_description": "Windows Description"}], "mainheat-description": [{"original_description": "Main Heating Description"}], "hotwater-description": [{"original_description": "Hot Water Description"}], "lighting-description": [{"original_description": "Good Lighting Efficiency"}] } 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" inst2 = Property(3, "AB12CD", "Test Address") assert inst2.epc_client.auth_token inst3 = Property(4, "AB12CD", "Test Address", data={"some": "data"}) assert inst3.data == {"some": "data"} data = inst3.search_address_epc() assert data is None 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) # Verify that the components are set correctly assert property_instance.roof == {"original_description": "Roof Description"} assert property_instance.walls == {"original_description": "Walls Description"} assert property_instance.windows == {"original_description": "Windows Description"} assert property_instance.main_heating == {"original_description": "Main Heating Description"} assert property_instance.hotwater == {"original_description": "Hot Water Description"} def test_get_components_without_cleaned_data(self, property_instance, mock_cleaner): # Modify the mock EpcClean to not have cleaned data mock_cleaner.cleaned = {} # 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) def test_get_components_no_data(self, property_instance, mock_cleaner): # 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() # Verify that ValueError is raised when no attributes are found with pytest.raises(ValueError, match="Either No attributes or multiple found for roof-description"): property_instance.get_components(mock_cleaner.cleaned) def test_get_components_multiple_attributes(self, property_instance, mock_cleaner): # This shouldn't happen - it would mean a cleaning error property_instance.search_address_epc() cleaned = { "roof-description": [ {"original_description": "Roof Description"}, {"original_description": "Roof Description"} ] } # 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)