- Deterministic scoring.py (quant 60 / qual 40 / flags -15, profitability heaviest) - Per-company JSON ledger with forecast-target chaining deck N-1 -> N - Single-shot sandbox agent with guided-JSON fallback ladder (no tool loop) - Portfolio dashboard with sparklines, KPI hit rates, BDEF category bars - 48 unit tests green; endpoints smoke-tested; npm check+build green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
117 lines
5.6 KiB
Python
117 lines
5.6 KiB
Python
"""Tests for ledger.py: company lifecycle, deck records, forward targets."""
|
|
import glob
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
import ledger as ledger_mod
|
|
|
|
|
|
def _record(deck_id, period, composite=70.0):
|
|
return {"schema_version": 1, "deck_id": deck_id, "period": period,
|
|
"composite": composite, "graded_at": "2026-07-01T00:00:00Z"}
|
|
|
|
|
|
def _ft(canonical, target, target_period, direction="gte"):
|
|
return {"name": canonical, "canonical_name": canonical, "target": target,
|
|
"unit": "", "target_period": target_period, "direction": direction,
|
|
"profitability": False, "source": "slide 9"}
|
|
|
|
|
|
class TestLedger(unittest.TestCase):
|
|
def setUp(self):
|
|
self._tmp = tempfile.TemporaryDirectory()
|
|
self.ledger = ledger_mod.Ledger(os.path.join(self._tmp.name, "ledger"))
|
|
|
|
def tearDown(self):
|
|
self._tmp.cleanup()
|
|
|
|
def test_ensure_company_auto_created(self):
|
|
c = self.ledger.ensure_company("acme")
|
|
self.assertTrue(c["auto_created"])
|
|
self.assertEqual(c["name"], "acme")
|
|
c2 = self.ledger.ensure_company("acme", name="Acme Robotics")
|
|
self.assertEqual(c2["name"], "acme") # existing entry wins
|
|
named = self.ledger.ensure_company("beta", name="Beta Corp")
|
|
self.assertFalse(named["auto_created"])
|
|
self.assertEqual(named["name"], "Beta Corp")
|
|
self.assertEqual(self.ledger.all_slugs(), ["acme", "beta"])
|
|
self.assertEqual(len(self.ledger.all_companies()), 2)
|
|
|
|
def test_merge_config_companies(self):
|
|
self.ledger.ensure_company("acme")
|
|
self.ledger.merge_config_companies([{
|
|
"slug": "acme", "name": "Acme Robotics",
|
|
"kpiAliases": "arr=annual recurring revenue;run_rate_arr\nchurn_rate=logo_churn",
|
|
"pinnedTargets": [{"kpi": "cash_balance", "target": 12.0, "unit": "$M",
|
|
"direction": "gte", "profitability": True}],
|
|
}])
|
|
c = self.ledger.get_company("acme")
|
|
self.assertFalse(c["auto_created"])
|
|
self.assertEqual(c["name"], "Acme Robotics")
|
|
self.assertEqual(c["kpi_aliases"],
|
|
{"arr": ["annual recurring revenue", "run_rate_arr"],
|
|
"churn_rate": ["logo_churn"]})
|
|
self.assertEqual(c["pinned_targets"][0]["kpi"], "cash_balance")
|
|
# slug derived from name when absent
|
|
self.ledger.merge_config_companies([{"name": "Beta Corp", "kpiAliases": "",
|
|
"pinnedTargets": []}])
|
|
self.assertIsNotNone(self.ledger.get_company("beta-corp"))
|
|
|
|
def test_record_supersede_prior_targets_roundtrip(self):
|
|
path = self.ledger.record_deck("acme", _record("2026-Q1", "2026-Q1", 82.7),
|
|
[_ft("arr", 12.0, "2026-Q2"),
|
|
_ft("arr", 15.0, "2026-Q3")])
|
|
self.assertTrue(os.path.isfile(path))
|
|
self.assertEqual(
|
|
[t["target"] for t in self.ledger.prior_targets("acme", "2026-Q2")], [12.0])
|
|
self.assertEqual(self.ledger.prior_targets("acme", "2026-Q4"), [])
|
|
self.assertEqual(self.ledger.prior_targets("nobody", "2026-Q2"), [])
|
|
|
|
# Re-grade the same deck: old record superseded (renamed), one live record.
|
|
self.ledger.record_deck("acme", _record("2026-Q1", "2026-Q1", 80.0),
|
|
[_ft("arr", 12.5, "2026-Q2")])
|
|
ddir = os.path.dirname(path)
|
|
self.assertEqual(len(glob.glob(os.path.join(ddir, "*.superseded-*.json"))), 1)
|
|
live = self.ledger.deck_records("acme")
|
|
self.assertEqual(len(live), 1)
|
|
self.assertEqual(live[0]["composite"], 80.0)
|
|
self.assertEqual(
|
|
[t["target"] for t in self.ledger.prior_targets("acme", "2026-Q2")], [12.5])
|
|
# history keeps one entry per period
|
|
c = self.ledger.get_company("acme")
|
|
self.assertEqual([h["period"] for h in c["history"]], ["2026-Q1"])
|
|
self.assertEqual(c["history"][0]["composite"], 80.0)
|
|
|
|
def test_newer_deck_replaces_targets_older_does_not(self):
|
|
self.ledger.record_deck("acme", _record("2026-Q1", "2026-Q1"),
|
|
[_ft("arr", 15.0, "2026-Q3")])
|
|
self.ledger.record_deck("acme", _record("2026-Q2", "2026-Q2"),
|
|
[_ft("arr", 16.0, "2026-Q3"),
|
|
_ft("churn_rate", 3.0, "2026-Q3", "lte")])
|
|
targets = self.ledger.prior_targets("acme", "2026-Q3")
|
|
self.assertEqual(sorted(t["target"] for t in targets), [3.0, 16.0])
|
|
c = self.ledger.get_company("acme")
|
|
self.assertEqual(c["extracted_targets"]["2026-Q3"]["from_deck"], "2026-Q2")
|
|
# Re-recording the OLDER deck must not clobber the newer deck's targets.
|
|
self.ledger.record_deck("acme", _record("2026-Q1", "2026-Q1"),
|
|
[_ft("arr", 15.0, "2026-Q3")])
|
|
targets = self.ledger.prior_targets("acme", "2026-Q3")
|
|
self.assertEqual(sorted(t["target"] for t in targets), [3.0, 16.0])
|
|
# History is sorted oldest first.
|
|
c = self.ledger.get_company("acme")
|
|
self.assertEqual([h["period"] for h in c["history"]], ["2026-Q1", "2026-Q2"])
|
|
|
|
def test_deck_record_lookup(self):
|
|
self.ledger.record_deck("acme", _record("2026-Q1", "2026-Q1"), [])
|
|
self.assertEqual(self.ledger.deck_record("acme", "2026-Q1")["period"], "2026-Q1")
|
|
self.assertIsNone(self.ledger.deck_record("acme", "2026-Q9"))
|
|
self.assertEqual(self.ledger.deck_records("nobody"), [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|