Browse Source

Initial commit

feedback
github-classroom[bot] 2 years ago committed by GitHub
commit
bc35187221
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      .github/workflows/main.yaml
  2. 162
      .gitignore
  3. 112
      README.md
  4. 0
      app/__init__.py
  5. 13
      app/account.py
  6. 69
      app/bank.py
  7. 21
      app/customer.py
  8. 127
      app/database.py
  9. 102
      app/main.py
  10. 34
      app/transaction.py
  11. 189
      poetry.lock
  12. 17
      pyproject.toml
  13. 0
      tests/__init__.py
  14. 0
      tests/e2e/__init__.py
  15. 6
      tests/e2e/test_bank.py
  16. 0
      tests/unit/__init__.py
  17. 4
      tests/unit/test_account.py
  18. 5
      tests/unit/test_customer.py
  19. 5
      tests/unit/test_database.py
  20. 5
      tests/unit/test_transaction.py

3
.github/workflows/main.yaml

@ -0,0 +1,3 @@
name: lab-testing
# add your pipline description below

162
.gitignore vendored

@ -0,0 +1,162 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.DS_Store
*.db

112
README.md

@ -0,0 +1,112 @@
# Bank Management System Project [LAB-TESTING]
## Overview
This project simulates a basic bank management system, designed to demonstrate the functionalities such as managing customers, accounts, and processing transactions. It's developed with a focus on testing, to utilize unit tests, mocks, and end-to-end (E2E) tests to ensure reliability and functionality.
## Background on Testing
Testing is an essential aspect of software development, ensuring that each part of the codebase works correctly and as expected. For this project, we emphasize three main keypoints:
- **Unit Tests**: Aim to test individual components or functions in isolation, ensuring that each piece of the system performs its intended task.
- **Mocking**: Involves simulating the behavior of real objects in controlled ways. Mocking is crucial for unit testing components that interact with external systems, like databases, to ensure tests are isolated and repeatable without side effects.
- **End-to-End (E2E) Tests**: Simulate real-user scenarios from start to finish, testing the system as a whole. E2E tests verify that all components of the application work together correctly to perform the intended tasks.
## Dependency Management with Poetry
For this project, we use [Poetry](https://python-poetry.org/) for dependency management and packaging. Poetry helps to simplify the management of project dependencies and environments.
To get started with Poetry:
1. Install Poetry by following the instructions on the [official documentation](https://python-poetry.org/docs/#installation).
2. Navigate to the project directory and run `poetry install` to install all dependencies defined in `pyproject.toml`.
```bash
sudo -H pip3 install poetry
poetry install
poetry add pytest pytest-mock
```
# LAB ASSIGNMENT
In this lab, you are required to create several tests. Please note that there are 5 main modules of the project under `app/` folder.
You have to learn them and write tests in the corresponding files inside the `tests/`.
For `Unit tests` - please add your tests in the files inside `tests/unit/test_*.py`. You'll need to add tests exclusively to these files.
> :warning: **Note:** if you add tests, all tests should pass for final submission.
> :warning: **Note:** the `app/` folder should not be altered and will be replaced in the autograding step.
> :warning: **Note:** use `poetry` for the Python environment and dependencies management.
### 1. Unit Tests (3pt)
- Create unit tests for the following modules to ensure each component functions correctly in isolation:
- `database.py` (At least 5 tests)
- `customer.py` (At least 2 tests)
- `account.py` (At least 2 tests)
- `transaction.py` (At least 2 tests)
- `bank.py`
- Utilize `unittest.mock` and `pytest-mock` to simulate external dependencies (code components from other modules) effectively.
- Add your tests in the corresponding files under `/tests/unit/` directory.
- Apply mocks in testing for all modules to accurately simulate external dependencies without relying on the actual database.
> :warning: **Note:** that mocking must be proper. For example, when you mock the database, the actual database file should not be touched by the system.
### 2. End-to-End (E2E) Tests (1pt)
- **`test_bank.py`**: Acts as an E2E test, it should call combination of all scripts. It should simulate real-life usage scenarios of the bank system.
- Add your test in the corresponding file `/tests/e2e/test_bank.py`
### 3. CI/CD with GitHub Actions (1pt)
- **Pipeline**: Implement a CI/CD pipeline using GitHub Actions, configured in the `main.yaml` file. This pipeline should automate the execution of tests.
> :warning: **Note:** The unit tests with mocks should be called on PUSH and the end-to-end on PULL.The unit tests with mocks should be called on when a commit is pushed and the end-to-end when a pull request is created.
## Running Tests
Tests are to be created inside the corresponding test files ( check the boiler plates in the files).
To execute all tests, use the following Poetry command:
```bash
poetry run pytest
```
To run a specific test:
```bash
poetry run pytest tests/unit/test_*.py
```
## Project Structure
```
Bank/
├── app/
│ ├── __init__.py
│ ├── bank.py
│ ├── database.py
│ ├── customer.py
│ ├── account.py
│ └── transaction.py
└── tests/
├── __init__.py
├── unit/
│ ├── __init__.py
│ ├── test_account.py
│ ├── test_customer.py
│ ├── test_transaction.py
│ └── test_database.py
└── e2e/
├── __init__.py
└── test_bank.py
```

0
app/__init__.py

13
app/account.py

@ -0,0 +1,13 @@
class Account:
def __init__(self, database, customer_id=None, account_type="checking", balance=0.0):
self.database = database
self.balance = balance
if customer_id:
self.account_id = self._create_account(customer_id, account_type, balance)
def _create_account(self, customer_id, account_type, balance):
return self.database.add_account(customer_id, account_type, balance)
def delete_account(self, account_id):
self.database.delete_account(account_id)

69
app/bank.py

@ -0,0 +1,69 @@
from .database import Database
from .transaction import Transaction
from .customer import Customer
from .account import Account
class Bank:
def __init__(self, db_path='bank.db'):
self.database = Database(db_path)
self.transaction_system = Transaction(self.database)
def add_customer(self, name, address):
"""Add a new customer to the bank."""
return self.database.add_customer(name, address)
def update_customer_details(self, customer_id, name, address):
"""Update details for an existing customer."""
self.database.update_customer(customer_id, name, address)
def delete_customer(self, customer_id):
"""Remove a customer and their accounts from the bank."""
accounts = self.database.get_customer_accounts(customer_id)
for account in accounts:
self.close_account(account[0])
self.database.delete_customer(customer_id)
def open_account(self, customer_id, account_type, balance):
"""Open a new account for an existing customer."""
account = Account(self.database, customer_id=customer_id, account_type=account_type, balance=balance)
return account.account_id
def close_account(self, account_id):
"""Close an existing account."""
self.database.delete_account(account_id)
def deposit_to_account(self, account_id, amount):
"""Deposit money into an account."""
self.transaction_system.deposit(account_id, amount)
def withdraw_from_account(self, account_id, amount):
"""Withdraw money from an account."""
self.transaction_system.withdraw(account_id, amount)
def transfer_between_accounts(self, from_account_id, to_account_id, amount):
"""Transfer money between two accounts."""
self.transaction_system.transfer(from_account_id, to_account_id, amount)
def get_customer_accounts(self, customer_id):
"""Retrieve all accounts associated with a customer."""
return self.database.get_customer_accounts(customer_id)
def get_account_transactions(self, account_id):
"""Get a list of transactions for a specific account."""
return self.database.get_transactions(account_id)
def get_all_customers(self):
"""Retrieve all customers from the bank."""
return self.database.get_all_customers()
def get_account(self, account_id):
"""Retrieve details for a specific account."""
return self.database.get_account(account_id)
def close_connection(self):
self.database.close()

21
app/customer.py

@ -0,0 +1,21 @@
class Customer:
def __init__(self, database, customer_id=None, name=None, address=None):
self.database = database
self.customer_id = customer_id
if customer_id is None and name and address:
self.customer_id = self.database.add_customer(name, address)
elif customer_id:
self._load_customer()
def _load_customer(self):
details = self.database.get_customer(self.customer_id)
if details:
self.name, self.address = details[1], details[2]
else:
raise ValueError("Customer does not exist.")
def update_details(self, name, address):
self.database.update_customer(self.customer_id, name, address)
def delete_customer(self):
self.database.delete_customer(self.customer_id)

127
app/database.py

@ -0,0 +1,127 @@
import sqlite3
class Database:
def __init__(self, db_path='bank.db'):
self.db_path = db_path
self.conn = sqlite3.connect(self.db_path)
self._init_db()
def _init_db(self):
cursor = self.conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS customers (
customer_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
address TEXT NOT NULL
);""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS accounts (
account_id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER,
account_type TEXT NOT NULL,
balance REAL NOT NULL,
FOREIGN KEY(customer_id) REFERENCES customers(customer_id)
);""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS transactions (
transaction_id INTEGER PRIMARY KEY AUTOINCREMENT,
from_account_id INTEGER,
to_account_id INTEGER,
amount REAL NOT NULL,
transaction_type TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(from_account_id) REFERENCES accounts(account_id),
FOREIGN KEY(to_account_id) REFERENCES accounts(account_id)
);""")
self.conn.commit()
def add_customer(self, name, address):
cursor = self.conn.cursor()
cursor.execute("""
INSERT INTO customers (name, address) VALUES (?, ?)
""", (name, address))
self.conn.commit()
return cursor.lastrowid
def update_customer(self, customer_id, name, address):
cursor = self.conn.cursor()
cursor.execute("""
UPDATE customers SET name = ?, address = ? WHERE customer_id = ?
""", (name, address, customer_id))
self.conn.commit()
def get_customer(self, customer_id):
"""Retrieve a customer's details by their customer ID."""
cursor = self.conn.cursor()
cursor.execute("SELECT * FROM customers WHERE customer_id = ?", (customer_id,))
return cursor.fetchone()
def delete_customer(self, customer_id):
cursor = self.conn.cursor()
cursor.execute("""
DELETE FROM customers WHERE customer_id = ?
""", (customer_id,))
self.conn.commit()
def add_account(self, customer_id, account_type, balance):
cursor = self.conn.cursor()
cursor.execute("""
INSERT INTO accounts (customer_id, account_type, balance) VALUES (?, ?, ?)
""", (customer_id, account_type, balance))
self.conn.commit()
return cursor.lastrowid
def get_account(self, account_id):
cursor = self.conn.cursor()
cursor.execute("""
SELECT account_id, customer_id, account_type, balance FROM accounts WHERE account_id = ?
""", (account_id,))
return cursor.fetchone()
def get_customer_accounts(self, customer_id):
"""Retrieve all accounts associated with a customer by their customer ID."""
cursor = self.conn.cursor()
cursor.execute("SELECT * FROM accounts WHERE customer_id = ?", (customer_id,))
return cursor.fetchall()
def update_account_balance(self, account_id, balance):
cursor = self.conn.cursor()
cursor.execute("""
UPDATE accounts SET balance = ? WHERE account_id = ?
""", (balance, account_id))
self.conn.commit()
def delete_account(self, account_id):
cursor = self.conn.cursor()
cursor.execute("""
DELETE FROM accounts WHERE account_id = ?
""", (account_id,))
self.conn.commit()
def add_transaction(self, from_account_id, to_account_id, amount, transaction_type):
self.conn.execute("INSERT INTO transactions (from_account_id, to_account_id, amount, transaction_type) VALUES (?, ?, ?, ?)",
(from_account_id, to_account_id, amount, transaction_type))
self.conn.commit()
def get_transactions(self, account_id):
cursor = self.conn.cursor()
cursor.execute("""
SELECT transaction_id, from_account_id, to_account_id, amount, transaction_type, timestamp
FROM transactions
WHERE from_account_id = ? OR to_account_id = ?
""", (account_id, account_id,))
return cursor.fetchall()
def get_all_customers(self):
"""Retrieve all customers from the database."""
cursor = self.conn.cursor()
cursor.execute("SELECT * FROM customers ORDER BY customer_id ASC")
return cursor.fetchall()
def close(self):
if self.conn:
self.conn.close()

102
app/main.py

@ -0,0 +1,102 @@
from .bank import Bank
def print_menu():
print("\n--- Bank System Main Menu ---")
print("1. Add Customer")
print("2. Update Customer Details")
print("3. Delete Customer")
print("4. Open Account")
print("5. Close Account")
print("6. Deposit")
print("7. Withdraw")
print("8. Transfer")
print("9. View Customer Accounts")
print("10. View Account Transactions")
print("11. View All Customers")
print("12. Exit")
def main():
bank = Bank()
while True:
print_menu()
choice = input("Enter your choice: ")
try:
if choice == "1":
name = input("Customer name: ")
address = input("Customer address: ")
customer_id = bank.add_customer(name, address)
print(f"Customer added with ID: {customer_id}")
elif choice == "2":
customer_id = int(input("Customer ID: "))
name = input("New name: ")
address = input("New address: ")
bank.update_customer_details(customer_id, name, address)
print("Customer details updated.")
elif choice == "3":
customer_id = int(input("Customer ID to delete: "))
bank.delete_customer(customer_id)
print("Customer deleted.")
elif choice == "4":
customer_id = int(input("Customer ID for new account: "))
account_type = input("Account type (checking/savings): ")
balance = float(input("Initial balance: "))
account_id = bank.open_account(customer_id, account_type, balance)
print(f"Account {account_id} opened.")
elif choice == "5":
account_id = int(input("Account ID to close: "))
bank.close_account(account_id)
print("Account closed.")
elif choice == "6":
account_id = int(input("Account ID for deposit: "))
amount = float(input("Amount to deposit: "))
bank.deposit_to_account(account_id, amount)
print("Deposit successful.")
elif choice == "7":
account_id = int(input("Account ID for withdrawal: "))
amount = float(input("Amount to withdraw: "))
bank.withdraw_from_account(account_id, amount)
print("Withdrawal successful.")
elif choice == "8":
from_account_id = int(input("From Account ID: "))
to_account_id = int(input("To Account ID: "))
amount = float(input("Amount to transfer: "))
bank.transfer_between_accounts(from_account_id, to_account_id, amount)
print("Transfer successful.")
elif choice == "9":
customer_id = int(input("Customer ID to view accounts: "))
accounts = bank.get_customer_accounts(customer_id)
for account in accounts:
print(account)
elif choice == "10":
account_id = int(input("Account ID to view transactions: "))
transactions = bank.get_account_transactions(account_id)
for transaction in transactions:
print(transaction)
elif choice == "11":
customers = bank.get_all_customers()
for customer in customers:
print(f"ID: {customer[0]}, Name: {customer[1]}, Address: {customer[2]}")
elif choice == "12":
print("Exiting the bank system.")
break
else:
print("Invalid choice, please try again.")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
main()

34
app/transaction.py

@ -0,0 +1,34 @@
class Transaction:
def __init__(self, database):
self.database = database
def deposit(self, account_id, amount):
if amount <= 0:
raise ValueError("Amount must be positive.")
account = self.database.get_account(account_id)
if not account:
raise ValueError("Account does not exist.")
new_balance = account[3] + amount
self.database.update_account_balance(account_id, new_balance)
self.database.add_transaction(None, account_id, amount, "deposit")
def withdraw(self, account_id, amount):
if amount <= 0:
raise ValueError("Amount must be positive.")
account = self.database.get_account(account_id)
if account[3] < amount:
raise ValueError("Insufficient funds.")
new_balance = account[3] - amount
self.database.update_account_balance(account_id, new_balance)
self.database.add_transaction(account_id, None, amount, "withdrawal")
def transfer(self, from_account_id, to_account_id, amount):
if amount <= 0:
raise ValueError("Amount must be positive.")
from_account = self.database.get_account(from_account_id)
to_account = self.database.get_account(to_account_id)
if from_account[3] < amount:
raise ValueError("Insufficient funds in the source account.")
self.database.update_account_balance(from_account_id, from_account[3] - amount)
self.database.update_account_balance(to_account_id, to_account[3] + amount)
self.database.add_transaction(from_account_id, to_account_id, amount, "transfer")

189
poetry.lock

@ -0,0 +1,189 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.4.1"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"},
{file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"},
{file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"},
{file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"},
{file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"},
{file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"},
{file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"},
{file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"},
{file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"},
{file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"},
{file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"},
{file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"},
{file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"},
{file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"},
{file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"},
{file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"},
{file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"},
{file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"},
{file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"},
{file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"},
{file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"},
{file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"},
{file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"},
{file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"},
{file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"},
{file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"},
{file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"},
{file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"},
{file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"},
{file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"},
{file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"},
{file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"},
{file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"},
{file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"},
{file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"},
{file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"},
{file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"},
{file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"},
{file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"},
{file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"},
{file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"},
{file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"},
{file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"},
{file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"},
{file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"},
{file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"},
{file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"},
{file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"},
{file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"},
{file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"},
{file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"},
{file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"},
]
[package.extras]
toml = ["tomli"]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "mock"
version = "5.1.0"
description = "Rolling backport of unittest.mock for all Pythons"
optional = false
python-versions = ">=3.6"
files = [
{file = "mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744"},
{file = "mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d"},
]
[package.extras]
build = ["blurb", "twine", "wheel"]
docs = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "packaging"
version = "23.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "pluggy"
version = "1.4.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pytest"
version = "8.0.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-8.0.1-py3-none-any.whl", hash = "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"},
{file = "pytest-8.0.1.tar.gz", hash = "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.3.0,<2.0"
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
version = "4.1.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
]
[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pytest-mock"
version = "3.12.0"
description = "Thin-wrapper around the mock package for easier use with pytest"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"},
{file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"},
]
[package.dependencies]
pytest = ">=5.0"
[package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "ba455ebc9b953fd646775b0eede8374fdf31580c422eebd588af5d272c410aa1"

17
pyproject.toml

@ -0,0 +1,17 @@
[tool.poetry]
name = "bank"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
pytest = "^8.0.1"
pytest-mock = "^3.11.0"
mock = "^5.1.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

0
tests/__init__.py

0
tests/e2e/__init__.py

6
tests/e2e/test_bank.py

@ -0,0 +1,6 @@
# - In this file, you have to write an E2E test on Bank project.
# - See, app/bank.py
# - 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.

0
tests/unit/__init__.py

4
tests/unit/test_account.py

@ -0,0 +1,4 @@
# - In this file, you have to add your tests on Account module.
# - See, app/account.py
# - Test account creation and deletion
# - Use mocks

5
tests/unit/test_customer.py

@ -0,0 +1,5 @@
# - In this file, you have to add your tests on Customer module.
# - See, app/customer.py
# - Test customer creation, loading, updating and deletion
# - Use mocks

5
tests/unit/test_database.py

@ -0,0 +1,5 @@
# - In this file, you have to add your tests on Database module.
# - See, app/database.py
# - Test most of the methods
# - Use mocks in proper parts

5
tests/unit/test_transaction.py

@ -0,0 +1,5 @@
# - In this file, you have to add your tests on Transaction module.
# - See, app/transaction.py
# - Test transaction with different types - deposit, withdraw and transfer
# - Use mocks accordingly
Loading…
Cancel
Save