import json from datetime import date, datetime, timezone from decimal import Decimal from pathlib import Path import pytest from pydantic import ValidationError from fin_hub.models.engagement_price import EngagementPrice, _validate_engagement_price from fin_hub.schemas.exchange import ( AllocationEvidence, BookedCostEvidence, ForecastEvidence, ) from fin_hub.services.exchange import ( booked_cost_projection, exchange_health, financial_constraint_projection, ingest_planning_evidence, ingest_resource_forecast, ingest_resource_usage, reconcile_resource_period, ) from fin_hub.services.ledger import import_csv def _common_planning() -> dict: return { "schema_version": "0.1", "record_id": "forecast:platform-audit-storage:2026-08", "revision_of": None, "resource_id": "resource:platform_audit_storage", "service_id": "object-storage", "workload_id": "platform-pg", "tenant_id": None, "environment": "production", "cost_attribution_key": "platform:audit-storage", "period_start": "2026-09-01", "period_end": "2027-08-31", "currency": "eur", "source_evidence": ["resource-control:data/forecasts/platform-audit-storage"], "created_at": "2026-08-10T17:10:00Z", } def test_booked_cost_money_relationships_and_decimal_json(): record = BookedCostEvidence( financial_fact_id="fact:1", correction_of=None, adjustment_kind="charge", source_type="provider_invoice", source_document_id="invoice:1", source_line_id="invoice:1:line:1", content_fingerprint="sha256:abc", provider="provider", accounting_period="2026-07", currency="eur", net_amount="10.005", discount_amount="1.00", tax_status="known", tax_amount="1.90", gross_amount="10.90", adjustment_amount="0", effective_amount="10.90", source_evidence_ref="invoice:1", recorded_at=datetime.now(timezone.utc), ) assert record.net_amount == Decimal("10.00") assert record.currency == "EUR" assert '"gross_amount":"10.90"' in record.model_dump_json() def test_booked_cost_rejects_invalid_relationships(): with pytest.raises(ValidationError, match="gross_amount"): BookedCostEvidence( financial_fact_id="fact:1", adjustment_kind="charge", source_type="invoice", source_document_id="doc", source_line_id="line", content_fingerprint="hash", provider="provider", accounting_period="2026-07", currency="EUR", net_amount="10", discount_amount="0", tax_status="known", tax_amount="1.90", gross_amount="12", effective_amount="12", source_evidence_ref="doc", recorded_at=datetime.now(timezone.utc), ) def test_planning_forecast_is_typed_and_idempotent(tmp_path: Path): payload = { **_common_planning(), "record_type": "forecast", "scenario": "base", "forecast_version": "2026-08-base", "costs": { "infrastructure": "2.89", "internal_labor": "60", "external_services": "0", "setup": "0", "other": "0", }, "uncertainty": "provider price excludes unknown tax", "assumptions": ["180 GB stored"], } ledger = tmp_path / "ledger.db" first = ingest_planning_evidence(payload, ledger_path=ledger) second = ingest_planning_evidence(payload, ledger_path=ledger) assert isinstance(first, ForecastEvidence) assert first.costs.total == Decimal("62.89") assert second.record_id == first.record_id def test_resource_control_backup_forecast_enters_planning_store(tmp_path: Path): payload = { "schema_version": "0.1", "record_type": "forecast", "workload": "platform-pg", "cost_attribution_key": "platform:audit-storage", "provider_id": "scaleway-standard-multi-az", "created_at": "2026-08-10T17:10:00Z", "scenario": "base", "forecast_ref": None, "rows": [ { "period": "2026-09", "database_gb": 5, "stored_gb": 180, "wal_gb": 30, "restore_egress_gb": 5, "write_requests": 2500, "read_requests": 1000, "infrastructure_eur": 2.89, "internal_labor_hours": 1, "internal_labor_eur": 60, "total_eur": 62.89, } ], } records = ingest_resource_forecast( payload, resource_id="resource:platform_audit_storage", source_evidence_ref="resource-control:data/forecasts/platform-audit-storage", ledger_path=tmp_path / "ledger.db", ) assert len(records) == 1 assert isinstance(records[0], ForecastEvidence) assert records[0].costs.total == Decimal("62.89") def test_allocation_requires_shares_and_residual_to_reconcile(): payload = { **_common_planning(), "record_type": "allocation", "record_id": "allocation:1", "financial_fact_ids": ["fact:1"], "method": "namespace-cpu-v1", "allocated_amount": "10.00", "shares": [{"target_key": "client:acme", "share": "0.75"}], "residual_share": "0.20", } with pytest.raises(ValidationError, match="must equal 1"): AllocationEvidence.model_validate(payload) def test_booked_cost_projection_uses_current_corrected_fact(tmp_path: Path): source = tmp_path / "cost.csv" ledger = tmp_path / "ledger.db" source.write_text( "product,amount,currency,invoice_date,environment\n" "Server,10.00,EUR,2026-07-01,production\n", encoding="utf-8", ) import_csv(source, "hosteurope", ledger_path=ledger) source.write_text( "product,amount,currency,invoice_date,environment\n" "Server,12.00,EUR,2026-07-01,production\n", encoding="utf-8", ) import_csv(source, "hosteurope", ledger_path=ledger, force=True) records = booked_cost_projection(ledger_path=ledger) assert len(records) == 1 assert records[0].effective_amount == Decimal("12.00") assert records[0].correction_of is not None assert records[0].environment == "production" def test_hosteurope_fact_joins_external_resource_by_stable_fact_id(tmp_path: Path): source = tmp_path / "hosteurope.csv" ledger = tmp_path / "ledger.db" source.write_text( "product,amount,currency,invoice_date,environment\n" "Server,49.00,EUR,2026-07-01,production\n", encoding="utf-8", ) import_csv(source, "hosteurope", ledger_path=ledger) fact = booked_cost_projection(ledger_path=ledger)[0] joined = booked_cost_projection( ledger_path=ledger, fact_resource_ids={ fact.financial_fact_id: "resource:hosteurope:railiance01" }, ) assert joined[0].resource_id == "resource:hosteurope:railiance01" assert joined[0].provider == "hosteurope" with pytest.raises(ValueError, match="unknown current financial facts"): booked_cost_projection( ledger_path=ledger, fact_resource_ids={"fact:unknown": "resource:hosteurope:railiance01"}, ) def test_sqlalchemy_price_uses_shared_invariants(): price = EngagementPrice( client_id="acme", application_id="portal", app_instance_id="prod-01", cost_attribution_key="wrong", effective_from=date(2026, 7, 1), amount=Decimal("100.001"), currency="eur", source="agreement", ) with pytest.raises(ValueError, match="does not match"): _validate_engagement_price(None, None, price) def test_financial_constraints_are_bounded_typed_and_idempotent(): kwargs = { "domain_slug": "financials", "period_start": date(2026, 8, 1), "period_end": date(2026, 8, 31), "currency": "eur", "budget_ceiling": "100.00", "active_commitment": "20.00", "spent": "85.00", "monthly_burn": "30.00", "runway_months": "2.5", "runway_threshold_months": "3", "resource_id": "resource:hosteurope:railiance01", "source_evidence": ("fin-hub:budget:2026-08", "fin-hub:runway:2026-08"), "generated_at": datetime(2026, 8, 11, 13, 0, tzinfo=timezone.utc), } first = financial_constraint_projection(**kwargs) second = financial_constraint_projection(**kwargs) assert first == second assert [signal.signal_kind for signal in first.signals] == [ "budget_ceiling", "active_commitment", "burn_pressure", "runway_pressure", ] assert first.signals[0].classification == "policy_constraint" assert first.signals[2].classification == "informational_warning" assert first.signals[3].metric_value == Decimal("2.5") assert all(signal.currency == "EUR" for signal in first.signals) assert "Not payment authority" in first.disclaimer def test_financial_constraints_do_not_invent_missing_authoritative_values(): export = financial_constraint_projection( domain_slug="financials", period_start=date(2026, 8, 1), period_end=date(2026, 8, 31), currency="EUR", source_evidence=("fin-hub:budget:none",), generated_at=datetime(2026, 8, 11, 13, 0, tzinfo=timezone.utc), ) assert export.signals == () def test_financial_constraint_rejects_invalid_period_or_unbounded_provenance(): with pytest.raises(ValueError, match="period_end"): financial_constraint_projection( domain_slug="financials", period_start=date(2026, 9, 1), period_end=date(2026, 8, 31), currency="EUR", source_evidence=("fin-hub:budget",), ) with pytest.raises(ValueError, match="source_evidence"): financial_constraint_projection( domain_slug="financials", period_start=date(2026, 8, 1), period_end=date(2026, 8, 31), currency="EUR", source_evidence=(), ) def test_exchange_health_exposes_rejection_stale_forecast_and_unattributed_cost( tmp_path: Path, ): ledger = tmp_path / "ledger.db" source = tmp_path / "hosteurope.csv" source.write_text( "product,amount,currency,invoice_date,environment\n" "Shared service,10.00,EUR,2026-07-01,production\n", encoding="utf-8", ) import_csv(source, "hosteurope", ledger_path=ledger) stale = { **_common_planning(), "record_type": "forecast", "record_id": "forecast:stale", "period_start": "2026-01-01", "period_end": "2026-01-31", "scenario": "base", "forecast_version": "v1", "costs": {}, "assumptions": [], } ingest_planning_evidence(stale, ledger_path=ledger) invalid = {**stale, "record_id": "forecast:rejected", "currency": "EURO"} with pytest.raises(ValidationError): ingest_planning_evidence(invalid, ledger_path=ledger) with pytest.raises(ValidationError): ingest_planning_evidence(invalid, ledger_path=ledger) health = exchange_health(ledger_path=ledger, as_of=date(2026, 8, 11)) assert health.booked_fact_count == 1 assert health.current_planning_record_count == 1 assert health.rejected_delivery_count == 2 assert {issue.code for issue in health.issues} == { "rejected_planning_delivery", "stale_forecast", "unattributed_booked_cost", } FIXTURES = Path(__file__).parent / "fixtures" AUDIT_STORAGE = "resource:platform:audit-storage" def test_audit_storage_usage_omits_nulls_and_does_not_invent_zero_spend(tmp_path: Path): ledger = tmp_path / "ledger.db" payload = json.loads( (FIXTURES / "platform-audit-storage-usage-2026-08.json").read_text() ) records = ingest_resource_usage( payload, resource_id=AUDIT_STORAGE, source_evidence_ref="resource-control:data/actuals/2026-08.json", ledger_path=ledger, ) assert len(records) == 1 names = {measure.name: measure for measure in records[0].measures} assert set(names) == { "database_gb", "stored_gb", "backup_success_pct", "restore_rto_minutes", } assert "infrastructure_eur" not in names assert names["stored_gb"].value == Decimal("0.0066") assert any("gap:infrastructure_eur" in item for item in records[0].source_evidence) forecast = { "schema_version": "0.1", "record_type": "forecast", "workload": "platform-pg", "cost_attribution_key": "platform:audit-storage", "provider_id": "scaleway-standard-multi-az", "created_at": "2026-08-10T17:10:00Z", "scenario": "base", "forecast_ref": None, "rows": [ { "period": "2026-09", "database_gb": 5, "stored_gb": 180, "wal_gb": 30, "restore_egress_gb": 5, "write_requests": 2500, "read_requests": 1000, "infrastructure_eur": 2.89, "internal_labor_hours": 1, "internal_labor_eur": 60, "total_eur": 62.89, } ], } ingest_resource_forecast( forecast, resource_id=AUDIT_STORAGE, source_evidence_ref="resource-control:data/forecasts/platform-audit-storage", ledger_path=ledger, ) august = reconcile_resource_period( resource_id=AUDIT_STORAGE, period="2026-08", ledger_path=ledger ) assert august.usage_record_ids assert august.missing_booked_fact is True assert august.booked_effective_amount is None assert august.invented_zero is False health = exchange_health(ledger_path=ledger) assert "usage_without_booked_fact" in {issue.code for issue in health.issues}