portfolio-optimiser/tests/test_costsim.py

92 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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))
def test_estimate_equals_hand_computed() -> None:
"""Anchor the integer-øre math: m-cheap = 10 øre/1k, effort standard (100%), max_tokens 100_000
→ 100_000*100//100 * 10 // 1000 = 1000 øre/run; 3 projects → 3000 øre."""
pricing = costsim.PricingContract(**_VALID_PRICING)
assert costsim.estimate_run_ore("m-cheap", "standard", pricing, max_tokens=100_000) == 1000
assert (
costsim.estimate_portfolio_ore(
"m-cheap", "standard", pricing, n_projects=3, max_tokens=100_000
)
== 3000
)
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"