aboutsummaryrefslogtreecommitdiffstats
path: root/tests/integration/test_live.py
blob: ec900d78ada78851ea0a2d8554f9b1f8175603a4 (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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>  GPL-2.0-only
import os, unittest
from firefly_cli.client import Client
from firefly_cli.resolver import Resolver
from firefly_cli.output import unwrap

URL = os.environ.get("FIREFLY_TEST_URL")
TOKEN = os.environ.get("FIREFLY_TEST_TOKEN")

@unittest.skipUnless(URL and TOKEN,
    "set FIREFLY_TEST_URL and FIREFLY_TEST_TOKEN to run live tests")
class TestLive(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.client = Client(URL, TOKEN)
        cls.resolver = Resolver(cls.client)

    def test_about_shape(self):
        resp = self.client.request("GET", "/api/v1/about")
        self.assertIn("version", resp["data"])

    def test_accounts_envelope_and_balance_field(self):
        resp = self.client.request("GET", "/api/v1/accounts",
                                   params={"type": "asset", "limit": 5})
        self.assertIn("data", resp)
        accs = unwrap(resp)
        if accs:
            # Confirm the balance field name assumed by the design.
            self.assertIn("current_balance", accs[0],
                "balance field name differs; update account balance cmd + unit test")

    def test_create_then_delete_transaction(self):
        # Needs an asset account and an expense account on the TEST account.
        accs = unwrap(self.client.request("GET", "/api/v1/accounts",
                                          params={"type": "asset", "limit": 2}))
        if len(accs) < 1:
            self.skipTest("test account has no asset account")
        exp = unwrap(self.client.request("GET", "/api/v1/accounts",
                                         params={"type": "expense", "limit": 1}))
        if not exp:
            self.skipTest("test account has no expense account")
        body = {"transactions": [{
            "type": "withdrawal",
            "date": "2026-06-30",
            "amount": "0.01",
            "description": "firefly-cli integration test",
            "source_id": accs[0]["id"],
            "destination_id": exp[0]["id"],
        }]}
        created = self.client.request("POST", "/api/v1/transactions", body=body)
        tx_id = created["data"]["id"]
        try:
            got = self.client.request("GET", f"/api/v1/transactions/{tx_id}")
            self.assertEqual(got["data"]["id"], tx_id)
        finally:
            self.client.request("DELETE", f"/api/v1/transactions/{tx_id}")

    def test_piggy_add_remove_roundtrip(self):
        # Create a piggy bank on the TEST account, drive it through the CLI
        # handlers, then delete it.
        import io, contextlib, json
        from types import SimpleNamespace
        from firefly_cli.commands import piggy
        from firefly_cli.errors import FireflyError
        accs = unwrap(self.client.request("GET", "/api/v1/accounts",
                                          params={"type": "asset", "limit": 50}))
        acc = next((a for a in accs if float(a.get("current_balance") or 0) >= 10), None)
        if acc is None:
            self.skipTest("test account has no asset account with >= 10 balance")
        created = self.client.request("POST", "/api/v1/piggy-banks", body={
            "name": "firefly-cli integration piggy", "target_amount": "100",
            "transaction_currency_code": acc.get("currency_code") or "EUR",
            "start_date": "2026-01-01",
            "accounts": [{"account_id": acc["id"], "current_amount": "0"}]})
        pb_id = created["data"]["id"]
        ctx = SimpleNamespace(client=self.client, resolver=self.resolver, human=False)

        def run(fn, amount):
            buf = io.StringIO()
            with contextlib.redirect_stdout(buf):
                fn(SimpleNamespace(ref=pb_id, amount=amount, account=None), ctx)
            return json.loads(buf.getvalue())["accounts"][0]["current_amount"]
        try:
            from firefly_cli.errors import ApiError
            try:
                first = run(piggy.cmd_add, "2.09")
            except ApiError as e:
                if "bcsub()" in str(e):
                    self.skipTest("firefly-iii/firefly-iii#13063: piggy PUT with accounts 500s")
                raise
            self.assertEqual(float(first), 2.09)
            self.assertEqual(float(run(piggy.cmd_add, "0.91")), 3.00)
            self.assertEqual(float(run(piggy.cmd_remove, "1")), 2.00)
            with self.assertRaises(FireflyError):   # more than the account holds
                run(piggy.cmd_add, "99999999")
            linked = unwrap(self.client.request("GET", f"/api/v1/piggy-banks/{pb_id}"))["accounts"]
            self.assertEqual(len(linked), 1)        # PUT did not unlink the account
        finally:
            self.client.request("DELETE", f"/api/v1/piggy-banks/{pb_id}")