mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
refetch_solar=False models with stored Solar insights and never calls Google 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e08048244a
commit
c1a558d2b5
1 changed files with 102 additions and 0 deletions
|
|
@ -124,6 +124,18 @@ def _baseline_orchestrator() -> Iterator[MagicMock]:
|
|||
yield orchestrator
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _solar_repository() -> Iterator[MagicMock]:
|
||||
"""Patch the stored-Solar repository for every test — the handler reads
|
||||
stored insights through it whatever refetch_solar says. Default: no stored
|
||||
rows; solar tests override get_many with real payloads."""
|
||||
with patch(
|
||||
"applications.modelling_e2e.handler.SolarPostgresRepository"
|
||||
) as repository:
|
||||
repository.return_value.get_many.return_value = {}
|
||||
yield repository
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Solar throttle: per-container call gap resolved from env (quota/N safety)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -208,6 +220,96 @@ def test_trigger_body_new_flags_default_to_true() -> None:
|
|||
assert result.refetch_solar is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# refetch_solar semantics — False skips the LIVE fetch only, never the measure
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_refetch_solar_false_models_with_stored_solar_and_never_calls_google(
|
||||
_solar_repository: MagicMock,
|
||||
) -> None:
|
||||
"""refetch_solar=False means "don't pay for a Google call", NOT "model
|
||||
without solar": the stored Solar insights are read from the DB and handed to
|
||||
the Modelling stage, and the live Google client is never invoked.
|
||||
|
||||
Provenance: portfolio 814's 2026-07-02 re-model ran with refetch_solar=False
|
||||
and every plan lost its solar candidates (pids 742071/742077/742397/742399 —
|
||||
houses stopped short of goal B with bill-negative ASHP-only plans) even
|
||||
though the `solar` table held healthy payloads for their UPRNs."""
|
||||
# Arrange
|
||||
stored_insights = {"solarPotential": {"maxArrayPanelsCount": 58}}
|
||||
mock_engine = _engine_mock([PROPERTY_ID], [UPRN], [POSTCODE])
|
||||
mock_uow = MagicMock()
|
||||
|
||||
with ExitStack() as stack:
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.os.environ", _ENV)
|
||||
)
|
||||
stack.enter_context(
|
||||
patch(
|
||||
"applications.modelling_e2e.handler._get_engine",
|
||||
return_value=mock_engine,
|
||||
)
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.EpcClientService")
|
||||
).return_value.get_by_uprn.return_value = MagicMock()
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.GeospatialS3Repository")
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.GoogleSolarApiClient")
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler._spatial_for", return_value=None)
|
||||
)
|
||||
mock_live_fetch = stack.enter_context(
|
||||
patch(
|
||||
"applications.modelling_e2e.handler._solar_insights_for",
|
||||
return_value=None,
|
||||
)
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.overlays_from", return_value=[])
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.PropertyOverridesPostgresReader")
|
||||
).return_value.overrides_for_many.return_value = {}
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
|
||||
).return_value.get_many.return_value = [MagicMock()]
|
||||
stack.enter_context(
|
||||
patch(
|
||||
"applications.modelling_e2e.handler.catalogue_snapshot_with_off_catalogue_overrides"
|
||||
)
|
||||
)
|
||||
stack.enter_context(patch("applications.modelling_e2e.handler.Session"))
|
||||
mock_run_modelling = stack.enter_context(
|
||||
patch(
|
||||
"applications.modelling_e2e.handler.run_modelling",
|
||||
return_value=_plan_mock(),
|
||||
)
|
||||
)
|
||||
MockUoW = stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.PostgresUnitOfWork")
|
||||
)
|
||||
MockUoW.return_value.__enter__.return_value = mock_uow
|
||||
MockUoW.return_value.__exit__.return_value = False
|
||||
_solar_repository.return_value.get_many.return_value = {
|
||||
UPRN: stored_insights
|
||||
}
|
||||
|
||||
# Act — _BODY carries refetch_solar=False
|
||||
_call_handler(_BODY)
|
||||
|
||||
# Assert — the stored insights reach the Modelling stage unchanged, and the
|
||||
# paid Google leg is never exercised.
|
||||
assert (
|
||||
mock_run_modelling.call_args.kwargs["solar_insights"] == stored_insights
|
||||
)
|
||||
mock_live_fetch.assert_not_called()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Child SubTask creation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue