summaryrefslogtreecommitdiffstats
path: root/tests/unit/test_commands_piggy.py
blob: 24565ac1abac79e948c0fafb572a7b0197553eef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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()