summaryrefslogtreecommitdiffstats
path: root/tests/unit/test_commands_piggy.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/test_commands_piggy.py')
-rw-r--r--tests/unit/test_commands_piggy.py80
1 files changed, 80 insertions, 0 deletions
diff --git a/tests/unit/test_commands_piggy.py b/tests/unit/test_commands_piggy.py
new file mode 100644
index 0000000..24565ac
--- /dev/null
+++ b/tests/unit/test_commands_piggy.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+import unittest
+from unittest.mock import MagicMock
+from types import SimpleNamespace
+import firefly_cli.commands.piggy as piggy
+from firefly_cli.errors import FireflyError
+
+
+def _pb(accounts):
+ return {"id": "3", "name": "Round-ups", "accounts": accounts}
+
+
+def _ctx(pb, put_accounts=None):
+ """Resolver returns `pb`; the PUT echoes `put_accounts` (what Firefly saved)."""
+ resolver = MagicMock()
+ resolver.piggy.return_value = pb
+ client = MagicMock()
+ client.request.return_value = {"data": {"id": "3", "attributes": {
+ "name": "Round-ups", "accounts": put_accounts or []}}}
+ return SimpleNamespace(client=client, resolver=resolver, human=False)
+
+
+ONE = [{"account_id": "6", "name": "BBVA", "current_amount": "1.50"}]
+TWO = ONE + [{"account_id": "8", "name": "Fineco", "current_amount": "0.00"}]
+
+
+class TestPiggyAddRemove(unittest.TestCase):
+ def test_add_single_account_puts_new_total(self):
+ ctx = _ctx(_pb(ONE), [{"account_id": "6", "current_amount": "3.59"}])
+ rc = piggy.cmd_add(SimpleNamespace(ref="Round-ups", amount="2.09", account=None), ctx)
+ self.assertEqual(rc, 0)
+ method, path = ctx.client.request.call_args[0][:2]
+ self.assertEqual((method, path), ("PUT", "/api/v1/piggy-banks/3"))
+ body = ctx.client.request.call_args[1]["body"]
+ self.assertEqual(body, {"accounts": [{"account_id": "6", "current_amount": "3.59"}]})
+
+ def test_remove_subtracts(self):
+ ctx = _ctx(_pb(ONE), [{"account_id": "6", "current_amount": "1.00"}])
+ piggy.cmd_remove(SimpleNamespace(ref="Round-ups", amount="0.50", account=None), ctx)
+ body = ctx.client.request.call_args[1]["body"]
+ self.assertEqual(body["accounts"][0]["current_amount"], "1.00")
+
+ def test_remove_below_zero_is_error_without_write(self):
+ ctx = _ctx(_pb(ONE))
+ with self.assertRaises(FireflyError):
+ piggy.cmd_remove(SimpleNamespace(ref="Round-ups", amount="5", account=None), ctx)
+ ctx.client.request.assert_not_called()
+
+ def test_multi_account_needs_account_flag(self):
+ ctx = _ctx(_pb(TWO))
+ with self.assertRaises(FireflyError):
+ piggy.cmd_add(SimpleNamespace(ref="Round-ups", amount="1", account=None), ctx)
+
+ def test_multi_account_resends_others_without_amount(self):
+ # Firefly syncs the account list: omitting an account would unlink it.
+ ctx = _ctx(_pb(TWO), [{"account_id": "8", "current_amount": "1.00"}])
+ piggy.cmd_add(SimpleNamespace(ref="Round-ups", amount="1", account="fineco"), ctx)
+ body = ctx.client.request.call_args[1]["body"]
+ self.assertEqual(body["accounts"], [{"account_id": "6"},
+ {"account_id": "8", "current_amount": "1.00"}])
+
+ def test_unknown_account_is_error(self):
+ ctx = _ctx(_pb(ONE))
+ with self.assertRaises(FireflyError):
+ piggy.cmd_add(SimpleNamespace(ref="Round-ups", amount="1", account="Nope"), ctx)
+
+ def test_silent_refusal_is_detected(self):
+ # Firefly skips an add it can't afford but still returns 200.
+ ctx = _ctx(_pb(ONE), [{"account_id": "6", "current_amount": "1.50"}])
+ with self.assertRaises(FireflyError):
+ piggy.cmd_add(SimpleNamespace(ref="Round-ups", amount="2", account=None), ctx)
+
+ def test_bad_amount(self):
+ for bad in ("0", "-1", "abc"):
+ with self.assertRaises(FireflyError):
+ piggy.cmd_add(SimpleNamespace(ref="x", amount=bad, account=None), _ctx(_pb(ONE)))
+
+
+if __name__ == "__main__":
+ unittest.main()