feat(s36): estimate table (kost-mot-verdi + placeholder rows + sourced quality guidance)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0145ZKPLMVeqM47z2jxxokym
This commit is contained in:
parent
eb889a7f0e
commit
37625435c4
3 changed files with 150 additions and 0 deletions
|
|
@ -76,3 +76,17 @@ def test_unknown_effort_raises() -> None:
|
|||
pricing = costsim.PricingContract(**_VALID_PRICING)
|
||||
with pytest.raises(ValueError, match="unknown effort"):
|
||||
costsim.estimate_run_ore("m-cheap", "turbo", pricing)
|
||||
|
||||
|
||||
def test_estimate_table_structure() -> None:
|
||||
pricing = costsim.PricingContract(**_VALID_PRICING)
|
||||
mm = {"prof": {"proposer": "m-cheap", "checker": "m-dear"}}
|
||||
table = costsim.build_estimate_table(
|
||||
"prof", ["low", "high"], pricing, n_projects=2, model_map=mm
|
||||
)
|
||||
assert len(table["rows"]) == 4 # 2 roles × 2 efforts
|
||||
for row in table["rows"]:
|
||||
assert row["estimat_type"] == "øvre grense"
|
||||
assert isinstance(row["estimat_kr"], str) # edge-formatted string, never a float
|
||||
assert table["kost_mot_verdi"]["kjoring_kostet_ore"] > 0
|
||||
assert table["pricing_source"] == "unit-test prices"
|
||||
|
|
|
|||
|
|
@ -125,3 +125,46 @@ def test_estimate_scales_with_model_and_effort() -> None:
|
|||
assert dear_high != high, "model must scale the estimate"
|
||||
# control: same model + same effort → identical (not incidental)
|
||||
assert costsim.estimate_run_ore("m-cheap", "high", pricing) == high
|
||||
|
||||
|
||||
def test_azure_placeholder_rows_unpriced() -> None:
|
||||
"""The defined azure path: ``--profile azure`` ships REPLACE-WITH-* placeholders → rows are
|
||||
unpriced (``estimat_ore`` None, placeholder label), NEVER a crash. Uses the bundled config."""
|
||||
pricing = costsim.load_pricing()
|
||||
table = costsim.build_estimate_table("azure", ["standard"], pricing, n_projects=4)
|
||||
assert table["rows"], "azure profile must still produce rows"
|
||||
for row in table["rows"]:
|
||||
assert row["estimat_ore"] is None
|
||||
assert "placeholder" in row["estimat_type"]
|
||||
|
||||
|
||||
def test_table_carries_kost_mot_verdi_field() -> None:
|
||||
"""SC5: the table carries a top-level ``kost_mot_verdi`` field ready for the S5.4 value report.
|
||||
Detach point: remove the field from ``build_estimate_table`` → RED."""
|
||||
pricing = costsim.PricingContract(**_TWO_MODEL_PRICING)
|
||||
table = costsim.build_estimate_table(
|
||||
"p", ["standard"], pricing, n_projects=1, model_map={"p": {"proposer": "m-cheap"}}
|
||||
)
|
||||
assert "kost_mot_verdi" in table
|
||||
assert "kjoring_kostet_ore" in table["kost_mot_verdi"]
|
||||
|
||||
|
||||
def test_row_carries_sourced_quality_guidance() -> None:
|
||||
"""Brief Goal 4: a priced row surfaces ``kvalitets_veiledning`` with a source (guidance with
|
||||
kilde, never a bare number). Detach point: drop ``kvalitets_veiledning`` from the row → RED."""
|
||||
pricing = costsim.PricingContract(**_PRICING) # m-known carries guidance
|
||||
table = costsim.build_estimate_table(
|
||||
"p", ["standard"], pricing, n_projects=1, model_map={"p": {"proposer": "m-known"}}
|
||||
)
|
||||
row = table["rows"][0]
|
||||
assert row["kvalitets_veiledning"] is not None
|
||||
assert row["kvalitets_veiledning"]["source"].strip()
|
||||
|
||||
|
||||
def test_table_is_byte_deterministic() -> None:
|
||||
"""Serialized table is byte-stable across identical inputs (sort_keys/indent/LF)."""
|
||||
pricing = costsim.PricingContract(**_TWO_MODEL_PRICING)
|
||||
mm = {"p": {"proposer": "m-cheap", "checker": "m-dear"}}
|
||||
t1 = costsim.build_estimate_table("p", ["low", "high"], pricing, n_projects=3, model_map=mm)
|
||||
t2 = costsim.build_estimate_table("p", ["low", "high"], pricing, n_projects=3, model_map=mm)
|
||||
assert costsim.dump_estimate_table(t1).encode() == costsim.dump_estimate_table(t2).encode()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue