import pytest from unittest.mock import MagicMock from recommendations import recommendation_utils from datatypes.enums import QuantityUnits class TestRecommendationUtils: @pytest.fixture def property_mock(self): PropertyMock = MagicMock() PropertyMock.data = { 'total_floor_area_group_decile': 'Decile 1', 'number-habitable-rooms': 3, 'number-heated-rooms': 2 } return PropertyMock def test_r_value_per_mm_to_u_value(self): assert recommendation_utils.r_value_per_mm_to_u_value(1, 2) == 0.5 with pytest.raises(ZeroDivisionError): recommendation_utils.r_value_per_mm_to_u_value(0, 2) def test_calculate_u_value_uplift(self): assert recommendation_utils.calculate_u_value_uplift(1, 2) == (0.33333333333333337, 0.6666666666666666) with pytest.raises(ZeroDivisionError): recommendation_utils.calculate_u_value_uplift(0, 2) with pytest.raises(ZeroDivisionError): recommendation_utils.calculate_u_value_uplift(1, 0) def test_is_diminishing_returns(self): assert not recommendation_utils.is_diminishing_returns([1, 2, 3], 1, 1, 1) assert recommendation_utils.is_diminishing_returns([1, 2, 3], 0.5, 1, 1) assert not recommendation_utils.is_diminishing_returns([], 1, None, 1) def test_update_lowest_selected_u_value(self): assert recommendation_utils.update_lowest_selected_u_value(1, 2) == 1 assert recommendation_utils.update_lowest_selected_u_value(None, 2) == 2 assert recommendation_utils.update_lowest_selected_u_value(1, 0.5) == 0.5 def test_get_recommended_part(self): part = {'depths': [1, 2, 3]} assert recommendation_utils.get_recommended_part( part=part, selected_depth=1, selected_total_cost=50, quantity=99, quantity_unit="m2" ) == {'depths': [1], 'estimated_cost': 50, 'quantity': 99, 'quantity_unit': QuantityUnits.m2.value} def test_get_uvalue_estimate(self, property_mock): uvalue_estimates = [ { 'total-floor-area_group': 'Decile 1', 'number-habitable-rooms': 3, 'number-heated-rooms': 2, 'median_thermal_transmittance': 1 }, { 'total-floor-area_group': 'Decile 1', 'number-habitable-rooms': 3, 'number-heated-rooms': 2, 'median_thermal_transmittance': 2 } ] assert recommendation_utils.get_uvalue_estimate(uvalue_estimates, property_mock, "Decile 1") == 1.5 with pytest.raises(ValueError): recommendation_utils.get_uvalue_estimate([], property_mock, "Decile 1") # Test with missing 'median_thermal_transmittance' key uvalue_estimates_missing_key = [ { 'total-floor-area_group': 'Decile 1', 'number-habitable-rooms': 3, 'number-heated-rooms': 2 } ] with pytest.raises(KeyError): recommendation_utils.get_uvalue_estimate(uvalue_estimates_missing_key, property_mock, "Decile 1") def test_get_roof_u_value(self): # Test case 1: Insulation thickness is known and is_loft is True description_dict = { 'insulation_thickness': '50', 'is_loft': True, 'is_roof_room': False, 'is_thatched': False, 'has_dwelling_above': False, 'is_flat': False, 'is_pitched': True, 'is_at_rafters': False, } for age_band in ["A", "B", "C", "D"]: assert recommendation_utils.get_roof_u_value(description_dict, age_band) == 0.68 def test_get_roof_u_value_case_2(self): description_dict = { 'original_description': 'Pitched, 400+ mm insulation at joists', 'clean_description': 'Pitched, 400+ mm insulation at joists', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': False, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '400+' } age_band = "J" u_value = recommendation_utils.get_roof_u_value(description_dict, age_band) assert u_value == 0.16, f"Expected 0.16, but got {u_value}" def test_get_roof_u_value_case_3(self): description_dict = { 'original_description': 'Room-in-roof, 200 mm insulation at rafters', 'clean_description': 'Room-in-roof, 200 mm insulation at rafters', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False, 'is_roof_room': True, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': True, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '200' } age_band = "J" u_value = recommendation_utils.get_roof_u_value(description_dict, age_band) assert u_value == 0.21, f"Expected 0.21, but got {u_value}" def test_get_roof_u_value_case_4(self): description_dict = { 'original_description': 'Pitched, below average insulation', 'clean_description': 'Pitched, below average insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': False, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': 'below average' } age_band = "E" u_value = recommendation_utils.get_roof_u_value(description_dict, age_band) assert u_value == 1.5, f"Expected 1.5, but got {u_value}" def test_get_roof_u_value_case_5(): # Test case where insulation thickness is exactly specified description_dict = { 'original_description': 'Pitched, 100mm insulation', 'clean_description': 'Pitched, 100mm insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': False, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '100' } age_band = "G" u_value = get_roof_u_value(description_dict, age_band, table_s9, table_s10) assert u_value == 0.40, f"Expected 0.40, but got {u_value}" def test_get_roof_u_value_case_6(): # Test case for a thatched roof description_dict = { 'original_description': 'Thatched, 75mm insulation', 'clean_description': 'Thatched, 75mm insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False, 'is_roof_room': False, 'is_loft': False, 'is_flat': False, 'is_thatched': True, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '75' } age_band = "H" u_value = get_roof_u_value(description_dict, age_band, table_s9, table_s10) assert u_value == 0.22, f"Expected 0.22, but got {u_value}" def test_get_roof_u_value_case_7(): # Test case where the roof has a room in it description_dict = { 'original_description': 'Pitched, room-in-roof, 100mm insulation', 'clean_description': 'Pitched, room-in-roof, 100mm insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': True, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '100' } age_band = "J" u_value = get_roof_u_value(description_dict, age_band, table_s9, table_s10) assert u_value == 0.30, f"Expected 0.30, but got {u_value}" def test_get_roof_u_value_case_8(): # Test case where there is a dwelling above the roof, U-value should be 0 description_dict = { 'original_description': 'Pitched, 100mm insulation', 'clean_description': 'Pitched, 100mm insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': False, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False, 'has_dwelling_above': True, 'is_valid': True, 'insulation_thickness': '100' } age_band = "J" u_value = get_roof_u_value(description_dict, age_band, table_s9, table_s10) assert u_value == 0.0, f"Expected 0.0, but got {u_value}"