|
1 | 1 | import random |
2 | 2 | import re |
| 3 | +from datetime import UTC, datetime |
3 | 4 | from decimal import Decimal |
4 | 5 | from unittest.mock import patch |
5 | 6 |
|
|
10 | 11 | _verify_account_ids, |
11 | 12 | create_account, |
12 | 13 | read_account, |
| 14 | + read_account_balance, |
13 | 15 | read_accounts, |
14 | 16 | update_account_balances, |
15 | 17 | update_accounts, |
16 | 18 | ) |
17 | 19 | from kayman.schemas.account import Account |
18 | | -from kayman.tests.factories import AccountFactory |
| 20 | +from kayman.tests.factories import AccountFactory, TransactionFactory |
19 | 21 |
|
20 | 22 |
|
21 | 23 | def test_create_account(session: Session): |
@@ -44,6 +46,90 @@ def test_read_account_not_found(session: Session): |
44 | 46 | assert read_account(session, 1) is None |
45 | 47 |
|
46 | 48 |
|
| 49 | +def test_read_account_balance_empty_account(session: Session): |
| 50 | + account = AccountFactory() |
| 51 | + |
| 52 | + assert read_account_balance(session, account.id) == Decimal(0) |
| 53 | + |
| 54 | + |
| 55 | +def test_read_account_balance_nonexistent_account(session: Session): |
| 56 | + assert read_account_balance(session, 9999) == Decimal(0) |
| 57 | + |
| 58 | + |
| 59 | +def test_read_account_balance_sums_all_txns_when_at_is_none(session: Session): |
| 60 | + account = AccountFactory() |
| 61 | + TransactionFactory( |
| 62 | + account=account, |
| 63 | + amount=Decimal("10.50"), |
| 64 | + created_at=datetime(2026, 1, 5, tzinfo=UTC), |
| 65 | + ) |
| 66 | + TransactionFactory( |
| 67 | + account=account, |
| 68 | + amount=Decimal("-3.25"), |
| 69 | + created_at=datetime(2026, 2, 1, tzinfo=UTC), |
| 70 | + ) |
| 71 | + TransactionFactory( |
| 72 | + account=account, |
| 73 | + amount=Decimal("7"), |
| 74 | + created_at=datetime(2026, 3, 1, tzinfo=UTC), |
| 75 | + ) |
| 76 | + |
| 77 | + assert read_account_balance(session, account.id) == Decimal("14.25") |
| 78 | + |
| 79 | + |
| 80 | +def test_read_account_balance_at_includes_only_txns_before_cutoff(session: Session): |
| 81 | + account = AccountFactory() |
| 82 | + TransactionFactory( |
| 83 | + account=account, |
| 84 | + amount=Decimal("100"), |
| 85 | + created_at=datetime(2025, 12, 1, tzinfo=UTC), |
| 86 | + ) |
| 87 | + TransactionFactory( |
| 88 | + account=account, |
| 89 | + amount=Decimal("50"), |
| 90 | + created_at=datetime(2025, 12, 31, tzinfo=UTC), |
| 91 | + ) |
| 92 | + TransactionFactory( |
| 93 | + account=account, |
| 94 | + amount=Decimal("999"), |
| 95 | + created_at=datetime(2026, 2, 1, tzinfo=UTC), |
| 96 | + ) |
| 97 | + |
| 98 | + assert read_account_balance( |
| 99 | + session, account.id, at=datetime(2026, 1, 1, tzinfo=UTC) |
| 100 | + ) == Decimal("150") |
| 101 | + |
| 102 | + |
| 103 | +def test_read_account_balance_at_is_exclusive(session: Session): |
| 104 | + account = AccountFactory() |
| 105 | + cutoff = datetime(2026, 1, 1, tzinfo=UTC) |
| 106 | + TransactionFactory( |
| 107 | + account=account, |
| 108 | + amount=Decimal("5"), |
| 109 | + created_at=datetime(2025, 12, 31, tzinfo=UTC), |
| 110 | + ) |
| 111 | + TransactionFactory(account=account, amount=Decimal("10"), created_at=cutoff) |
| 112 | + |
| 113 | + assert read_account_balance(session, account.id, at=cutoff) == Decimal("5") |
| 114 | + |
| 115 | + |
| 116 | +def test_read_account_balance_isolates_account(session: Session): |
| 117 | + account_a = AccountFactory() |
| 118 | + account_b = AccountFactory() |
| 119 | + TransactionFactory( |
| 120 | + account=account_a, |
| 121 | + amount=Decimal("10"), |
| 122 | + created_at=datetime(2026, 1, 5, tzinfo=UTC), |
| 123 | + ) |
| 124 | + TransactionFactory( |
| 125 | + account=account_b, |
| 126 | + amount=Decimal("999"), |
| 127 | + created_at=datetime(2026, 1, 5, tzinfo=UTC), |
| 128 | + ) |
| 129 | + |
| 130 | + assert read_account_balance(session, account_a.id) == Decimal("10") |
| 131 | + |
| 132 | + |
47 | 133 | def test_read_accounts(session: Session): |
48 | 134 | accounts = AccountFactory.create_batch(10) |
49 | 135 | db_accounts = read_accounts(session) |
|
0 commit comments