Implement BDEF v1.1 grading: scoring core, per-deck pipeline, ledger, dashboard, StartOS layer
- 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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1dde915540
commit
b1d7aed9f4
@@ -0,0 +1,118 @@
|
||||
"""Tests for decks.py: slugify, period parsing/sorting, inbox discovery."""
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import decks
|
||||
|
||||
|
||||
class TestSlugify(unittest.TestCase):
|
||||
def test_basic(self):
|
||||
self.assertEqual(decks.slugify("Acme Robotics"), "acme-robotics")
|
||||
self.assertEqual(decks.slugify(" Acme, Inc. (US) "), "acme-inc-us")
|
||||
self.assertEqual(decks.slugify("ALLCAPS"), "allcaps")
|
||||
self.assertEqual(decks.slugify(""), "company")
|
||||
self.assertEqual(decks.slugify("---"), "company")
|
||||
|
||||
|
||||
class TestParsePeriod(unittest.TestCase):
|
||||
def test_quarters(self):
|
||||
self.assertEqual(decks.parse_period_from_name("acme_2026-Q2_board.pdf"), "2026-Q2")
|
||||
self.assertEqual(decks.parse_period_from_name("2026Q4 deck.pptx"), "2026-Q4")
|
||||
self.assertEqual(decks.parse_period_from_name("Q3 2025 update.pptx"), "2025-Q3")
|
||||
self.assertEqual(decks.parse_period_from_name("q1_2024_board.docx"), "2024-Q1")
|
||||
|
||||
def test_halves(self):
|
||||
self.assertEqual(decks.parse_period_from_name("board-2026-H1.docx"), "2026-H1")
|
||||
self.assertEqual(decks.parse_period_from_name("2025H2-review.pdf"), "2025-H2")
|
||||
|
||||
def test_months(self):
|
||||
self.assertEqual(decks.parse_period_from_name("acme 2026-05 board.pdf"), "2026-05")
|
||||
self.assertEqual(decks.parse_period_from_name("2026_12_flash.txt"), "2026-12")
|
||||
self.assertIsNone(decks.parse_period_from_name("2026-13 notes.pdf"))
|
||||
self.assertIsNone(decks.parse_period_from_name("2026-00 notes.pdf"))
|
||||
|
||||
def test_fiscal_year(self):
|
||||
self.assertEqual(decks.parse_period_from_name("FY2025 review.pdf"), "FY2025")
|
||||
self.assertEqual(decks.parse_period_from_name("fy-2024 plan.txt"), "FY2024")
|
||||
self.assertEqual(decks.parse_period_from_name("FY 2026 budget.docx"), "FY2026")
|
||||
|
||||
def test_no_period(self):
|
||||
self.assertIsNone(decks.parse_period_from_name("notes.txt"))
|
||||
self.assertIsNone(decks.parse_period_from_name("budget_2027.xlsx"))
|
||||
self.assertIsNone(decks.parse_period_from_name("Q5 2026.pdf"))
|
||||
|
||||
def test_quarter_wins_over_month(self):
|
||||
# "2026-Q2" must not be misread; Q pattern is checked before YYYY-MM.
|
||||
self.assertEqual(decks.parse_period_from_name("2026-Q2 and 2026-05.pdf"), "2026-Q2")
|
||||
|
||||
def test_not_inside_digit_runs(self):
|
||||
self.assertIsNone(decks.parse_period_from_name("doc-20261-05.pdf"))
|
||||
|
||||
|
||||
class TestPeriodSortKey(unittest.TestCase):
|
||||
def test_ordering_mixed_granularities(self):
|
||||
ordered = ["FY2025", "2025-Q4", "2026-H1", "2026-Q1", "2026-01",
|
||||
"2026-Q2", "2026-05", "2026-H2", "2026-Q4"]
|
||||
self.assertEqual(sorted(ordered, key=decks.period_sort_key), ordered)
|
||||
|
||||
def test_start_months(self):
|
||||
self.assertEqual(decks.period_sort_key("2026-Q2")[:2], (2026, 4))
|
||||
self.assertEqual(decks.period_sort_key("2026-H2")[:2], (2026, 7))
|
||||
self.assertEqual(decks.period_sort_key("2026-11")[:2], (2026, 11))
|
||||
self.assertEqual(decks.period_sort_key("FY2026")[:2], (2026, 1))
|
||||
|
||||
def test_unknown_sorts_last(self):
|
||||
keys = [decks.period_sort_key(p) for p in ("2026-Q4", None, "garbage", "FY2026")]
|
||||
self.assertEqual(max(keys), decks.period_sort_key(None))
|
||||
self.assertEqual(decks.period_sort_key(None), decks.period_sort_key("garbage"))
|
||||
self.assertGreater(decks.period_sort_key(None), decks.period_sort_key("2099-Q4"))
|
||||
|
||||
|
||||
class TestDiscover(unittest.TestCase):
|
||||
def _touch(self, *parts):
|
||||
path = os.path.join(*parts)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w") as f:
|
||||
f.write("x")
|
||||
|
||||
def test_discover(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
inbox = os.path.join(tmp, "inbox")
|
||||
self._touch(inbox, "Acme Robotics", "acme-2026-Q1.pdf")
|
||||
self._touch(inbox, "Acme Robotics", "acme-2026-Q1-appendix.txt")
|
||||
self._touch(inbox, "Acme Robotics", "acme-2026-Q2.pptx")
|
||||
self._touch(inbox, "Acme Robotics", "chart-2026-Q2.png")
|
||||
self._touch(inbox, "Acme Robotics", "notes.txt")
|
||||
self._touch(inbox, "Acme Robotics", ".DS_Store")
|
||||
self._touch(inbox, "beta-corp", "deck 2026-H1.docx")
|
||||
self._touch(inbox, "stray.pdf")
|
||||
|
||||
out = decks.discover(inbox)
|
||||
self.assertEqual(out["skipped"], ["stray.pdf"])
|
||||
units = out["units"]
|
||||
keys = [(u["company_slug"], u["period"], u["period_source"]) for u in units]
|
||||
self.assertEqual(keys, [
|
||||
("acme-robotics", "2026-Q1", "filename"),
|
||||
("acme-robotics", "2026-Q2", "filename"),
|
||||
("acme-robotics", None, "unknown"),
|
||||
("beta-corp", "2026-H1", "filename"),
|
||||
])
|
||||
q1 = units[0]
|
||||
self.assertEqual([os.path.basename(f) for f in q1["files"]],
|
||||
["acme-2026-Q1-appendix.txt", "acme-2026-Q1.pdf"])
|
||||
self.assertTrue(all(os.path.isabs(f) for f in q1["files"]))
|
||||
q2 = units[1]
|
||||
self.assertEqual([os.path.basename(f) for f in q2["files"]], ["acme-2026-Q2.pptx"])
|
||||
self.assertEqual(q2["ignored"], ["chart-2026-Q2.png"])
|
||||
self.assertEqual([os.path.basename(f) for f in units[2]["files"]], ["notes.txt"])
|
||||
|
||||
def test_missing_inbox(self):
|
||||
self.assertEqual(decks.discover("/nonexistent/inbox"), {"units": [], "skipped": []})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user