aboutsummaryrefslogtreecommitdiffstats
path: root/tests/integration/test_live.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-15 11:02:34 +0200
committerDanilo M. <danix@danix.xyz>2026-10-15 11:02:34 +0200
commit07f4134d38e53bf6c9235f3ad18dd5b33e89d9ff (patch)
tree65eed6420426560a7e8127baa3ea7a703d1223a0 /tests/integration/test_live.py
parent50cb852130986249cacec411e23392a8610e12f1 (diff)
downloadfirefly-cli-07f4134d38e53bf6c9235f3ad18dd5b33e89d9ff.tar.gz
firefly-cli-07f4134d38e53bf6c9235f3ad18dd5b33e89d9ff.zip
feat(piggy): piggy list/add/remove (v0.6.0)HEADv0.6.0master
New `piggy` group: - `piggy list` lists piggy banks with saved/target amounts and linked accounts. - `piggy add|remove <name|id> <amount> [--account NAME]` changes the saved amount on a linked account via PUT /piggy-banks/{id}. Firefly syncs the whole account list on that PUT, so every linked account is resent (others without current_amount, which keeps their value) or it would be unlinked. Removing below zero is rejected before any write, and the saved amount is read back so a refused add fails loudly instead of returning 200 unchanged. add/remove currently hit an upstream bug on Firefly III <= 6.7.9: PiggyBankFactory::linkToAccountIds passes the pivot current_amount (an int from the DB) to bcsub() uncast, so any PUT with "accounts" 500s (firefly-iii/firefly-iii#13063). Documented in SKILL.md/README; the live integration test skips on that error and runs once upstream is fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/integration/test_live.py')
-rw-r--r--tests/integration/test_live.py43
1 files changed, 43 insertions, 0 deletions
diff --git a/tests/integration/test_live.py b/tests/integration/test_live.py
index a815b88..ec900d7 100644
--- a/tests/integration/test_live.py
+++ b/tests/integration/test_live.py
@@ -54,3 +54,46 @@ class TestLive(unittest.TestCase):
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}")