diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-01 11:37:53 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-01 11:37:53 +0200 |
| commit | 4588f4bf54ae6eefd7a75904d81ce2256afb805d (patch) | |
| tree | dade60deaee537480b4f6101c4fd6571dc1a69a1 /tests/unit/test_commands_transaction.py | |
| parent | 238a4506cfeda388d097265b133004b63f130de6 (diff) | |
| download | firefly-cli-4588f4bf54ae6eefd7a75904d81ce2256afb805d.tar.gz firefly-cli-4588f4bf54ae6eefd7a75904d81ce2256afb805d.zip | |
feat: tx add echoes transfer direction to stderr (v0.3.5)v0.3.5
Transfers are easy to reverse silently (swapped --from/--to), showing up
later as a 2x balance discrepancy (ISSUES.md #5). On a transfer, tx add now
prints "transfer: <from> → <to>, <amount>" to stderr before writing, in both
the real-write and --dry-run paths. stdout JSON and exit codes are unchanged,
so the contract holds; PATCH bump. Withdrawals/deposits are unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'tests/unit/test_commands_transaction.py')
| -rw-r--r-- | tests/unit/test_commands_transaction.py | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/tests/unit/test_commands_transaction.py b/tests/unit/test_commands_transaction.py index 13d6469..0fa02ac 100644 --- a/tests/unit/test_commands_transaction.py +++ b/tests/unit/test_commands_transaction.py @@ -134,6 +134,54 @@ class TestTxAdd(unittest.TestCase): self.assertEqual(client.request.call_args[0][:2], ("POST", "/api/v1/transactions")) + def test_transfer_prints_direction_hint_to_stderr(self): + import io + from contextlib import redirect_stderr + ctx, client, resolver = make_ctx() + resolver.account.side_effect = lambda n: { + "BBVA": {"id": "3", "name": "BBVA", "type": "asset"}, + "Medio": {"id": "4", "name": "Medio", "type": "asset"}, + }[n] + client.request.return_value = {"data": {"id": "1", "attributes": {}}} + args = MagicMock(amount="100", source="BBVA", dest="Medio", desc=None, + date=None, category=None, tags=None, type=None, + dry_run=False, skip_dupes=False) + buf = io.StringIO() + with redirect_stderr(buf): + tx.cmd_add(args, ctx) + self.assertIn("transfer: BBVA → Medio, 100", buf.getvalue()) + + def test_transfer_hint_shown_in_dry_run(self): + import io + from contextlib import redirect_stderr + ctx, client, resolver = make_ctx() + resolver.account.side_effect = lambda n: {"id": "1", "type": "asset", "name": n} + args = MagicMock(amount="5", source="A", dest="B", desc=None, date=None, + category=None, tags=None, type="transfer", + dry_run=True, skip_dupes=False) + buf = io.StringIO() + with redirect_stderr(buf): + tx.cmd_add(args, ctx) + self.assertIn("transfer: A → B, 5", buf.getvalue()) + client.request.assert_not_called() + + def test_withdrawal_no_direction_hint(self): + import io + from contextlib import redirect_stderr + ctx, client, resolver = make_ctx() + resolver.account.side_effect = lambda n: { + "Checking": {"id": "1", "name": "Checking", "type": "asset"}, + "Groceries": {"id": "2", "name": "Groceries", "type": "expense"}, + }[n] + client.request.return_value = {"data": {"id": "1", "attributes": {}}} + args = MagicMock(amount="5", source="Checking", dest="Groceries", desc=None, + date=None, category=None, tags=None, type=None, + dry_run=False, skip_dupes=False) + buf = io.StringIO() + with redirect_stderr(buf): + tx.cmd_add(args, ctx) + self.assertNotIn("transfer:", buf.getvalue()) + def test_dry_run_beats_skip_dupes_no_search(self): ctx, client, resolver = make_ctx() resolver.account.side_effect = lambda n: {"id": "1", "type": "asset", "name": n} |
