Spaces:
Sleeping
Sleeping
Claude
test: rΓ©organiser les 110 fichiers tests/test_*.py par cercle architectural
d109222 unverified | """Tests Sprint 79 β A.I.6 : projection de coΓ»t en volume cible. | |
| Couvre : | |
| 1. ``project_cost_total`` : | |
| - Calcul linΓ©aire en target_pages | |
| - target_pages = 0 β 0 | |
| - target_pages nΓ©gatif β None | |
| - cost_per_1k_pages_eur = None β None | |
| 2. ``project_co2_total`` : idem COβ. | |
| 3. ``project_engine`` : retour structurΓ©. | |
| 4. ``project_all_engines`` : N moteurs en une passe ; ValueError | |
| sur target nΓ©gatif. | |
| 5. ``cost_gap_table`` : | |
| - Delta absolu/relatif vs baseline | |
| - Baseline = 0 β delta_rel None | |
| - Baseline inconnue β KeyError | |
| 6. Cas rΓ©aliste BnF : 80 000 pages BMS β 4 moteurs avec coΓ»ts | |
| diffΓ©rents β projection cohΓ©rente. | |
| """ | |
| from __future__ import annotations | |
| import pytest | |
| from picarones.measurements.cost_projection import ( | |
| ProjectedCost, | |
| cost_gap_table, | |
| project_all_engines, | |
| project_co2_total, | |
| project_cost_total, | |
| project_engine, | |
| ) | |
| from picarones.measurements.pricing import EngineCost | |
| def _ec(name: str, cost_1k: float | None, co2_1k: float | None = None, | |
| type_: str = "cloud_api") -> EngineCost: | |
| return EngineCost( | |
| engine_key=name, | |
| type=type_, | |
| cost_per_1k_pages_eur=cost_1k, | |
| co2_per_1k_pages_g=co2_1k, | |
| ) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. project_cost_total | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestProjectCostTotal: | |
| def test_linear_scaling(self) -> None: | |
| ec = _ec("x", 10.0) # 10 β¬/1000 pages | |
| assert project_cost_total(ec, 1000) == 10.0 | |
| assert project_cost_total(ec, 10000) == 100.0 | |
| assert project_cost_total(ec, 80000) == 800.0 | |
| def test_zero_pages(self) -> None: | |
| ec = _ec("x", 10.0) | |
| assert project_cost_total(ec, 0) == 0.0 | |
| def test_negative_returns_none(self) -> None: | |
| ec = _ec("x", 10.0) | |
| assert project_cost_total(ec, -100) is None | |
| def test_no_cost_data(self) -> None: | |
| ec = _ec("x", None) | |
| assert project_cost_total(ec, 1000) is None | |
| def test_fractional_pages(self) -> None: | |
| ec = _ec("x", 10.0) | |
| # 500 pages β 5 β¬ | |
| assert project_cost_total(ec, 500) == 5.0 | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2. project_co2_total | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestProjectCo2: | |
| def test_linear_scaling(self) -> None: | |
| ec = _ec("x", None, co2_1k=50.0) # 50 g/1000 pages | |
| assert project_co2_total(ec, 10000) == 500.0 | |
| def test_no_co2_data(self) -> None: | |
| ec = _ec("x", 10.0) # cost mais pas co2 | |
| assert project_co2_total(ec, 1000) is None | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3. project_engine | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestProjectEngine: | |
| def test_full_struct(self) -> None: | |
| ec = _ec("tess", 5.0, co2_1k=20.0, type_="local") | |
| proj = project_engine(ec, 80000) | |
| assert isinstance(proj, ProjectedCost) | |
| assert proj.engine_key == "tess" | |
| assert proj.target_pages == 80000 | |
| assert proj.cost_total_eur == 400.0 | |
| assert proj.co2_total_g == 1600.0 | |
| assert proj.cost_per_1k_pages_eur == 5.0 | |
| assert proj.type == "local" | |
| def test_as_dict(self) -> None: | |
| ec = _ec("tess", 5.0) | |
| proj = project_engine(ec, 1000) | |
| d = proj.as_dict() | |
| assert d["engine_key"] == "tess" | |
| assert d["cost_total_eur"] == 5.0 | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4. project_all_engines | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestProjectAll: | |
| def test_multi_engines(self) -> None: | |
| engines = { | |
| "tess": _ec("tess", 1.0, type_="local"), | |
| "mistral": _ec("mistral", 3.5, type_="cloud_api"), | |
| "gpt4o": _ec("gpt4o", 7.5, type_="cloud_api"), | |
| } | |
| result = project_all_engines(engines, 10000) | |
| assert result["tess"].cost_total_eur == 10.0 | |
| assert result["mistral"].cost_total_eur == 35.0 | |
| assert result["gpt4o"].cost_total_eur == 75.0 | |
| def test_negative_target_raises(self) -> None: | |
| with pytest.raises(ValueError, match="target_pages"): | |
| project_all_engines({"x": _ec("x", 1.0)}, -1) | |
| def test_engine_without_data_kept(self) -> None: | |
| engines = { | |
| "known": _ec("known", 1.0), | |
| "unknown": _ec("unknown", None), | |
| } | |
| result = project_all_engines(engines, 1000) | |
| assert result["known"].cost_total_eur == 1.0 | |
| assert "unknown" in result | |
| assert result["unknown"].cost_total_eur is None | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 5. cost_gap_table | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestCostGapTable: | |
| def _projections(self) -> dict: | |
| return { | |
| "tess": project_engine(_ec("tess", 1.0), 10000), | |
| "mistral": project_engine(_ec("mistral", 5.0), 10000), | |
| } | |
| def test_gap_vs_baseline(self) -> None: | |
| projs = self._projections() | |
| gaps = cost_gap_table(projs, baseline_engine="tess") | |
| # tess : 10 β¬, mistral : 50 β¬ | |
| assert gaps["tess"]["delta_abs"] == 0.0 | |
| assert gaps["mistral"]["delta_abs"] == 40.0 | |
| assert gaps["mistral"]["delta_rel"] == 4.0 # 40/10 | |
| def test_unknown_baseline_raises(self) -> None: | |
| with pytest.raises(KeyError): | |
| cost_gap_table(self._projections(), "nonexistent") | |
| def test_baseline_zero_relative_none(self) -> None: | |
| projs = { | |
| "tess": project_engine(_ec("tess", 0.0, type_="local"), 10000), | |
| "mistral": project_engine(_ec("mistral", 5.0), 10000), | |
| } | |
| gaps = cost_gap_table(projs, "tess") | |
| # tess Γ 0 β relative non calculable | |
| assert gaps["mistral"]["delta_rel"] is None | |
| # absolute reste calculable | |
| assert gaps["mistral"]["delta_abs"] == 50.0 | |
| def test_engine_without_data_skipped_in_calc(self) -> None: | |
| projs = { | |
| "known": project_engine(_ec("known", 1.0), 1000), | |
| "unknown": project_engine(_ec("unknown", None), 1000), | |
| } | |
| gaps = cost_gap_table(projs, "known") | |
| assert gaps["unknown"]["delta_abs"] is None | |
| assert gaps["unknown"]["delta_rel"] is None | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 6. Cas rΓ©aliste BnF | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestRealisticBnF: | |
| def test_80k_pages_bms_scenario(self) -> None: | |
| # ScΓ©nario : registres paroissiaux, 80 000 pages | |
| engines = { | |
| "tesseract": _ec("tesseract", 0.04, type_="local"), | |
| "pero": _ec("pero", 0.0, type_="local"), | |
| "mistral": _ec("mistral", 3.5, type_="cloud_api"), | |
| "gpt4o": _ec("gpt4o", 7.5, type_="cloud_api"), | |
| } | |
| target = 80000 | |
| result = project_all_engines(engines, target) | |
| # VΓ©rifications quantitatives | |
| assert result["tesseract"].cost_total_eur == pytest.approx(3.2) | |
| assert result["pero"].cost_total_eur == 0.0 | |
| assert result["mistral"].cost_total_eur == 280.0 | |
| assert result["gpt4o"].cost_total_eur == 600.0 | |
| # Gap vs Tesseract : Mistral coΓ»te 276,8 β¬ de plus | |
| gaps = cost_gap_table(result, "tesseract") | |
| assert gaps["mistral"]["delta_abs"] == pytest.approx(276.8) | |