diff --git a/.github/workflows/unittests.yaml b/.github/workflows/unittests.yaml new file mode 100644 index 00000000..9b7f6025 --- /dev/null +++ b/.github/workflows/unittests.yaml @@ -0,0 +1,16 @@ +name: Continuous Integration +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.10.0 + architecture: x64 + - name: Install dependencies + run: pip install -r requirements.txt + - name: Run Tests + run: python -m pytest \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a81c8ee1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,138 @@ +# 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 + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__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/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..7da850cf --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# BankAccount +This is an educational public repository to illustrate the power of automated testing through Github Actions. + +# Run locally +1. Set up Python virtual environment. +``` +python -m venv venv +``` +2. Install required dependencies. +``` +pip install -r requirements.txt +``` +3. Run unit tests. +``` +python -m pytest +``` +4. Run the app. +``` +python app.py +``` \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 00000000..713cf648 --- /dev/null +++ b/app.py @@ -0,0 +1,28 @@ +from flask import Flask, jsonify, request + +app = Flask(__name__) + +balance = 0 + +@app.route('/') +def index(): + return jsonify({'balance': balance}) + +@app.route('/deposit') +def deposit(): + global balance + amount = request.args.get('amount') + balance = balance + int(amount) + return jsonify({'balance': balance}) + +@app.route('/withdraw') +def withdraw(): + global balance + amount = request.args.get('amount') + if(int(amount) > balance): + return jsonify({'balance': balance}) + balance = balance - int(amount) + return jsonify({'balance': balance}) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..61deed63 Binary files /dev/null and b/requirements.txt differ diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..95bd6280 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,13 @@ +import pytest + +from app import app as flask_app + + +@pytest.fixture +def app(): + yield flask_app + + +@pytest.fixture +def client(app): + return app.test_client() diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 00000000..9a1dd2fb --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,57 @@ +import json + +def test_balance(app, client): + del app + res = client.get('/') + assert res.status_code == 200 + expected = {'balance': 0} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_deposit(app, client): + del app + res = client.get('/deposit?amount=500') + assert res.status_code == 200 + expected = {'balance': 500} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_deposit_2(app, client): + del app + res = client.get('/deposit?amount=300') + assert res.status_code == 200 + expected = {'balance': 800} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_withdraw_1(app, client): + del app + res = client.get('/withdraw?amount=1000') + assert res.status_code == 200 + expected = {'balance': 800} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_withdraw_2(app, client): + del app + res = client.get('/withdraw?amount=100') + assert res.status_code == 200 + expected = {'balance': 700} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_deposit_3(app, client): + del app + res = client.get('/deposit?amount=500') + assert res.status_code == 200 + expected = {'balance': 1200} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_withdraw_3(app, client): + del app + res = client.get('/withdraw?amount=1000') + assert res.status_code == 200 + expected = {'balance': 200} + assert expected == json.loads(res.get_data(as_text=True)) + +def test_balance_2(app, client): + del app + res = client.get('/') + assert res.status_code == 200 + expected = {'balance': 200} + assert expected == json.loads(res.get_data(as_text=True)) \ No newline at end of file