diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1029ba4..c0412a4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,16 @@ -stages: - - build +default: + image: python:3.9 +stages: + - lint + - build build job: stage: build script: - cd src - - apt-get update -qy - - apt-get install -y python3-dev python3-pip python3.12-venv curl + # - apt-get update -qy + # - apt-get install -y python3-dev python3-pip python3.12-venv curl - python3 -V # - pip3 install --break-system-packages -r requirements.txt - curl -sSL https://pdm-project.org/install-pdm.py | python3 - @@ -22,11 +25,18 @@ build job: - export FLASK_APP=app - pdm run pytest tests --cov --cov-report term --cov-report html - artifacts: paths: - src/htmlcov/ +lint job: + stage: lint + dependencies: [] + script: + - python3 -m pip install flake8 + - flake8 src/ + allow_failure: true # Linter can fail, fixing it is for now outside of the projects scope + pages: stage: build dependencies: diff --git a/docs/questions-part1.md b/docs/questions-part1.md index 8a7c2db..54264b3 100644 --- a/docs/questions-part1.md +++ b/docs/questions-part1.md @@ -11,3 +11,7 @@ ## Q1.2 - It's a very bad practice. The secret key will be exposed in the codebase and can be easily accessed by anyone who has access to the codebase. This can lead to security vulnerabilities and compromise the integrity of the application. - To fix this, you can use environment variables to store the secret key. + +## Q1.3 +- A linter is a tool to statically analyse code for readability and improving code quality. It is usually executed from a standalone tool. +- It is used to check for errors, vulns, code smells or general issues but also to enforce a coding style over the whole project. \ No newline at end of file diff --git a/src/app.py b/src/app.py index 782c882..2f76b3b 100644 --- a/src/app.py +++ b/src/app.py @@ -18,12 +18,8 @@ from flask import request, Flask, url_for, render_template, redirect import operators import json -from dotenv import load_dotenv import os - - - __author__ = 'Michael Mäder' __date__ = "2025-03-10" __version__ = "0.5" @@ -40,7 +36,8 @@ A little web application that offers API calls for arithmetic operations # creation of the Flask application app = Flask(__name__) -app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY') # super secure key against CSRF attacks +# super secure key against CSRF attacks +app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY') # global variable containing the name of the login user global_data = {'username': 'no_user'} @@ -51,18 +48,19 @@ global_data = {'username': 'no_user'} def plus_one(): try: x = int(request.args.get('x', 1)) - except: + except ValueError: return json.dumps({"error": "Type error"}) return json.dumps({'x': operators.addition(x, 1)}) + # addition route, the parameters will be passed with 'x' and 'y' @app.route('/add') def plus_y(): try: x = int(request.args.get('x', 1)) y = int(request.args.get('y', 1)) - except: + except ValueError: return json.dumps({"error": "Type error"}) return json.dumps({'result': operators.addition(x, y)}) @@ -73,7 +71,7 @@ def multiply_y(): try: x = int(request.args.get('x', 1)) y = int(request.args.get('y', 1)) - except: + except ValueError: return json.dumps({"error": "Type error"}) return json.dumps({'result': operators.multiplication(x, y)}) @@ -84,7 +82,7 @@ def division_y(): try: x = int(request.args.get('x', 1)) y = int(request.args.get('y', 1)) - except: + except ValueError: return json.dumps({"error": "Type error"}) return json.dumps({'result': operators.division(x, y)}) diff --git a/src/operators.py b/src/operators.py index 8aa5a40..5168248 100644 --- a/src/operators.py +++ b/src/operators.py @@ -23,17 +23,17 @@ __email__ = "michael.maeder@hefr.ch" # file containing the operations for the calculator -# addition: returns x+y +# addition: returns x+y def addition(x, y): return x+y -# subtraction: returns x-y +# subtraction: returns x-y def subtraction(x, y): return x-y -# multiplication: returns x*y +# multiplication: returns x*y def multiplication(x, y): return x*y @@ -42,6 +42,6 @@ def multiplication(x, y): # returns None if the divisor is zero # the result might be of float type def division(x, y): - if y==0: + if y == 0: return None return x/y diff --git a/src/tests/conftest.py b/src/tests/conftest.py index ed58e1a..ed50b44 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -5,7 +5,7 @@ from app import app # the client here allow to use the app without running in live server -# see https://flask.palletsprojects.com/en/2.0.x/testing/#sending-requests-with-the-test-client +# see https://flask.palletsprojects.com/en/2.0.x/testing/#sending-requests-with-the-test-client # noqa: E501 @pytest.fixture def client(): app.config['TESTING'] = True diff --git a/src/tests/test_api.py b/src/tests/test_api.py index cceefb7..486ddbc 100644 --- a/src/tests/test_api.py +++ b/src/tests/test_api.py @@ -17,7 +17,6 @@ from urllib.parse import urlencode import json -import pytest __author__ = 'Michael Mader' __date__ = "2023-03-12" @@ -27,12 +26,12 @@ __email__ = "michael.maeder@hefr.ch" def call(client, path, params): """calling function that simulates an API webcall of a specific function/route - + Args: client: this is the client object used by pytest to 'simulate' the API without running the webserver path: route of the API to use params: GET parameter that are passed to the function - + Returns: json: the result of the client call """ @@ -40,50 +39,59 @@ def call(client, path, params): response = client.get(url) return json.loads(response.data.decode('utf-8')) + # increment test 1 def test_plus_one1(client): result = call(client, '/inc', {'x': 2}) assert result['x'] == 3 + # increment test 1 def test_plus_one2(client): result = call(client, '/inc', {'x': -2}) assert result['x'] == -1 + # adding test with negative value def test_plus_y(client): result = call(client, '/add', {'x': -2, 'y': 7}) assert result['result'] == 5 + # multiplication test def test_multiply(client): result = call(client, '/mul', {'x': -2, 'y': 7}) assert result['result'] == -14 + # division test def test_division(client): result = call(client, '/div', {'x': 35, 'y': 7}) assert result['result'] == 5 + # type tests def test_type_inc(client): result = call(client, '/inc', {'x': 'a'}) assert result["error"] == "Type error" + def test_type_add(client): result = call(client, '/add', {'x': 'a', 'y': 1}) assert result["error"] == "Type error" result = call(client, '/add', {'x': 1, 'y': 'a'}) assert result["error"] == "Type error" + def test_type_mul(client): result = call(client, '/mul', {'x': 'a', 'y': 1}) assert result["error"] == "Type error" result = call(client, '/mul', {'x': 1, 'y': 'a'}) assert result["error"] == "Type error" + def test_type_div(client): result = call(client, '/div', {'x': 'a', 'y': 1}) assert result["error"] == "Type error" result = call(client, '/div', {'x': 1, 'y': 'a'}) - assert result["error"] == "Type error" \ No newline at end of file + assert result["error"] == "Type error" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..8000b14 --- /dev/null +++ b/tox.ini @@ -0,0 +1,5 @@ +[flake8] +extend-ignore = E203 +exclude = .git,__pycache__,.venv +max-complexity = 10 +max-line-length = 120