diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-05 19:17:04 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-05 19:17:04 +0200 |
| commit | 50cb852130986249cacec411e23392a8610e12f1 (patch) | |
| tree | d49c544811f3f088b966046c859412f1b1015162 /tests | |
| parent | 2f768bb3373105e96d0cbc6fb4ba37c777bbca25 (diff) | |
| download | firefly-cli-50cb852130986249cacec411e23392a8610e12f1.tar.gz firefly-cli-50cb852130986249cacec411e23392a8610e12f1.zip | |
Add two deferred budget follow-ups:
- tx edit --budget: re-assign a budget on an existing transaction,
mirroring the existing tx add --budget flag (name/id resolved, hard
error on miss). Resolves the ISSUES.md backfill wish.
- budget update: rename a budget and/or edit its auto-budget fields via
PUT /budgets/{id}; only fields passed are sent, empty is a hard error.
New command adds to the CLI surface without breaking callers, so this is
a MINOR bump (0.4.1 -> 0.5.0). Docs (README, SKILL.md), completion, and
TODO synced. The tx add --budget-id item stays open (still YAGNI).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/unit/test_commands_budget.py | 42 | ||||
| -rw-r--r-- | tests/unit/test_commands_transaction.py | 22 |
2 files changed, 60 insertions, 4 deletions
diff --git a/tests/unit/test_commands_budget.py b/tests/unit/test_commands_budget.py index b365d27..96452c1 100644 --- a/tests/unit/test_commands_budget.py +++ b/tests/unit/test_commands_budget.py @@ -119,6 +119,47 @@ class TestBudgetLifecycle(unittest.TestCase): self.assertFalse(client.request.call_args[1]["body"]["active"]) +class TestBudgetUpdate(unittest.TestCase): + def _resolver(self): + r = MagicMock() + r.budget.return_value = {"id": "5", "name": "Rent"} + return r + + def _args(self, **kw): + base = dict(ref="Rent", name=None, currency=None, auto_budget_amount=None, + auto_budget_period=None, auto_budget_type=None) + base.update(kw) + return SimpleNamespace(**base) + + def test_rename_puts_name(self): + client = MagicMock() + client.request.return_value = {"data": {"id": "5", "attributes": {}}} + budget.cmd_update(self._args(name="Housing"), + _ctx(client=client, resolver=self._resolver())) + method, path = client.request.call_args[0][:2] + body = client.request.call_args[1]["body"] + self.assertEqual((method, path), ("PUT", "/api/v1/budgets/5")) + self.assertEqual(body, {"name": "Housing"}) + + def test_auto_budget_fields(self): + client = MagicMock() + client.request.return_value = {"data": {"id": "5", "attributes": {}}} + budget.cmd_update( + self._args(auto_budget_amount=500, auto_budget_period="monthly", + auto_budget_type="reset", currency="EUR"), + _ctx(client=client, resolver=self._resolver())) + body = client.request.call_args[1]["body"] + self.assertEqual(body["auto_budget_amount"], "500") + self.assertEqual(body["auto_budget_period"], "monthly") + self.assertEqual(body["auto_budget_type"], "reset") + self.assertEqual(body["currency_code"], "EUR") + self.assertNotIn("name", body) + + def test_no_fields_errors(self): + with self.assertRaises(FireflyError): + budget.cmd_update(self._args(), _ctx(resolver=self._resolver())) + + class TestBudgetLimit(unittest.TestCase): def _resolver(self): r = MagicMock() @@ -168,6 +209,7 @@ class TestBudgetRegistration(unittest.TestCase): expected = { "budget list": budget.cmd_list, "budget create": budget.cmd_create, + "budget update": budget.cmd_update, "budget delete": budget.cmd_delete, "budget enable": budget.cmd_enable, "budget disable": budget.cmd_disable, diff --git a/tests/unit/test_commands_transaction.py b/tests/unit/test_commands_transaction.py index 7366b5c..ec4e54c 100644 --- a/tests/unit/test_commands_transaction.py +++ b/tests/unit/test_commands_transaction.py @@ -274,7 +274,8 @@ class TestTxEdit(unittest.TestCase): ctx, client, resolver = make_ctx() client.request.return_value = {"data": {"id": "9", "attributes": {}}} args = MagicMock(id="9", amount="12.00", date=None, desc="fixed", - source=None, dest=None, category=None, tags=None, type=None) + source=None, dest=None, category=None, tags=None, + budget=None, type=None) rc = tx.cmd_edit(args, ctx) self.assertEqual(rc, 0) method, path = client.request.call_args[0][:2] @@ -291,7 +292,8 @@ class TestTxEdit(unittest.TestCase): }[n] client.request.return_value = {"data": {"id": "9", "attributes": {}}} args = MagicMock(id="9", amount=None, date=None, desc=None, - source="BBVA", dest="Medio", category=None, tags=None, type=None) + source="BBVA", dest="Medio", category=None, tags=None, + budget=None, type=None) tx.cmd_edit(args, ctx) split = client.request.call_args[1]["body"]["transactions"][0] self.assertEqual(split, {"source_id": "3", "destination_id": "4"}) @@ -300,7 +302,8 @@ class TestTxEdit(unittest.TestCase): ctx, client, resolver = make_ctx() client.request.return_value = {"data": {"id": "9", "attributes": {}}} args = MagicMock(id="9", amount=None, date=None, desc=None, source=None, - dest=None, category="Cat", tags="a, b", type="transfer") + dest=None, category="Cat", tags="a, b", budget=None, + type="transfer") tx.cmd_edit(args, ctx) split = client.request.call_args[1]["body"]["transactions"][0] self.assertEqual(split, @@ -311,11 +314,22 @@ class TestTxEdit(unittest.TestCase): from firefly_cli.errors import FireflyError ctx, client, _ = make_ctx() args = MagicMock(id="9", amount=None, date=None, desc=None, source=None, - dest=None, category=None, tags=None, type=None) + dest=None, category=None, tags=None, budget=None, type=None) with self.assertRaises(FireflyError): tx.cmd_edit(args, ctx) client.request.assert_not_called() + def test_edit_budget_ref_sets_budget_id(self): + ctx, client, resolver = make_ctx() + resolver.budget.return_value = {"id": "12", "name": "Groceries"} + client.request.return_value = {"data": {"id": "9", "attributes": {}}} + args = MagicMock(id="9", amount=None, date=None, desc=None, source=None, + dest=None, category=None, tags=None, budget="Groceries", + type=None) + tx.cmd_edit(args, ctx) + split = client.request.call_args[1]["body"]["transactions"][0] + self.assertEqual(split, {"budget_id": "12"}) + class TestTxDelete(unittest.TestCase): def test_delete_requires_yes(self): |
