2026-07-08 00:50:24 +02:00
|
|
|
"""Smoke tests for fin-hub model registration."""
|
|
|
|
|
|
2026-08-11 10:25:03 +02:00
|
|
|
from datetime import date
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
2026-08-10 20:43:05 +02:00
|
|
|
from fin_hub.models import (
|
|
|
|
|
Budget,
|
|
|
|
|
BurnRate,
|
|
|
|
|
Commitment,
|
|
|
|
|
EngagementPrice,
|
|
|
|
|
RunwayProjection,
|
|
|
|
|
ServiceCost,
|
|
|
|
|
TokenSpend,
|
|
|
|
|
)
|
2026-08-11 10:25:03 +02:00
|
|
|
from fin_hub.models.engagement_price import _validate_engagement_price
|
|
|
|
|
from fin_hub.models.service_cost import _normalize_service_cost_attribution
|
2026-07-08 00:50:24 +02:00
|
|
|
from hub_core.models.base import Base
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fin_models_register_on_metadata():
|
|
|
|
|
tables = {table.name for table in Base.metadata.sorted_tables}
|
|
|
|
|
assert "fin_budgets" in tables
|
|
|
|
|
assert "fin_commitments" in tables
|
|
|
|
|
assert "fin_burn_rates" in tables
|
|
|
|
|
assert "fin_runway_projections" in tables
|
|
|
|
|
assert "fin_token_spends" in tables
|
2026-07-08 00:59:39 +02:00
|
|
|
assert "fin_service_costs" in tables
|
2026-08-10 20:43:05 +02:00
|
|
|
assert "fin_engagement_prices" in tables
|
2026-07-08 00:50:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_model_classes_importable():
|
|
|
|
|
assert Budget.__tablename__ == "fin_budgets"
|
|
|
|
|
assert Commitment.__tablename__ == "fin_commitments"
|
|
|
|
|
assert BurnRate.__tablename__ == "fin_burn_rates"
|
|
|
|
|
assert RunwayProjection.__tablename__ == "fin_runway_projections"
|
2026-07-08 00:59:39 +02:00
|
|
|
assert TokenSpend.__tablename__ == "fin_token_spends"
|
2026-08-10 20:32:09 +02:00
|
|
|
assert ServiceCost.__tablename__ == "fin_service_costs"
|
2026-08-10 20:43:05 +02:00
|
|
|
assert EngagementPrice.__tablename__ == "fin_engagement_prices"
|
2026-08-10 20:32:09 +02:00
|
|
|
|
|
|
|
|
|
2026-08-15 19:01:45 +02:00
|
|
|
def test_token_spend_token_counts_are_nullable():
|
|
|
|
|
columns = TokenSpend.__table__.columns
|
|
|
|
|
assert columns["tokens_in"].nullable is True
|
|
|
|
|
assert columns["tokens_out"].nullable is True
|
|
|
|
|
assert columns["tokens_in"].default is None
|
|
|
|
|
assert columns["tokens_out"].default is None
|
|
|
|
|
|
|
|
|
|
|
2026-08-10 20:32:09 +02:00
|
|
|
def test_service_cost_has_external_attribution_seam():
|
|
|
|
|
columns = ServiceCost.__table__.columns
|
|
|
|
|
assert columns["client_id"].nullable is True
|
|
|
|
|
assert columns["application_id"].nullable is True
|
|
|
|
|
assert columns["app_instance_id"].nullable is True
|
|
|
|
|
assert columns["cost_attribution_key"].nullable is True
|
2026-08-11 10:25:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_service_cost_orm_uses_shared_money_and_period_invariants():
|
|
|
|
|
cost = ServiceCost(
|
|
|
|
|
service_id="cluster",
|
|
|
|
|
environment="production",
|
|
|
|
|
period_month="2026-07",
|
|
|
|
|
amount=Decimal("10.005"),
|
|
|
|
|
currency="eur",
|
|
|
|
|
source=" invoice ",
|
|
|
|
|
)
|
|
|
|
|
_normalize_service_cost_attribution(None, None, cost)
|
|
|
|
|
assert cost.amount == Decimal("10.00")
|
|
|
|
|
assert cost.currency == "EUR"
|
|
|
|
|
assert cost.source == "invoice"
|
|
|
|
|
|
|
|
|
|
cost.period_month = "2026-13"
|
|
|
|
|
with pytest.raises(ValueError, match="YYYY-MM"):
|
|
|
|
|
_normalize_service_cost_attribution(None, None, cost)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_engagement_price_orm_normalizes_same_basis_as_sqlite():
|
|
|
|
|
price = EngagementPrice(
|
|
|
|
|
client_id="acme",
|
|
|
|
|
application_id="portal",
|
|
|
|
|
app_instance_id="prod-01",
|
|
|
|
|
cost_attribution_key=None,
|
|
|
|
|
period_month="placeholder",
|
|
|
|
|
effective_from=date(2026, 7, 15),
|
|
|
|
|
amount=Decimal("100.005"),
|
|
|
|
|
currency="eur",
|
|
|
|
|
source=" agreement ",
|
|
|
|
|
)
|
|
|
|
|
_validate_engagement_price(None, None, price)
|
|
|
|
|
assert price.period_month == "2026-07"
|
|
|
|
|
assert price.amount == Decimal("100.00")
|
|
|
|
|
assert price.currency == "EUR"
|
|
|
|
|
assert price.source == "agreement"
|