feat(fase2a): percent-mål mot baseline 0 reiser ValueError (S2.0)
This commit is contained in:
parent
8ec71c9814
commit
9311813080
2 changed files with 23 additions and 1 deletions
|
|
@ -420,6 +420,12 @@ def _goal_limit_if_reached(goal: GoalContract, observed_ore: int, baseline_ore:
|
||||||
if goal.absolute_ore is not None and observed_ore >= goal.absolute_ore:
|
if goal.absolute_ore is not None and observed_ore >= goal.absolute_ore:
|
||||||
return goal.absolute_ore
|
return goal.absolute_ore
|
||||||
if goal.percent is not None:
|
if goal.percent is not None:
|
||||||
|
if baseline_ore <= 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"percent goal ({goal.percent}%) is meaningless against a non-positive baseline "
|
||||||
|
f"({baseline_ore} øre): int(percent/100 * 0) == 0 would falsely read as 'goal "
|
||||||
|
"reached'. Supply an absolute_ore target, or ensure the addressable baseline is > 0."
|
||||||
|
)
|
||||||
threshold = int(goal.percent / 100 * baseline_ore)
|
threshold = int(goal.percent / 100 * baseline_ore)
|
||||||
if observed_ore >= threshold:
|
if observed_ore >= threshold:
|
||||||
return threshold
|
return threshold
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,11 @@ from __future__ import annotations
|
||||||
|
|
||||||
from importlib.resources import files
|
from importlib.resources import files
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from portfolio_optimiser.contracts import GoalContract
|
||||||
from portfolio_optimiser.reference_domain import Project
|
from portfolio_optimiser.reference_domain import Project
|
||||||
from portfolio_optimiser.run import run_portfolio
|
from portfolio_optimiser.run import _goal_limit_if_reached, run_portfolio
|
||||||
from portfolio_optimiser.verdicts import ProposalFeatures, capture_verdict, write_verdict
|
from portfolio_optimiser.verdicts import ProposalFeatures, capture_verdict, write_verdict
|
||||||
|
|
||||||
_MINI_BUNDLE = str(files("portfolio_optimiser").joinpath("data/bundles/bygg-energi-mikro-a"))
|
_MINI_BUNDLE = str(files("portfolio_optimiser").joinpath("data/bundles/bygg-energi-mikro-a"))
|
||||||
|
|
@ -166,3 +169,16 @@ async def test_dropped_verdict_in_kplus1_inbox_reaches_store(
|
||||||
"a verdict dropped as a file in k+1's verdict_dir inbox did not reach the store — the "
|
"a verdict dropped as a file in k+1's verdict_dir inbox did not reach the store — the "
|
||||||
"verdict_dir threading in run_portfolio is detached"
|
"verdict_dir threading in run_portfolio is detached"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_percent_goal_against_zero_baseline_raises_value_error() -> None:
|
||||||
|
"""T-2.0c (direct unit test of the chokepoint): a percent goal against a non-positive baseline is
|
||||||
|
meaningless — ``int(percent/100 * 0) == 0`` would falsely read as 'goal reached'. The guard in
|
||||||
|
``_goal_limit_if_reached`` raises ``ValueError`` instead. Detach the guard → no raise (false
|
||||||
|
'reached') → RED. Tested DIRECTLY on the function: ``_goal_limit_if_reached`` is only called
|
||||||
|
inside a non-empty loop with ``baseline=sum(total_cost)``, and no reference project has zero
|
||||||
|
cost, so a baseline-0 path is not constructable via ``run_portfolio`` — the direct call is the
|
||||||
|
unambiguous, constructable test."""
|
||||||
|
goal = GoalContract(percent=10.0)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
_goal_limit_if_reached(goal, observed_ore=0, baseline_ore=0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue