diff --git a/Pipfile b/Pipfile index 123122bb5d3cdd5b0abf66ecec6906d0154a4e02..a04ff596da5df669dcfb74da4376731bf1f577d5 100644 --- a/Pipfile +++ b/Pipfile @@ -23,11 +23,14 @@ psycopg2-binary = "*" flask-restplus = "*" celery = {extras = ["auth", "yaml", "msgpack", "redis"]} python-ldap = "*" +mockredispy = "*" [dev-packages] pytest-cov = "*" pylint = "*" pytest = "*" +pytest-mock = "*" +mock = "*" [requires] python_version = "3.6" diff --git a/portal/rest/errors.py b/portal/rest/errors.py index 3c74490983652499c9153c3592084e4cded0323f..27e6805d2e09675c3366911e3210ff9f9225d7e7 100644 --- a/portal/rest/errors.py +++ b/portal/rest/errors.py @@ -1,3 +1,4 @@ +import flask from flask import Flask import json import logging @@ -20,7 +21,7 @@ def load_errors(app: Flask): @rest_api.errorhandler def default_error_handler(): - return {'message': 'Default error handler has been triggered'}, 401 + return flask.jsonify({'message': 'Default error handler has been triggered'}), 401 @rest_api.errorhandler(PortalAPIError) @@ -32,27 +33,27 @@ def handle_portal_api_error(ex: PortalAPIError): @rest_api.errorhandler(NoAuthorizationError) def handle_missing_auth_header(ex: NoAuthorizationError): log.debug(f"[AUTH] Auth headers are missing: {ex} ") - return json.dumps({'message': 'Auth headers is missing', 'error': f'{ex}'}), 401 + return flask.jsonify({'message': 'Auth headers is missing', 'error': f'{ex}'}), 401 @rest_api.errorhandler(IncorrectCredentialsError) def handle_missing_auth_header(ex: IncorrectCredentialsError): log.debug(f"[AUTH] Credentials are incorrect: {ex} ") - return json.dumps({'message': 'Credentials are incorrect', 'error': f'{ex}'}), 401 + return flask.jsonify({'message': 'Credentials are incorrect', 'error': f'{ex}'}), 401 @rest_api.errorhandler(ValidationError) def handle_validation_error(ex: ValidationError): log.warning(f"[VALID] Validation failed: {ex}") - return json.dumps({'message': f"Validation failed on: {ex.messages}"}), 400 + return flask.jsonify({'message': f"Validation failed on: {ex.messages}"}), 400 @rest_api.errorhandler(SQLAlchemyError) def handle_db_error(ex: SQLAlchemyError): log.error(f"[DB] Error: {ex}", ex) - return json.dumps({'message': f'Database error: {ex}'}), 400 + return flask.jsonify({'message': f'Database error: {ex}'}), 400 @rest_api.errorhandler(NotImplementedError) def handle_not_implemented_error(): - return json.dumps({'message': f'Not implemented yet!'}), 404 + return flask.jsonify({'message': f'Not implemented yet!'}), 404 diff --git a/portal/service/projects.py b/portal/service/projects.py index a1fdb0d435d278d1035039d02a5c08ad8aba2ddb..76457033dd255ff7fae31508ac213a1af36856b8 100644 --- a/portal/service/projects.py +++ b/portal/service/projects.py @@ -89,7 +89,8 @@ def create_project(course: Course, data: dict) -> Project: Returns(Project): Project instance """ - test_files_source = data['config']['test_files_source'] + config = data.get('config') + test_files_source = config.get('test_files_source') if config else None new_project = Project(course=course, test_files_source=test_files_source) __set_project_data(project=new_project, data=data) log.info(f"[CREATE] Project for course {course.id} ({course.codename}): " diff --git a/tests/rest/test_group.py b/tests/rest/test_group.py index 9c149a8aa4e7ec21c5236ead14759e8cc0fa165e..44aada37eb84af8565db3ec82c5c024185139401 100644 --- a/tests/rest/test_group.py +++ b/tests/rest/test_group.py @@ -279,9 +279,9 @@ def test_remove_user_not_in_group(client): u = User.query.filter_by(username="student1").first() assert g not in u.groups - with pytest.raises(PortalAPIError): - path = f"/courses/{tc2.codename}/groups/{g.name}/users/{u.username}" - utils.make_request(client, path, method='delete') + path = f"/courses/{tc2.codename}/groups/{g.name}/users/{u.username}" + response = utils.make_request(client, path, method='delete') + response.status_code == 400 g_updated = Group.query.filter( Group.course_id == tc2.id).filter(Group.id == g.id).first() diff --git a/tests/rest/test_project.py b/tests/rest/test_project.py index 5bce235293e6d97e6092a832be35a17bfacb6846..761eb56a7a50dbb57456f8f3cb076cd8174281f2 100644 --- a/tests/rest/test_project.py +++ b/tests/rest/test_project.py @@ -1,6 +1,7 @@ import json from datetime import timedelta from flask_jwt_extended import create_access_token +import pytest from portal.database.models import Course, Project, User from portal.tools.time import current_time, strip_seconds @@ -31,8 +32,8 @@ def test_create(client): "name": "new_project", "config": { "test_files_source": "foo@git.git", - }, - } + }, + } request_json = json.dumps(request_dict) response = utils.make_request(client, f"/courses/{cpp.codename}/projects", method='post', data=request_json, @@ -68,7 +69,7 @@ def test_update(client): p_name = p.name request_dict = dict( name="new project name", - ) + ) request_json = json.dumps(request_dict) response = utils.make_request(client, f"/courses/{cpp.codename}/projects/{p.name}", data=request_json, method='put', @@ -120,7 +121,7 @@ def test_config_update(client): test_files_source="new source", pre_submit_script="a python script", submissions_allowed_to=new_time - ) + ) request_json = json.dumps(request_dict, cls=utils.DateTimeEncoder) response = utils.make_request(client, f"/courses/{cpp.codename}/projects/{p.name}/config", @@ -185,9 +186,9 @@ def test_create_submission(client): "url": "https://gitlab.fi.muni.cz/xkompis/test-hello-world.git", "branch": "master", "checkout": "master" + } } } - } request_json = json.dumps(request_dict, cls=utils.DateTimeEncoder) path = f"/courses/{cpp.codename}/projects/{p.name}/submissions" diff --git a/tests/rest/test_role.py b/tests/rest/test_role.py index 161265c7636b772e101f981bfa173c85f34a49b1..983e77973dad0550c7fe85155ee9e91c3ae4a198 100644 --- a/tests/rest/test_role.py +++ b/tests/rest/test_role.py @@ -1,9 +1,7 @@ -import pytest +import json from portal.database.models import Course, Role, User -from portal.service.errors import PortalAPIError from . import utils -import json def test_list(client): @@ -28,7 +26,7 @@ def test_create(client): request_dict = { "name": "new role", "description": "new role desc" - } + } request_json = json.dumps(request_dict) response = utils.make_request(client, f"/courses/{cpp.codename}/roles", data=request_json, headers={"content-type": "application/json"}, method='post') @@ -64,9 +62,10 @@ def test_update(client): request_dict = dict( name="new role name", description="new role desc", - ) + ) request_json = json.dumps(request_dict) - response = utils.make_request(client, f"/courses/{cpp.codename}/roles/{r.name}", data=request_json, + response = utils.make_request(client, f"/courses/{cpp.codename}/roles/{r.name}", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 assert response.mimetype == 'application/json' @@ -118,9 +117,10 @@ def test_users_update_add(client): request_dict = dict( add=[user.id] - ) + ) request_json = json.dumps(request_dict) - response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", data=request_json, + response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 @@ -142,9 +142,10 @@ def test_users_update_add_duplicate(client): request_dict = dict( add=[user.id] - ) + ) request_json = json.dumps(request_dict) - response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", data=request_json, + response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 @@ -166,9 +167,10 @@ def test_users_update_remove(client): request_dict = dict( remove=[user.id] - ) + ) request_json = json.dumps(request_dict) - response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", data=request_json, + response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 @@ -190,9 +192,10 @@ def test_users_update_remove_user_not_in(client): request_dict = dict( remove=[user.id] - ) + ) request_json = json.dumps(request_dict) - response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", data=request_json, + response = utils.make_request(client, f"/courses/{c.codename}/roles/{r.name}/users", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 @@ -269,9 +272,9 @@ def test_remove_user_not_in(client): u = User.query.filter_by(username="student1").first() assert r not in u.roles - with pytest.raises(PortalAPIError): - utils.make_request( - client, f"/courses/{c.codename}/roles/{r.name}/users/{u.username}", method='delete') + response = utils.make_request( + client, f"/courses/{c.codename}/roles/{r.name}/users/{u.username}", method='delete') + assert response.status_code == 400 r_updated = Role.query.filter( Role.course_id == c.id).filter(Role.id == r.id).first() @@ -301,10 +304,11 @@ def test_permissions_update(client): view_course_full=True, write_roles=True, read_submissions_all=True - ) + ) request_json = json.dumps(request_dict, cls=utils.DateTimeEncoder) - response = utils.make_request(client, f"/courses/{cpp.codename}/roles/{r.name}/permissions", data=request_json, + response = utils.make_request(client, f"/courses/{cpp.codename}/roles/{r.name}/permissions", + data=request_json, headers={"content-type": "application/json"}, method='put') assert response.status_code == 204 assert response.mimetype == 'application/json' diff --git a/tests/rest/test_user.py b/tests/rest/test_user.py index 96d46133ff81fb8f8ab0a5e0f26625d0d3701854..6d413422d7eea610a9957efe5bc114a5121d3f0b 100644 --- a/tests/rest/test_user.py +++ b/tests/rest/test_user.py @@ -135,8 +135,8 @@ def test_list_filter_group(client): def test_list_filter_missing_course(client): - with pytest.raises(PortalAPIError): - utils.make_request(client, '/users?group=g1', method='get') + response = utils.make_request(client, '/users?group=g1', method='get') + assert response.status_code == 400 def test_read(client): @@ -231,10 +231,10 @@ def test_read_submissions_filter_course_project(client): def test_read_submissions_filter_missing_course(client): - with pytest.raises(PortalAPIError): - user = User.query.filter_by(username="student1").first() - path = f"/users/{user.id}/submissions?project=p1" - utils.make_request(client, path, method='get') + user = User.query.filter_by(username="student1").first() + path = f"/users/{user.id}/submissions?project=p1" + response = utils.make_request(client, path, method='get') + assert response.status_code == 400 def test_read_roles(client):