test(modelling): pin non-gas->gas + already-insulated-cylinder boiler upgrades

Two more boiler-upgrade cascade pins, validating the existing generator across
fuels and cylinder states (no source change):

- oil combi: an oil boiler (fuel 28, code 130) on a mains-gas street converts to
  a gas condensing combi (fuel 28->26, code 104). Proves the non-gas -> gas
  conversion gated on a mains-gas connection (ADR-0024 revised).
- already-insulated cylinder: a gas boiler heating a pre-jacketed cylinder
  (type 2 / 80 mm, no thermostat) gets a new boiler + a thermostat, with the
  jacket NOT re-applied. Proves the cylinder path's skip-jacket branch against a
  real cert. (Sourced from an LPG re-lodgement whose fuel the Summary mapper
  reads as mains gas 26 — a separate LPG fuel-mapping gap, noted in the test.)

Both pin delta 0 (SAP/CO2/PE) against the relodged after.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-09 16:37:32 +00:00
parent 63dd69ff8b
commit 7bc9797a26
5 changed files with 47 additions and 0 deletions

View file

@ -809,6 +809,53 @@ def test_boiler_combi_overlay_reproduces_the_relodged_after() -> None:
_assert_overlay_reproduces_after(before, after, option.overlay)
def test_oil_combi_overlay_reproduces_the_relodged_after() -> None:
# Arrange — an OIL combi (fuel 28, SAP code 130, no cylinder) on a mains-gas
# street re-lodged as a gas condensing combi (fuel 28->26, code 104, fanned
# flue). Validates the non-gas -> gas conversion: the upgrade targets gas
# because a mains-gas connection is present (ADR-0024 revised). Controls are
# already adequate (2106), so they are unchanged.
before: EpcPropertyData = parse_recommendation_summary(
"boiler_combi_oil_001431_before.pdf"
)
after: EpcPropertyData = parse_recommendation_summary(
"boiler_combi_oil_001431_after.pdf"
)
recommendation: Recommendation | None = recommend_heating(before, _AnyProduct())
assert recommendation is not None
option = next(
o for o in recommendation.options if o.measure_type == "gas_boiler_upgrade"
)
# Act / Assert
_assert_overlay_reproduces_after(before, after, option.overlay)
def test_boiler_with_already_insulated_cylinder_overlay_reproduces_the_relodged_after() -> None:
# Arrange — a gas boiler heating an ALREADY-jacketed cylinder (insulation
# type 2 / 80 mm) with no thermostat, re-lodged as a new gas condensing
# boiler (code 102) with a cylinder thermostat added. Validates the cylinder
# path's skip-jacket branch (the 80 mm jacket is not re-applied) while the
# thermostat is still added. (Sourced from an LPG re-lodgement; the Summary
# mapper reads its fuel as mains gas — fuel 26 — so this exercises the gas
# cylinder path, not a true LPG conversion. The LPG fuel-mapping gap is a
# separate mapper-front concern.)
before: EpcPropertyData = parse_recommendation_summary(
"boiler_cyl_lpg_001431_before.pdf"
)
after: EpcPropertyData = parse_recommendation_summary(
"boiler_cyl_lpg_001431_after.pdf"
)
recommendation: Recommendation | None = recommend_heating(before, _AnyProduct())
assert recommendation is not None
option = next(
o for o in recommendation.options if o.measure_type == "gas_boiler_upgrade"
)
# Act / Assert
_assert_overlay_reproduces_after(before, after, option.overlay)
# --- Solar PV cascade pins (ADR-0026) -------------------------------------
#
# The solar before/after Summaries lodge *synthetic* PV arrays (each 1.00 kWp,