diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f65f010..18e9f5c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,3 +1,39 @@ name: lab-testing # add your pipline description below + +on: [push, pull_request] + +jobs: + unit-tests: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.11.6' + - name: Run image + uses: abatilo/actions-poetry@v2 + with: + poetry-version: '1.7.1' + - name: Install dependencies + run: poetry install; poetry add pytest; poetry add pytest-mock; + - name: Run pytest + run: poetry run pytest tests/unit/test_*.py + e2e-tests: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.11.6' + - name: Run image + uses: abatilo/actions-poetry@v2 + with: + poetry-version: '1.7.1' + - name: Install dependencies + run: poetry install; poetry add pytest; poetry add pytest-mock; + - name: Run pytest + run: poetry run pytest tests/e2e/test_*.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7e674ae..c1379eb 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,5 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ .DS_Store -*.db \ No newline at end of file +*.db +/.idea/ diff --git a/README.md b/README.md index fe5b44c..1e19bad 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-24ddc0f5d75046c5622901739e7c5dd533143b0c8e959d652212380cedb1ea36.svg)](https://classroom.github.com/a/9TQ_ubhE) # Bank Management System Project [LAB-TESTING] ## Overview diff --git a/tests/e2e/test_bank.py b/tests/e2e/test_bank.py index a843738..48e5618 100644 --- a/tests/e2e/test_bank.py +++ b/tests/e2e/test_bank.py @@ -3,4 +3,78 @@ # - For understanding purposes, you can interact with main.py # - Create a real life usage scenario for this project and follow the order for testing components # - Make sure that the test tests almost all of the functionalities of the project. +import os +import pytest +from app.bank import Bank + +@pytest.fixture +def bank(): + return Bank(db_path='db') + +def test_customer_operations(bank): + customer_id = bank.add_customer("Maks", "Universitetskaaya 1") + assert customer_id is not None + + customers = bank.get_all_customers() + assert len(customers) >= 1 + + bank.update_customer_details(customer_id, "Maks New", "Universitetskaaya 1/3") + updated_customer = bank.get_all_customers()[0] + print(updated_customer) + assert updated_customer[1] == "Maks New" + + bank.delete_customer(customer_id) + customers_after_delete = bank.get_all_customers() + assert len(customers_after_delete) == 0 + +def test_account_operations(bank): + customer_id = bank.add_customer("Nikita", "Universitetskaya 1/1") + account_id = bank.open_account(customer_id, "deposit", 1000) + assert account_id is not None + + account = bank.get_account(account_id) + assert account[1] == customer_id + + bank.deposit_to_account(account_id, 500) + account = bank.get_account(account_id) + assert account[3] == 1500 + + bank.withdraw_from_account(account_id, 200) + account = bank.get_account(account_id) + assert account[3] == 1300 + + bank.close_account(account_id) + closed_account = bank.get_account(account_id) + assert closed_account is None + +def test_transfer_between_accounts(bank): + customer_id = bank.add_customer("Nikita", "Universitetskaya 1/1") + account1_id = bank.open_account(customer_id, "deposit", 1000) + account2_id = bank.open_account(customer_id, "sending", 2000) + + bank.transfer_between_accounts(account1_id, account2_id, 500) + account1 = bank.get_account(account1_id) + account2 = bank.get_account(account2_id) + assert account1[3] == 500 + assert account2[3] == 2500 + +def test_get_account_transactions(bank): + customer_id = bank.add_customer("Nikita", "Universitetskaya 1/1") + account_id = bank.open_account(customer_id, "deposit", 1000) + + transactions = bank.get_account_transactions(account_id) + assert len(transactions) == 0 + + bank.deposit_to_account(account_id, 500) + bank.withdraw_from_account(account_id, 200) + + transactions = bank.get_account_transactions(account_id) + assert len(transactions) == 2 + +@pytest.fixture(scope="session", autouse=True) +def delete_test_database(): + db_file = 'db' + yield + if os.path.exists(db_file): + os.remove(db_file) \ No newline at end of file diff --git a/tests/unit/test_account.py b/tests/unit/test_account.py index ae070d6..fd4b312 100644 --- a/tests/unit/test_account.py +++ b/tests/unit/test_account.py @@ -2,3 +2,30 @@ # - See, app/account.py # - Test account creation and deletion # - Use mocks + +from app.account import Account +from app.database import Database + +import pytest +from unittest.mock import patch + +@pytest.fixture +def db(): + return Database("") + +@patch('app.database.Database') +def test_account_creation(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_account.return_value = 26 + account = Account(mock_db_instance, customer_id=1, account_type="checking", balance=1000.0) + assert account.account_id == 26 + mock_db_instance.add_account.assert_called_once_with(1, "checking", 1000.0) + +@patch('app.database.Database') +def test_delete_account(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_account.return_value = 26 + mock_db_instance.get_account.return_value = (26, 1, "checking", 1000.0) + account = Account(mock_db_instance, customer_id=1, account_type="checking", balance=1000.0) + account.delete_account(account.account_id) + mock_db_instance.delete_account.assert_called_once_with(account.account_id) \ No newline at end of file diff --git a/tests/unit/test_customer.py b/tests/unit/test_customer.py index 81efcf4..7962a5a 100644 --- a/tests/unit/test_customer.py +++ b/tests/unit/test_customer.py @@ -3,3 +3,46 @@ # - Test customer creation, loading, updating and deletion # - Use mocks +from app.customer import Customer +from app.database import Database +import pytest +from unittest.mock import patch + +@pytest.fixture +def db(): + return Database('') + +@patch('app.database.Database') +def test_customer_initialization(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_customer.return_value = 26 + customer = Customer(mock_db_instance, name='Maks', address='Universitetskaya 1') + assert customer.customer_id is not None + mock_db_instance.add_customer.assert_called_once_with('Maks', 'Universitetskaya 1') + +@patch('app.database.Database') +def test_customer_loading(mock_db_class): + mock_db_instance = mock_db_class.return_value + customer_id = 26 + mock_db_instance.get_customer.return_value = (customer_id, 'Maks', 'Universitetskaya 1') + customer = Customer(mock_db_instance, customer_id=customer_id) + assert customer.name == 'Maks' + assert customer.address == 'Universitetskaya 1' + +@patch('app.database.Database') +def test_update_customer_details(mock_db_class): + mock_db_instance = mock_db_class.return_value + customer_id = 26 + mock_db_instance.add_customer.return_value = customer_id + customer = Customer(mock_db_instance, customer_id=customer_id, name='Maks', address='Universitetskaya 1') + customer.update_details('Maks New', 'Universitetskaya 1/3') + mock_db_instance.update_customer.assert_called_once_with(customer_id, 'Maks New', 'Universitetskaya 1/3') + +@patch('app.database.Database') +def test_delete_customer(mock_db_class): + mock_db_instance = mock_db_class.return_value + customer_id = 26 + mock_db_instance.add_customer.return_value = customer_id + customer = Customer(mock_db_instance, customer_id=customer_id) + customer.delete_customer() + mock_db_instance.delete_customer.assert_called_once_with(customer_id) \ No newline at end of file diff --git a/tests/unit/test_database.py b/tests/unit/test_database.py index 0503956..1754bb9 100644 --- a/tests/unit/test_database.py +++ b/tests/unit/test_database.py @@ -3,3 +3,71 @@ # - Test most of the methods # - Use mocks in proper parts +from app.database import Database +from unittest.mock import patch +import pytest + +@pytest.fixture +def db(): + return Database('') + +@patch('app.database.Database') +def test_add_customer(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_customer.return_value = 1 + assert mock_db_instance.add_customer('Maks', 'Universitetskaya 1') == 1 + mock_db_instance.add_customer.assert_called_once_with('Maks', 'Universitetskaya 1') + +@patch('app.database.Database') +def test_get_customer(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.get_customer.return_value = (1, 'Maks', 'Universitetskaya 1') + customer = mock_db_instance.get_customer(1) + assert customer[1] == 'Maks' + assert customer[2] == 'Universitetskaya 1' + +@patch('app.database.Database') +def test_update_customer(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_customer.return_value = 1 + mock_db_instance.get_customer.return_value = (1, 'Maks New', 'Universitetskaya 1/3') + customer_id = mock_db_instance.add_customer('Maks', 'Universitetskaya 1') + mock_db_instance.update_customer(customer_id, 'Maks New', 'Universitetskaya 1/3') + updated_customer = mock_db_instance.get_customer(customer_id) + assert updated_customer[1] == 'Maks New' + assert updated_customer[2] == 'Universitetskaya 1/3' + +@patch('app.database.Database') +def test_delete_customer(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_customer.return_value = 1 + mock_db_instance.get_customer.return_value = None + customer_id = mock_db_instance.add_customer('Maks', 'Universitetskaya 1') + mock_db_instance.delete_customer(customer_id) + deleted_customer = mock_db_instance.get_customer(customer_id) + assert deleted_customer is None + +@patch('app.database.Database') +def test_add_account(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_account.return_value = 1 + customer_id = mock_db_class.add_customer('Maks', 'Universitetskaya 1') + account_id = mock_db_class.add_account(customer_id, 'deposit', 1000.00) + assert account_id is not None + +@patch('app.database.Database') +def test_get_account(mock_db_class): + mock_db_instance = mock_db_class.return_value + mock_db_instance.add_customer.return_value = 1 + mock_db_instance.add_account.return_value = 1 + mock_db_instance.get_account.return_value = (1, 1, 'deposit', 1000.00) + customer_id = mock_db_instance.add_customer('Maks', 'Universitetskaya 1') + account_id = mock_db_instance.add_account(customer_id, 'deposit', 1000.00) + retrieved_account = mock_db_instance.get_account(account_id) + assert retrieved_account[2] == 'deposit' + assert retrieved_account[3] == 1000.00 + +@patch('app.database.Database.add_transaction') +def test_add_transaction(mock_add_transaction, db): + db.add_transaction(1, 2, 100, 'deposit') + mock_add_transaction.assert_called_once_with(1, 2, 100, 'deposit') diff --git a/tests/unit/test_transaction.py b/tests/unit/test_transaction.py index 5fd1eb5..a54457c 100644 --- a/tests/unit/test_transaction.py +++ b/tests/unit/test_transaction.py @@ -3,3 +3,58 @@ # - Test transaction with different types - deposit, withdraw and transfer # - Use mocks accordingly +from app.transaction import Transaction +from app.database import Database + +import pytest +from unittest.mock import patch + +@pytest.fixture +def mock_database(): + return Database("") + +@patch('app.database.Database') +def test_deposit_positive_amount(mock_db_class): + mock_db_instance = mock_db_class.return_value + transaction = Transaction(mock_db_instance) + account_id = 1 + amount = 500 + initial_balance = 1000 + mock_db_instance.get_account.return_value = (account_id, None, None, initial_balance) + transaction.deposit(account_id, amount) + mock_db_instance.get_account.assert_called_once_with(account_id) + mock_db_instance.update_account_balance.assert_called_once_with(account_id, initial_balance + amount) + mock_db_instance.add_transaction.assert_called_once_with(None, account_id, amount, "deposit") + +@patch('app.database.Database') +def test_withdraw_positive_amount(mock_db_class): + mock_db_instance = mock_db_class.return_value + transaction = Transaction(mock_db_instance) + account_id = 1 + amount = 500 + initial_balance = 1000 + mock_db_instance.get_account.return_value = (account_id, None, None, initial_balance) + transaction.withdraw(account_id, amount) + mock_db_instance.get_account.assert_called_once_with(account_id) + mock_db_instance.update_account_balance.assert_called_once_with(account_id, initial_balance - amount) + mock_db_instance.add_transaction.assert_called_once_with(account_id, None, amount, "withdrawal") + +@patch('app.database.Database') +def test_transfer_positive_amount(mock_db_class): + mock_db_instance = mock_db_class.return_value + transaction = Transaction(mock_db_instance) + from_account_id = 1 + to_account_id = 2 + amount = 500 + initial_balance_from = 1000 + initial_balance_to = 2000 + mock_db_instance.get_account.side_effect = [ + (from_account_id, None, None, initial_balance_from), + (to_account_id, None, None, initial_balance_to) + ] + transaction.transfer(from_account_id, to_account_id, amount) + mock_db_instance.get_account.assert_any_call(from_account_id) + mock_db_instance.get_account.assert_any_call(to_account_id) + mock_db_instance.update_account_balance.assert_any_call(from_account_id, initial_balance_from - amount) + mock_db_instance.update_account_balance.assert_any_call(to_account_id, initial_balance_to + amount) + mock_db_instance.add_transaction.assert_called_once_with(from_account_id, to_account_id, amount, "transfer") \ No newline at end of file