From 6310a2d77190c5cb98250d4b537bd3cb97cf7e77 Mon Sep 17 00:00:00 2001 From: atuldharne <33314690+atuldharne@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:16:55 +0000 Subject: [PATCH 1/5] Part1: add unit tests for add_funds() and fix missing amount validation - Added 5 unit tests covering add_funds() happy path, error on missing account, and rejection of zero/negative amounts - Fixed bug in Bank.add_funds(): amount <= 0 now raises ValueError --- bank_api/bank.py | 2 ++ tests/test_bank.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/bank_api/bank.py b/bank_api/bank.py index afe6353..2e003af 100644 --- a/bank_api/bank.py +++ b/bank_api/bank.py @@ -45,6 +45,8 @@ def get_account(self, name: str) -> Account: def add_funds(self, name: str, amount: int) -> None: """Add funds to the named account""" + if amount <= 0: + raise ValueError("Amount must be a positive integer") account = self.get_account(name) now = datetime.now() self.transactions.append(Transaction(account, now, amount)) diff --git a/tests/test_bank.py b/tests/test_bank.py index 0f106f0..170c7db 100644 --- a/tests/test_bank.py +++ b/tests/test_bank.py @@ -32,5 +32,33 @@ def test_get_account_raises_error_if_no_account_matches(bank: Bank): with pytest.raises(ValueError): bank.get_account('Name 2') -# TODO: Add unit tests for bank.add_funds() +# --- add_funds() tests --- + +def test_add_funds_creates_transaction(bank: Bank): + bank.create_account('Alice') + bank.add_funds('Alice', 1000) + + assert len(bank.transactions) == 1 + +def test_add_funds_transaction_has_correct_amount(bank: Bank): + bank.create_account('Alice') + bank.add_funds('Alice', 500) + + assert bank.transactions[0].amount == 500 + +def test_add_funds_raises_error_if_account_not_found(bank: Bank): + with pytest.raises(ValueError): + bank.add_funds('Nobody', 100) + +def test_add_funds_raises_error_if_amount_is_negative(bank: Bank): + bank.create_account('Alice') + + with pytest.raises(ValueError): + bank.add_funds('Alice', -50) + +def test_add_funds_raises_error_if_amount_is_zero(bank: Bank): + bank.create_account('Alice') + + with pytest.raises(ValueError): + bank.add_funds('Alice', 0) From e4a37037abfc1e171e07675771667f049f2ab01b Mon Sep 17 00:00:00 2001 From: atuldharne <33314690+atuldharne@users.noreply.github.com> Date: Sat, 21 Feb 2026 22:40:46 +0000 Subject: [PATCH 2/5] Part2: add integration tests for account POST and GET endpoints - test_account_creation: POST creates account, GET retrieves it, both return 200 and correct JSON body - test_get_account_returns_404_if_not_found: verifies 404 on missing account --- tests/test_app.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/test_app.py b/tests/test_app.py index e05b062..fe7f187 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -16,8 +16,18 @@ def client(): def test_account_creation(client: FlaskClient): - # Use the client to make requests to the Flask app. - # response = client.get('/example/route') - # Or use client.post to make a POST request - # https://flask.palletsprojects.com/en/1.1.x/testing/ - pass + post_response = client.post('/accounts/Alice') + + assert post_response.status_code == 200 + assert post_response.get_json() == {"name": "Alice"} + + get_response = client.get('/accounts/Alice') + + assert get_response.status_code == 200 + assert get_response.get_json() == {"name": "Alice"} + + +def test_get_account_returns_404_if_not_found(client: FlaskClient): + response = client.get('/accounts/Nobody') + + assert response.status_code == 404 From c24f409a694e19e29d408073dd0cb827effefe59 Mon Sep 17 00:00:00 2001 From: atuldharne <33314690+atuldharne@users.noreply.github.com> Date: Sat, 21 Feb 2026 22:57:53 +0000 Subject: [PATCH 3/5] StretchA: add BankReport class with naive unit tests - New BankReport class in bank_api/bank_report.py with get_balance(name) that sums transactions for the named account - 3 unit tests: zero balance, summing multiple transactions, account isolation - Tests use real Bank instance (to be mocked in StretchC) --- bank_api/bank_report.py | 10 ++++++++++ tests/test_bank_report.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 bank_api/bank_report.py create mode 100644 tests/test_bank_report.py diff --git a/bank_api/bank_report.py b/bank_api/bank_report.py new file mode 100644 index 0000000..380ed96 --- /dev/null +++ b/bank_api/bank_report.py @@ -0,0 +1,10 @@ +from bank_api.bank import Bank + + +class BankReport: + def __init__(self, bank: Bank): + self.bank = bank + + def get_balance(self, name: str) -> int: + account = self.bank.get_account(name) + return sum(t.amount for t in self.bank.transactions if t.account == account) diff --git a/tests/test_bank_report.py b/tests/test_bank_report.py new file mode 100644 index 0000000..b82a2bc --- /dev/null +++ b/tests/test_bank_report.py @@ -0,0 +1,37 @@ +"""Unit tests for bank_report.py""" + +import pytest + +from bank_api.bank import Bank +from bank_api.bank_report import BankReport + + +@pytest.fixture +def bank() -> Bank: + return Bank() + + +def test_balance_is_zero_for_new_account(bank: Bank): + bank_report = BankReport(bank) + bank.create_account('Alice') + + assert bank_report.get_balance('Alice') == 0 + + +def test_balance_sums_multiple_transactions(bank: Bank): + bank_report = BankReport(bank) + bank.create_account('Alice') + bank.add_funds('Alice', 500) + bank.add_funds('Alice', 300) + + assert bank_report.get_balance('Alice') == 800 + + +def test_balance_only_counts_own_transactions(bank: Bank): + bank_report = BankReport(bank) + bank.create_account('Alice') + bank.create_account('Bob') + bank.add_funds('Alice', 1000) + bank.add_funds('Bob', 200) + + assert bank_report.get_balance('Alice') == 1000 From 1a4bcdd1594dde1544e8efcc85dfd21d4d202aa4 Mon Sep 17 00:00:00 2001 From: atuldharne <33314690+atuldharne@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:00:16 +0000 Subject: [PATCH 4/5] StretchB: expose balance on GET /accounts/ endpoint - Import BankReport into app.py and wire it to the bank instance - GET /accounts/ now returns {"name": ..., "balance": ...} - Added integration test asserting balance reflects added funds - Updated existing GET assertion to include balance: 0 --- bank_api/app.py | 5 ++++- tests/test_app.py | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bank_api/app.py b/bank_api/app.py index 948b973..db02122 100644 --- a/bank_api/app.py +++ b/bank_api/app.py @@ -2,6 +2,7 @@ from flask_restx import Api, Resource, reqparse, fields, abort from bank_api.bank import Bank +from bank_api.bank_report import BankReport def create_app(): @@ -11,6 +12,7 @@ def create_app(): api = Api(app, title='My Banking API', description='A simple banking API for learning Test-Driven-Development') bank = Bank() + bank_report = BankReport(bank) # Custom API documentation add_money = api.model("Add", { @@ -31,7 +33,8 @@ def post(self, name): def get(self, name): """Get an Account""" try: - return bank.get_account(name).to_dict() + account = bank.get_account(name) + return {**account.to_dict(), 'balance': bank_report.get_balance(name)} except Exception: abort(404, 'Account not found') diff --git a/tests/test_app.py b/tests/test_app.py index fe7f187..aa0e107 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -24,10 +24,20 @@ def test_account_creation(client: FlaskClient): get_response = client.get('/accounts/Alice') assert get_response.status_code == 200 - assert get_response.get_json() == {"name": "Alice"} + assert get_response.get_json() == {"name": "Alice", "balance": 0} def test_get_account_returns_404_if_not_found(client: FlaskClient): response = client.get('/accounts/Nobody') assert response.status_code == 404 + + +def test_get_account_includes_balance(client: FlaskClient): + client.post('/accounts/Alice') + client.post('/money', json={'name': 'Alice', 'amount': 1000}) + + response = client.get('/accounts/Alice') + + assert response.status_code == 200 + assert response.get_json()['balance'] == 1000 From 56a9d0a3aef0f67823b064bdc519f74c94f3bcd5 Mon Sep 17 00:00:00 2001 From: atuldharne <33314690+atuldharne@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:06:07 +0000 Subject: [PATCH 5/5] StretchC: rewrite BankReport tests with monkeypatch for true isolation - Replaced naive tests (using real Bank) with monkeypatched equivalents - get_account mocked via lambda; transactions replaced with controlled list - Isolation verified: breaking Bank.get_account fails test_bank.py but leaves test_bank_report.py fully green --- tests/test_bank_report.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/test_bank_report.py b/tests/test_bank_report.py index b82a2bc..bd396e7 100644 --- a/tests/test_bank_report.py +++ b/tests/test_bank_report.py @@ -1,8 +1,10 @@ """Unit tests for bank_report.py""" +from datetime import datetime + import pytest -from bank_api.bank import Bank +from bank_api.bank import Account, Bank, Transaction from bank_api.bank_report import BankReport @@ -11,27 +13,35 @@ def bank() -> Bank: return Bank() -def test_balance_is_zero_for_new_account(bank: Bank): +def test_balance_is_zero_with_no_transactions(bank: Bank, monkeypatch): bank_report = BankReport(bank) - bank.create_account('Alice') + account = Account('Alice') + monkeypatch.setattr(bank, 'get_account', lambda name: account) + monkeypatch.setattr(bank, 'transactions', []) assert bank_report.get_balance('Alice') == 0 -def test_balance_sums_multiple_transactions(bank: Bank): +def test_balance_sums_transactions(bank: Bank, monkeypatch): bank_report = BankReport(bank) - bank.create_account('Alice') - bank.add_funds('Alice', 500) - bank.add_funds('Alice', 300) + account = Account('Alice') + monkeypatch.setattr(bank, 'get_account', lambda name: account) + monkeypatch.setattr(bank, 'transactions', [ + Transaction(account, datetime.now(), 500), + Transaction(account, datetime.now(), 300), + ]) assert bank_report.get_balance('Alice') == 800 -def test_balance_only_counts_own_transactions(bank: Bank): +def test_balance_ignores_other_accounts_transactions(bank: Bank, monkeypatch): bank_report = BankReport(bank) - bank.create_account('Alice') - bank.create_account('Bob') - bank.add_funds('Alice', 1000) - bank.add_funds('Bob', 200) + alice = Account('Alice') + bob = Account('Bob') + monkeypatch.setattr(bank, 'get_account', lambda name: alice) + monkeypatch.setattr(bank, 'transactions', [ + Transaction(alice, datetime.now(), 1000), + Transaction(bob, datetime.now(), 200), + ]) assert bank_report.get_balance('Alice') == 1000