feat(s36): PricingContract + fail-fast loader + placeholder path + quality guidance

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145ZKPLMVeqM47z2jxxokym
This commit is contained in:
Kjell Tore Guttormsen 2026-07-15 09:57:48 +02:00
commit cfa93799a5
4 changed files with 195 additions and 0 deletions

59
tests/test_costsim.py Normal file
View file

@ -0,0 +1,59 @@
"""S3.6 costsim — unit tests: pricing load/fail-fast, deterministic estimate, table, CLI.
Load-bearing detach seams live in ``test_costsim_loadbearing.py``; these are the happy-path +
schema unit tests.
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from pydantic import ValidationError
from portfolio_optimiser import costsim
_VALID_PRICING = {
"source": "unit-test prices",
"date": "2026-07-15",
"models": {
"m-cheap": {
"ore_per_1k_tokens": 10,
"quality_guidance": {"note": "cheap draft model", "source": "test-src 2026-07-15"},
},
"m-dear": {
"ore_per_1k_tokens": 50,
"quality_guidance": {"note": "stronger reasoning", "source": "test-src 2026-07-15"},
},
},
}
def _write(tmp_path: Path, data: dict) -> Path:
p = tmp_path / "pricing.json"
p.write_text(json.dumps(data), encoding="utf-8")
return p
def test_load_pricing_valid(tmp_path: Path) -> None:
pricing = costsim.load_pricing(_write(tmp_path, _VALID_PRICING))
assert pricing.models["m-cheap"].ore_per_1k_tokens == 10
assert pricing.source == "unit-test prices"
def test_load_pricing_bundled_default() -> None:
"""No path → the packaged data/pricing.example.json loads and prices the local model."""
pricing = costsim.load_pricing()
assert "qwen3:4b" in pricing.models
def test_load_pricing_missing_file_raises(tmp_path: Path) -> None:
with pytest.raises(FileNotFoundError):
costsim.load_pricing(tmp_path / "nope.json")
def test_load_pricing_missing_provenance_raises(tmp_path: Path) -> None:
bad = {"models": _VALID_PRICING["models"]} # no source/date
with pytest.raises(ValidationError):
costsim.load_pricing(_write(tmp_path, bad))

View file

@ -13,8 +13,23 @@ import subprocess
import sys
from pathlib import Path
import pytest
from portfolio_optimiser import costsim
_COSTSIM = Path(__file__).resolve().parents[1] / "src" / "portfolio_optimiser" / "costsim.py"
_PRICING = {
"source": "loadbearing-test prices",
"date": "2026-07-15",
"models": {
"m-known": {
"ore_per_1k_tokens": 20,
"quality_guidance": {"note": "known model", "source": "test-src"},
}
},
}
def test_costsim_registered_maf_free() -> None:
"""Meta: costsim.py is registered in the MAF-free guard list, so ``test_okf_is_maf_free``
@ -43,3 +58,38 @@ def test_costsim_import_is_maf_free() -> None:
)
result = subprocess.run([sys.executable, "-c", check], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
def test_missing_price_for_model_fails_fast() -> None:
"""SC1: a genuine (non-placeholder) model absent from the price map fails fast. Detach point:
remove the presence-check raise in ``_price_ore_per_1k`` a guessed/0 price is returned RED."""
pricing = costsim.PricingContract(**_PRICING)
with pytest.raises(ValueError, match="missing price for"):
costsim._price_ore_per_1k("m-unknown", pricing)
def test_priced_model_returns_ore_rate() -> None:
"""Control for the fail-fast seam: a priced model returns its øre rate (proves the raise fires
only on genuine absence, not always)."""
pricing = costsim.PricingContract(**_PRICING)
assert costsim._price_ore_per_1k("m-known", pricing) == 20
def test_placeholder_model_is_unpriced() -> None:
"""The defined azure-placeholder path: a ``REPLACE-WITH-*`` deployment id returns ``None`` (not
priced, not a crash) so ``--profile azure`` (all placeholders) is well-defined."""
pricing = costsim.PricingContract(**_PRICING)
assert costsim._price_ore_per_1k("REPLACE-WITH-FOUNDRY-DEPLOYMENT", pricing) is None
def test_quality_guidance_is_sourced_never_number() -> None:
"""Brief Goal 4: per-model quality trade-offs are sourced GUIDANCE (prose note + provenance
source), never a bare measured number. Detach point: ship a guidance entry without a source
the ``QualityGuidance.source`` (min_length=1) requirement makes load RED."""
pricing = costsim.load_pricing()
guided = [p for p in pricing.models.values() if p.quality_guidance is not None]
assert guided, "bundled pricing must carry per-model quality guidance"
for price in guided:
assert price.quality_guidance is not None
assert price.quality_guidance.source.strip()
assert price.quality_guidance.note.strip()