fin-hub/tests/test_ledger.py

108 lines
3.8 KiB
Python

import sqlite3
from pathlib import Path
import pytest
from fin_hub.services.evaluate import evaluate_runway
from fin_hub.services.evidence import build_runway_report, seed_fixture_ledger, write_runway_evidence
from fin_hub.services.ledger import import_csv, monthly_burn_series, monthly_summary, set_opening_balance
FIXTURES = Path(__file__).parent / "fixtures"
def test_ledger_import_and_monthly_summary(tmp_path: Path):
ledger = tmp_path / "ledger.db"
set_opening_balance(ledger, 12000.0)
cloud = import_csv(FIXTURES / "cloud-costs.csv", "cloud", ledger_path=ledger)
host = import_csv(FIXTURES / "hosteurope.csv", "hosteurope", ledger_path=ledger)
assert cloud.rows_imported == 3
assert host.rows_imported == 2
rollups = monthly_summary(ledger_path=ledger)
eur_totals = {rollup.period_month: rollup.total for rollup in rollups if rollup.currency == "EUR"}
assert eur_totals["2026-06"] == pytest.approx(85.5 + 99.8)
burns = monthly_burn_series(ledger_path=ledger, currency="EUR")
assert burns == [pytest.approx(185.3)]
def test_ledger_import_skips_unchanged_file(tmp_path: Path):
ledger = tmp_path / "ledger.db"
first = import_csv(FIXTURES / "cloud-costs.csv", "cloud", ledger_path=ledger)
second = import_csv(FIXTURES / "cloud-costs.csv", "cloud", ledger_path=ledger)
assert first.rows_imported == 3
assert second.skipped is True
assert second.rows_imported == 0
def test_ledger_import_preserves_client_attribution(tmp_path: Path):
source = tmp_path / "attributed.csv"
source.write_text(
"product,amount,currency,invoice_date,client_id,application_id,app_instance_id\n"
"Managed cluster,42.00,EUR,2026-07-01,acme,portal,prod-01\n",
encoding="utf-8",
)
ledger = tmp_path / "ledger.db"
import_csv(source, "hosteurope", ledger_path=ledger)
with sqlite3.connect(ledger) as conn:
row = conn.execute(
"SELECT client_id, application_id, app_instance_id, cost_attribution_key "
"FROM ledger_entries"
).fetchone()
assert row == (
"acme",
"portal",
"prod-01",
"client:acme|app:portal|instance:prod-01",
)
def test_existing_ledger_schema_is_migrated_additively(tmp_path: Path):
ledger = tmp_path / "legacy.db"
with sqlite3.connect(ledger) as conn:
conn.execute(
"CREATE TABLE ledger_entries ("
"id INTEGER PRIMARY KEY, source_type TEXT NOT NULL, category TEXT NOT NULL, "
"label TEXT NOT NULL, amount REAL NOT NULL, currency TEXT NOT NULL, "
"period_month TEXT NOT NULL, incurred_on TEXT, source_path TEXT NOT NULL, "
"imported_at TEXT NOT NULL)"
)
monthly_summary(ledger_path=ledger)
with sqlite3.connect(ledger) as conn:
columns = {row[1] for row in conn.execute("PRAGMA table_info(ledger_entries)")}
assert {
"client_id",
"application_id",
"app_instance_id",
"cost_attribution_key",
}.issubset(columns)
def test_evaluate_runway_from_ledger(tmp_path: Path):
ledger = tmp_path / "ledger.db"
seed_fixture_ledger(ledger_path=ledger, opening_balance=12000.0)
report = evaluate_runway(ledger_path=ledger, alert_threshold_months=3.0)
assert report["runway"]["current_balance"] == 12000.0
assert report["runway"]["monthly_burn"] > 0
def test_build_runway_report_and_write_evidence(tmp_path: Path):
ledger = tmp_path / "ledger.db"
seed_fixture_ledger(ledger_path=ledger, opening_balance=12000.0)
text = build_runway_report(ledger_path=ledger)
assert "Runway Dogfood Evidence" in text
assert "2026-06" in text
output = write_runway_evidence(
ledger_path=ledger,
output_dir=tmp_path / "evidence",
opening_balance=12000.0,
)
assert output.exists()
assert "runway-dogfood-" in output.name