import pytest from fin_hub.coupling.ops_hub import ServiceCostLine, build_service_cost_report from fin_hub.ingest.hosteurope import parse_hosteurope_csv from pathlib import Path def test_build_service_cost_report_groups_by_service(): rows = parse_hosteurope_csv(Path(__file__).parent / "fixtures" / "hosteurope.csv") lines = [ ServiceCostLine( service_id=row.service_id, environment=row.environment, period_month=row.period_month, amount=row.amount, currency=row.currency, source=row.source, ) for row in rows ] report = build_service_cost_report(lines) assert report["signal"] == "service_cost_attribution" assert len(report["services"]) == 2 assert report["totals_by_period_currency"] == [ {"period_month": "2026-06", "currency": "EUR", "total": pytest.approx(99.8)} ] assert report["attributions"][0]["cost_attribution_key"] is None assert report["unattributed"][0]["total"] == pytest.approx(99.8) def test_build_service_cost_report_groups_by_client_attribution(): lines = [ ServiceCostLine( service_id="cluster", environment="production", period_month="2026-07", amount=42.0, currency="EUR", source="fixture", client_id="acme", application_id="portal", app_instance_id="prod-01", cost_attribution_key="client:acme|app:portal|instance:prod-01", ) ] report = build_service_cost_report(lines) assert report["attributions"] == [ { "cost_attribution_key": "client:acme|app:portal|instance:prod-01", "client_id": "acme", "application_id": "portal", "app_instance_id": "prod-01", "environment": "production", "currency": "EUR", "months": {"2026-07": 42.0}, "total": 42.0, } ] def test_client_attribution_totals_do_not_mix_currencies(): common = { "service_id": "cluster", "environment": "production", "period_month": "2026-07", "source": "fixture", "client_id": "acme", "application_id": "portal", "app_instance_id": "prod-01", } report = build_service_cost_report( [ ServiceCostLine(amount=42.0, currency="EUR", **common), ServiceCostLine(amount=50.0, currency="USD", **common), ] ) assert {(row["currency"], row["total"]) for row in report["attributions"]} == { ("EUR", 42.0), ("USD", 50.0), } def test_service_totals_do_not_mix_currency_or_environment(): common = { "service_id": "cluster", "period_month": "2026-07", "source": "fixture", } report = build_service_cost_report( [ ServiceCostLine(amount=10.0, currency="EUR", environment="production", **common), ServiceCostLine(amount=20.0, currency="USD", environment="production", **common), ServiceCostLine(amount=30.0, currency="EUR", environment="development", **common), ] ) assert { (row["environment"], row["currency"], row["total"]) for row in report["services"] } == { ("production", "EUR", 10.0), ("production", "USD", 20.0), ("development", "EUR", 30.0), } assert report["totals_by_period_currency"] == [ {"period_month": "2026-07", "currency": "EUR", "total": 40.0}, {"period_month": "2026-07", "currency": "USD", "total": 20.0}, ]