diff options
| -rw-r--r-- | firefly_cli/resolver.py | 13 | ||||
| -rw-r--r-- | tests/unit/test_resolver.py | 33 |
2 files changed, 46 insertions, 0 deletions
diff --git a/firefly_cli/resolver.py b/firefly_cli/resolver.py index 7296aad..53144e1 100644 --- a/firefly_cli/resolver.py +++ b/firefly_cli/resolver.py @@ -42,3 +42,16 @@ class Resolver: def category(self, name): return self._match("category", self._list("/api/v1/categories"), name) + + def budget(self, name_or_id): + # Budgets must pre-exist (Firefly does not auto-create them for a + # transaction), so we resolve to an id. A numeric-looking ref goes + # straight to the show endpoint (lets same-name-safe callers pin an id); + # otherwise match by name against the list. + if str(name_or_id).isdigit(): + return self.budget_by_id(name_or_id) + return self._match("budget", self._list("/api/v1/budgets"), name_or_id) + + def budget_by_id(self, budget_id): + item = self.client.request("GET", f"/api/v1/budgets/{budget_id}")["data"] + return {"id": item["id"], **item.get("attributes", {})} diff --git a/tests/unit/test_resolver.py b/tests/unit/test_resolver.py index 82ac30b..8c93a84 100644 --- a/tests/unit/test_resolver.py +++ b/tests/unit/test_resolver.py @@ -51,3 +51,36 @@ class TestResolver(unittest.TestCase): acc = r.account_by_id("129") self.assertEqual(acc, {"id": "129", "name": "Nexi", "type": "revenue"}) c.request.assert_called_once_with("GET", "/api/v1/accounts/129") + + +def _budget_list_resp(budgets): + return {"data": [{"id": b["id"], "attributes": {"name": b["name"]}} + for b in budgets]} + + +class TestResolverBudget(unittest.TestCase): + def test_budget_by_name(self): + client = MagicMock() + client.request.return_value = _budget_list_resp( + [{"id": "3", "name": "Groceries"}, {"id": "4", "name": "Rent"}]) + r = Resolver(client) + self.assertEqual(r.budget("Groceries")["id"], "3") + + def test_budget_missing_raises_with_candidates(self): + client = MagicMock() + client.request.return_value = _budget_list_resp([{"id": "3", "name": "Rent"}]) + r = Resolver(client) + with self.assertRaises(ResolutionError) as cm: + r.budget("Nope") + self.assertIn("Rent", str(cm.exception)) + + def test_budget_numeric_ref_goes_by_id(self): + client = MagicMock() + client.request.return_value = { + "data": {"id": "7", "attributes": {"name": "Fun"}}} + r = Resolver(client) + got = r.budget("7") + self.assertEqual(got["id"], "7") + self.assertEqual(got["name"], "Fun") + # Must hit the show endpoint, not list. + client.request.assert_called_with("GET", "/api/v1/budgets/7") |
