mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
added base unit tests
This commit is contained in:
parent
c970cc81ca
commit
9a558c5bb5
1 changed files with 263 additions and 49 deletions
|
|
@ -1,64 +1,278 @@
|
||||||
import pytest
|
import pytest
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from utils.s3 import read_csv_from_s3
|
from backend.Funding import Funding, EligibilityCaveats
|
||||||
from backend.Funding import Funding
|
|
||||||
|
|
||||||
|
|
||||||
def get_funding_data():
|
@pytest.fixture
|
||||||
"""
|
def mock_project_scores_matrix():
|
||||||
This function retrieves the eco project scores matrix and the warm homes local grant funding data
|
data = []
|
||||||
:return:
|
floor_segments = ["0-72", "73-97", "98-199", "200"]
|
||||||
"""
|
starting_bands = ["Low_G", "High_G", "Low_F", "High_F", "Low_E", "High_E", "Low_D", "High_D", "Low_C", "High_C"]
|
||||||
project_scores_matrix = read_csv_from_s3(
|
finishing_bands = ["Low_C", "High_C", "Low_B"] # covers likely improvement targets
|
||||||
bucket_name="retrofit-data-dev",
|
|
||||||
filepath="funding/ECO4 Full Project Scores Matrix.csv",
|
cost = 50.0
|
||||||
|
for floor in floor_segments:
|
||||||
|
for start in starting_bands:
|
||||||
|
for finish in finishing_bands:
|
||||||
|
if start != finish: # skip identical start/finish (no SAP movement)
|
||||||
|
data.append({
|
||||||
|
"Floor Area Segment": floor,
|
||||||
|
"Starting Band": start,
|
||||||
|
"Finishing Band": finish,
|
||||||
|
"Cost Savings": cost
|
||||||
|
})
|
||||||
|
cost += 5.0 # increment to create variety
|
||||||
|
|
||||||
|
return pd.DataFrame(data)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_partial_scores_matrix():
|
||||||
|
return pd.DataFrame([{"dummy": "data"}]) # not used for eligibility tests yet
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_whlg_postcodes():
|
||||||
|
return pd.DataFrame([{"Postcode": "ab12cd"}])
|
||||||
|
|
||||||
|
|
||||||
|
### -------------------------
|
||||||
|
### PRIVATE RENTED SECTOR (PRS)
|
||||||
|
### -------------------------
|
||||||
|
|
||||||
|
def test_eco4_prs_eligible_with_swi(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Private",
|
||||||
)
|
)
|
||||||
project_scores_matrix = pd.DataFrame(project_scores_matrix)
|
|
||||||
project_scores_matrix.columns = ['Floor Area Segment', 'Starting Band', 'Finishing Band', 'Cost Savings']
|
|
||||||
project_scores_matrix["Cost Savings"] = project_scores_matrix["Cost Savings"].astype(float)
|
|
||||||
|
|
||||||
partial_project_scores_matrix = read_csv_from_s3(
|
# The property is:
|
||||||
bucket_name="retrofit-data-dev",
|
# 1) private,
|
||||||
filepath="funding/ECO4_Partial_Project_Scores_Matrix_v6.csv",
|
# 2) EPC E
|
||||||
|
# 3) is getting a solid was measure
|
||||||
|
# so it's eligible for ECO4
|
||||||
|
|
||||||
|
measures = [{"type": "internal_wall_insulation", "is_innovation": False}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=50, # EPC E
|
||||||
|
ending_sap=69,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True,
|
||||||
|
council_tax_band="B"
|
||||||
)
|
)
|
||||||
partial_project_scores_matrix = pd.DataFrame(partial_project_scores_matrix)
|
|
||||||
partial_project_scores_matrix["Cost Savings"] = partial_project_scores_matrix["Cost Savings"].astype(float)
|
|
||||||
|
|
||||||
whlg_eligible_postcodes = read_csv_from_s3(
|
assert funding.eco4_eligible
|
||||||
bucket_name="retrofit-data-dev",
|
assert EligibilityCaveats.TENANT_ON_BENEFITS_OR_LOW_INCOME in funding.eco4_eligibility_caveats
|
||||||
filepath="funding/whlg eligible postcodes.csv",
|
|
||||||
|
|
||||||
|
def test_eco4_prs_not_eligible_high_epc(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
"""Should NOT be eligible if EPC is too high (C or above)."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Private",
|
||||||
)
|
)
|
||||||
whlg_eligible_postcodes = pd.DataFrame(whlg_eligible_postcodes)
|
|
||||||
|
|
||||||
return project_scores_matrix, partial_project_scores_matrix, whlg_eligible_postcodes
|
measures = [{"type": "internal_wall_insulation", "is_innovation": False}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=72, # EPC C (too high)
|
||||||
|
ending_sap=75,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True,
|
||||||
|
council_tax_band="B"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not funding.eco4_eligible
|
||||||
|
|
||||||
|
|
||||||
class TestFunding:
|
def test_gbis_prs_general_eligibility(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
"""PRS EPC D–G & council tax band A–D should trigger GBIS general route."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Private",
|
||||||
|
)
|
||||||
|
|
||||||
def test_prs(self):
|
measures = [{"type": "internal_wall_insulation", "is_innovation": False}]
|
||||||
fps_matrix, pps_matrix, whlg_eligible_postcodes = get_funding_data()
|
funding.check_funding(
|
||||||
funding = Funding(
|
measures=measures,
|
||||||
project_scores_matrix=fps_matrix,
|
starting_sap=65, # EPC D
|
||||||
partial_project_scores_matrix=pps_matrix,
|
ending_sap=70,
|
||||||
whlg_eligible_postcodes=whlg_eligible_postcodes,
|
floor_area=80,
|
||||||
social_cavity_abs_rate=13.5,
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
social_solid_abs_rate=17,
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
private_cavity_abs_rate=13.5,
|
is_cavity=True,
|
||||||
private_solid_abs_rate=17,
|
council_tax_band="A"
|
||||||
tenure="Private",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
measures_1 = [
|
assert funding.gbis_eligible
|
||||||
{"type": "internal_wall_insulation", "is_innovation": False},
|
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
|
||||||
]
|
|
||||||
|
|
||||||
funding.check_funding(
|
|
||||||
measures=measures_1,
|
def test_gbis_prs_low_income_caveat(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
starting_sap=54,
|
"""PRS EPC D–G should flag low-income caveat when low-income route is used."""
|
||||||
ending_sap=69,
|
funding = Funding(
|
||||||
floor_area=73,
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
mainheat_description="Boiler and radiators, mains gas",
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
heating_control_description="Programmer, room thermostat and TRVs",
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
is_cavity=True
|
social_cavity_abs_rate=13.5,
|
||||||
)
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Private",
|
||||||
|
)
|
||||||
|
|
||||||
|
measures = [{"type": "cavity_wall_insulation", "is_innovation": False}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=60, # EPC D
|
||||||
|
ending_sap=70,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True,
|
||||||
|
council_tax_band="B"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert funding.gbis_eligible
|
||||||
|
assert EligibilityCaveats.TENANT_ON_BENEFITS_OR_LOW_INCOME in funding.gbis_eligibility_caveats
|
||||||
|
|
||||||
|
|
||||||
|
### -------------------------
|
||||||
|
### SOCIAL HOUSING
|
||||||
|
### -------------------------
|
||||||
|
|
||||||
|
def test_eco4_sh_epc_e_eligible(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
"""EPC E social housing should be ECO4 eligible without innovation."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Social",
|
||||||
|
)
|
||||||
|
|
||||||
|
measures = [{"type": "internal_wall_insulation", "is_innovation": False}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=50, # EPC E
|
||||||
|
ending_sap=69,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert funding.eco4_eligible
|
||||||
|
|
||||||
|
|
||||||
|
def test_eco4_sh_epc_d_requires_innovation(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
"""EPC D social housing should require an innovation measure."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Social",
|
||||||
|
)
|
||||||
|
|
||||||
|
measures = [{"type": "internal_wall_insulation", "is_innovation": False}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=60, # EPC D
|
||||||
|
ending_sap=69,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not funding.eco4_eligible
|
||||||
|
assert EligibilityCaveats.INNOVATION_REQUIRED in funding.eco4_eligibility_caveats
|
||||||
|
|
||||||
|
|
||||||
|
def test_eco4_sh_solar_pv_requires_heating(mock_project_scores_matrix, mock_partial_scores_matrix, mock_whlg_postcodes):
|
||||||
|
"""Solar PV as innovation measure requires ASHP or HHRSH."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Social",
|
||||||
|
)
|
||||||
|
|
||||||
|
measures = [{"type": "solar_pv", "is_innovation": True}]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=60, # EPC D
|
||||||
|
ending_sap=69,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not funding.eco4_eligible
|
||||||
|
assert EligibilityCaveats.SOLAR_NEEDS_HEATING in funding.eco4_eligibility_caveats
|
||||||
|
|
||||||
|
|
||||||
|
def test_eco4_sh_solar_pv_with_heating_is_ok(mock_project_scores_matrix, mock_partial_scores_matrix,
|
||||||
|
mock_whlg_postcodes):
|
||||||
|
"""Solar PV innovation with ASHP should pass EPC D innovation rule."""
|
||||||
|
funding = Funding(
|
||||||
|
project_scores_matrix=mock_project_scores_matrix,
|
||||||
|
partial_project_scores_matrix=mock_partial_scores_matrix,
|
||||||
|
whlg_eligible_postcodes=mock_whlg_postcodes,
|
||||||
|
social_cavity_abs_rate=13.5,
|
||||||
|
social_solid_abs_rate=17,
|
||||||
|
private_cavity_abs_rate=13.5,
|
||||||
|
private_solid_abs_rate=17,
|
||||||
|
tenure="Social",
|
||||||
|
)
|
||||||
|
|
||||||
|
measures = [
|
||||||
|
{"type": "solar_pv", "is_innovation": True},
|
||||||
|
{"type": "air_source_heat_pump", "is_innovation": False}
|
||||||
|
]
|
||||||
|
funding.check_funding(
|
||||||
|
measures=measures,
|
||||||
|
starting_sap=60, # EPC D
|
||||||
|
ending_sap=69,
|
||||||
|
floor_area=80,
|
||||||
|
mainheat_description="Boiler and radiators, mains gas",
|
||||||
|
heating_control_description="Programmer, room thermostat and TRVs",
|
||||||
|
is_cavity=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert funding.eco4_eligible
|
||||||
|
assert EligibilityCaveats.SOLAR_NEEDS_HEATING not in funding.eco4_eligibility_caveats
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue