From 724322dad09ed48a050e370b806d8e86a170f4c8 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 9 Oct 2023 15:53:36 +0200 Subject: [PATCH 01/53] new functionality --- dds_web/api/project.py | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 4b00c8042..6f0cf572b 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -188,6 +188,102 @@ def post(self): return {"message": return_message} + @auth.login_required(role=["Unit Admin", "Unit Personnel"]) + @logging_bind_request + @json_required + @handle_validation_errors + def patch(self): + """Partially update a the project status""" + # Get project ID, project and verify access + project_id = dds_web.utils.get_required_item(obj=flask.request.args, req="project") + project = dds_web.utils.collect_project(project_id=project_id) + dds_web.utils.verify_project_access(project=project) + + # Cannot change project status if project is busy + if project.busy: + raise ProjectBusyError( + message=( + f"The status for the project '{project_id}' is already in the process of being changed. " + "Please try again later. \n\nIf you know the project is not busy, contact support." + ) + ) + + self.set_busy(project=project, busy=True) + + try: + # get atributes + json_input = flask.request.get_json(silent=True) # Already checked by json_required + extend_deadline = json_input.get("extend_deadline", False) # False by default + + # some variable definition + curr_date = dds_web.utils.current_time() + send_email = False + + if extend_deadline: + # deadline can only be extended from Available + if not project.current_status == "Available": + raise DDSArgumentError("Can only extend deadline if the project is available") + + new_deadline_in = json_input.get("new_deadline_in") + if not new_deadline_in: + raise DDSArgumentError( + message="No new deadline provived, cannot perform operation." + ) + if new_deadline_in > 90: + raise DDSArgumentError( + message="The deadline needs to be less than (or equal to) 90 days." + ) + + if project.times_expired > 2: + raise DDSArgumentError( + "Project availability limit: Project cannot have an extended deadline any more times" + ) + + try: + # add a fake archived status to mimick a re-release in order to have an udpated deadline + new_status_row = self.expire_project( + project=project, + current_time=curr_date, + deadline_in=project.responsible_unit.days_in_expired, + ) + project.project_statuses.append(new_status_row) + db.session.commit() + + new_status_row = self.release_project( + project=project, current_time=curr_date, deadline_in=new_deadline_in + ) + project.project_statuses.append(new_status_row) + + project.busy = False # return to not busy + db.session.commit() + + except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err: + flask.current_app.logger.exception(err) + db.session.rollback() + raise DatabaseError( + message=str(err), + alt_message=( + "Status was not updated" + + ( + ": Database malfunction." + if isinstance(err, sqlalchemy.exc.OperationalError) + else ": Server Error." + ) + ), + ) from err + + return_message = f"{project.public_id} has been given a new deadline" + + return_message += ( + f". An e-mail notification has{' not ' if not send_email else ' '}been sent." + ) + + except: + self.set_busy(project=project, busy=False) + raise + + return {"message": return_message} + @staticmethod @dbsession def set_busy(project: models.Project, busy: bool) -> None: From ef340d2d10c2944282a0a09ea591abcf4dff694b Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 9 Oct 2023 16:24:26 +0200 Subject: [PATCH 02/53] started tests --- dds_web/api/project.py | 14 +++++++++--- tests/api/test_project.py | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 6f0cf572b..015a3d8a5 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -225,13 +225,19 @@ def patch(self): raise DDSArgumentError("Can only extend deadline if the project is available") new_deadline_in = json_input.get("new_deadline_in") + current_deadline = (project.current_deadline - curr_date).days + if not new_deadline_in: raise DDSArgumentError( message="No new deadline provived, cannot perform operation." ) - if new_deadline_in > 90: + if current_deadline > 10: + raise DDSArgumentError( + message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet" + ) + if new_deadline_in + current_deadline > 90: raise DDSArgumentError( - message="The deadline needs to be less than (or equal to) 90 days." + message="The new deadline needs to be less than (or equal to) 90 days." ) if project.times_expired > 2: @@ -250,7 +256,9 @@ def patch(self): db.session.commit() new_status_row = self.release_project( - project=project, current_time=curr_date, deadline_in=new_deadline_in + project=project, + current_time=curr_date, + deadline_in=new_deadline_in + current_deadline, ) project.project_statuses.append(new_status_row) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 867da5ef3..bff12417d 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1079,6 +1079,53 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"] +def test_extend_deadline(module_client, boto3_session): + """Extend a project deadline of a project already release""" + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) + + # Release project with a small deadline + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available", "deadline": 5}, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + deadline = project.current_deadline + + time.sleep(1) # tests are too fast + + # extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"extend_deadline": True, "new_deadline_in": 20}, + ) + assert response.status_code == http.HTTPStatus.OK + assert project.times_expired == 1 + assert not project.current_deadline == deadline + + assert f"{project_id} has been given a new deadline" in response.json["message"] + assert "An e-mail notification has not been sent." in response.json["message"] + + def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session): """Mock the different expections that can occur when deleting project.""" current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() From ffa340bc6ea95889b34413a0fbbf72ced38d7d51 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 10 Oct 2023 11:49:05 +0200 Subject: [PATCH 03/53] status code --- dds_web/api/project.py | 20 ++++++++++++++++---- doc/status_codes_api.md | 5 +++++ tests/api/test_project.py | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 015a3d8a5..fa6cb0ee8 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -199,6 +199,17 @@ def patch(self): project = dds_web.utils.collect_project(project_id=project_id) dds_web.utils.verify_project_access(project=project) + # get atributes + json_input = flask.request.get_json(silent=True) # Already checked by json_required + + # false by default - operation must be confirmed by the user + confirmed_operation = json_input.get("confirmed", False) + if not isinstance(confirmed_operation, bool): + raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.") + if not confirmed_operation: + warning_message = "Operation must be confirmed before proceding" + return {"warning": warning_message} + # Cannot change project status if project is busy if project.busy: raise ProjectBusyError( @@ -211,18 +222,19 @@ def patch(self): self.set_busy(project=project, busy=True) try: - # get atributes - json_input = flask.request.get_json(silent=True) # Already checked by json_required extend_deadline = json_input.get("extend_deadline", False) # False by default # some variable definition curr_date = dds_web.utils.current_time() send_email = False + # Update the deadline if extend_deadline: # deadline can only be extended from Available if not project.current_status == "Available": - raise DDSArgumentError("Can only extend deadline if the project is available") + raise DDSArgumentError( + "you can only extend the deadline for a project that has the status Available" + ) new_deadline_in = json_input.get("new_deadline_in") current_deadline = (project.current_deadline - curr_date).days @@ -242,7 +254,7 @@ def patch(self): if project.times_expired > 2: raise DDSArgumentError( - "Project availability limit: Project cannot have an extended deadline any more times" + "The project has already been available for download 3 times. cannot extend the deadline again" ) try: diff --git a/doc/status_codes_api.md b/doc/status_codes_api.md index 2bd2bc1a1..58aa04f35 100644 --- a/doc/status_codes_api.md +++ b/doc/status_codes_api.md @@ -237,6 +237,11 @@ - `archive_project` - Database or S3 issues +#### `patch` + +- [Authentication errors](#authentication) + + ### GetPublic - [Authentication errors](#authentication) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index bff12417d..53a5a09ad 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1116,7 +1116,7 @@ def test_extend_deadline(module_client, boto3_session): tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"extend_deadline": True, "new_deadline_in": 20}, + json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, ) assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 1 From 5df28d6d0400f4e2c77192466942d70c0b3436d5 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 10 Oct 2023 14:05:59 +0200 Subject: [PATCH 04/53] finalized tests --- dds_web/api/project.py | 10 +- doc/status_codes_api.md | 22 ++- tests/api/test_project.py | 328 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 339 insertions(+), 21 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index fa6cb0ee8..a0e24e17f 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -207,14 +207,14 @@ def patch(self): if not isinstance(confirmed_operation, bool): raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.") if not confirmed_operation: - warning_message = "Operation must be confirmed before proceding" + warning_message = "Operation must be confirmed before proceding." return {"warning": warning_message} # Cannot change project status if project is busy if project.busy: raise ProjectBusyError( message=( - f"The status for the project '{project_id}' is already in the process of being changed. " + f"The deadline for the project '{project_id}' is already in the process of being changed. " "Please try again later. \n\nIf you know the project is not busy, contact support." ) ) @@ -233,7 +233,7 @@ def patch(self): # deadline can only be extended from Available if not project.current_status == "Available": raise DDSArgumentError( - "you can only extend the deadline for a project that has the status Available" + "you can only extend the deadline for a project that has the status Available." ) new_deadline_in = json_input.get("new_deadline_in") @@ -245,7 +245,7 @@ def patch(self): ) if current_deadline > 10: raise DDSArgumentError( - message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet" + message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet." ) if new_deadline_in + current_deadline > 90: raise DDSArgumentError( @@ -254,7 +254,7 @@ def patch(self): if project.times_expired > 2: raise DDSArgumentError( - "The project has already been available for download 3 times. cannot extend the deadline again" + "Project availability limit: Project cannot be made Available any more times." ) try: diff --git a/doc/status_codes_api.md b/doc/status_codes_api.md index 58aa04f35..84fb085c0 100644 --- a/doc/status_codes_api.md +++ b/doc/status_codes_api.md @@ -240,7 +240,27 @@ #### `patch` - [Authentication errors](#authentication) - +- `400 Bad Request` + - Decorators + - Json required but not provided + - Validation error + - Schemas + - Project does not exist + - Project is busy + - `extend_deadline` + - Invalid deadline + - No deadline provided + - Project is not in Available state + - Max number of times available reached +- `403 Forbidden` + - Schemas + - User does not have access to project +- `500 Internal Server Error` + - Database errors + - `delete_project` + - Database or S3 issues + - `archive_project` + - Database or S3 issues ### GetPublic diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 53a5a09ad..1eeb787f9 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -21,6 +21,7 @@ import tests from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins +from tests.api.test_user import get_existing_projects from dds_web.database import models from dds_web.api.project import UserProjects @@ -44,6 +45,15 @@ # "date_updated", ] +release_project_small_deadline = {"new_status": "Available", "deadline": 5} + +extend_deadline_data_no_confirmed = { + "extend_deadline": True, + "new_deadline_in": 20, +} + +extend_deadline_data = {**extend_deadline_data_no_confirmed, "confirmed": True} + @pytest.fixture(scope="module") def test_project(module_client): @@ -1079,36 +1089,290 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"] -def test_extend_deadline(module_client, boto3_session): - """Extend a project deadline of a project already release""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 +def test_extend_deadline_bad_confirmed(module_client, boto3_session): + """Try to extend a deadline and send a not boolean for confirmation""" + username = "researchuser" + # Get user + user = models.User.query.filter_by(username=username).one_or_none() + assert user + + # Get project + project = user.projects[0] + assert project + project_id = project.public_id + + # Release project response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available"}, ) assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + json_data = {**extend_deadline_data, "confirmed": "true"} + # extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=json_data, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert "`confirmed` is a boolean value: True or False." in response.json["message"] + + +def test_extend_deadline_no_confirmed(module_client, boto3_session): + """Try to extend a deadline before confirmation should sent a warning and no operation perfrom""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available"}, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + # extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=extend_deadline_data_no_confirmed, + ) + # status code is ok but no operation perfrom + assert response.status_code == http.HTTPStatus.OK + assert project.times_expired == 0 + + assert "Operation must be confirmed before proceding." in response.json["warning"] + + +def test_extend_deadline_when_busy(module_client, boto3_session): + """Request should not be possible when project is busy.""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available"}, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + # set to busy + project.busy = True + db.session.commit() + assert project.busy + + time.sleep(1) # tests are too fast + + # attempt to extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=extend_deadline_data, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + + assert ( + f"The deadline for the project '{project_id}' is already in the process of being changed. " + in response.json["message"] + ) + assert ( + "Please try again later. \n\nIf you know the project is not busy, contact support." + in response.json["message"] + ) + + +def test_extend_deadline_project_not_available(module_client, boto3_session): + """Is not possible to extend deadline to project in other status than available.""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # attempt to extend deadline - project is in progress + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=extend_deadline_data, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + + assert ( + "you can only extend the deadline for a project that has the status Available." + in response.json["message"] + ) + + +def test_extend_deadline_no_deadline(module_client, boto3_session): + """If no deadline has been provided it should fail""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available"}, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + # try to extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"extend_deadline": True, "confirmed": True}, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert "No new deadline provived, cannot perform operation." in response.json["message"] + + +def test_extend_deadline_not_enough_time_left(module_client, boto3_session): + """If there are more than 10 days left, extend deadline should not be possible""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project with a big deadline + current_deadline = 30 + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"new_status": "Available", "deadline": current_deadline}, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + # try to extend upon such deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert ( + f"There are still {current_deadline} days left, it is not possible to extend deadline yet." + in response.json["message"] + ) + + +def test_extend_deadline_too_much_days(module_client, boto3_session): + """If the new deadline together with the time left already is more than 90 days it should not work""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project with a small deadline + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=release_project_small_deadline, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + # try to extend deadline a lot of days + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"extend_deadline": True, "new_deadline_in": 90, "confirmed": True}, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert ( + "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"] + ) + + +def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): + """If the deadline has been extended more than 2 times it should not work""" + + project, _ = get_existing_projects() + project_id = project.public_id # Release project with a small deadline response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available", "deadline": 5}, + json=release_project_small_deadline, ) assert response.status_code == http.HTTPStatus.OK # hasnt been expired or extended deadline yet assert project.times_expired == 0 deadline = project.current_deadline + new_deadline_in = 1 + + for i in range(1, 4): + time.sleep(1) # tests are too fast + + # extend deadline with a small new deadline so we can do it several times + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={**extend_deadline_data, "new_deadline_in": new_deadline_in}, + ) + if i < 3: + assert response.status_code == http.HTTPStatus.OK + assert project.times_expired == i + assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in) + deadline = project.current_deadline + assert f"{project_id} has been given a new deadline" in response.json["message"] + assert "An e-mail notification has not been sent." in response.json["message"] + else: + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert ( + "Project availability limit: Project cannot be made Available any more times" + in response.json["message"] + ) + + +def test_extend_deadline_ok(module_client, boto3_session): + """Extend a project deadline of a project already release""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project with a small deadline + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=release_project_small_deadline, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + deadline = project.current_deadline time.sleep(1) # tests are too fast # extend deadline @@ -1116,16 +1380,50 @@ def test_extend_deadline(module_client, boto3_session): tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, + json=extend_deadline_data, ) assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 1 - assert not project.current_deadline == deadline + assert project.current_deadline == deadline + datetime.timedelta( + days=extend_deadline_data.get("new_deadline_in") + ) assert f"{project_id} has been given a new deadline" in response.json["message"] assert "An e-mail notification has not been sent." in response.json["message"] +def test_extend_deadline_mock_database_error(module_client, boto3_session): + """Mock error when updating the database""" + + project, _ = get_existing_projects() + project_id = project.public_id + + # Release project with a small deadline + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=release_project_small_deadline, + ) + assert response.status_code == http.HTTPStatus.OK + # hasnt been expired or extended deadline yet + assert project.times_expired == 0 + + time.sleep(1) # tests are too fast + + token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client) + with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror): + # extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=token, + query_string={"project": project_id}, + json=extend_deadline_data, + ) + assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR + assert "Saving database changes failed." in response.json["message"] + + def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session): """Mock the different expections that can occur when deleting project.""" current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() From 294d9b7e7097d178c514a673253bf069ac6d8ca2 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 10 Oct 2023 14:26:45 +0200 Subject: [PATCH 05/53] fix test --- tests/api/test_project.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 1eeb787f9..7e140af7b 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1092,14 +1092,7 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session def test_extend_deadline_bad_confirmed(module_client, boto3_session): """Try to extend a deadline and send a not boolean for confirmation""" - username = "researchuser" - # Get user - user = models.User.query.filter_by(username=username).one_or_none() - assert user - - # Get project - project = user.projects[0] - assert project + project, _ = get_existing_projects() project_id = project.public_id # Release project @@ -1113,13 +1106,12 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 - json_data = {**extend_deadline_data, "confirmed": "true"} # extend deadline response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json=json_data, + json={**extend_deadline_data, "confirmed": "true"}, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST assert "`confirmed` is a boolean value: True or False." in response.json["message"] From 9c3805a1c7fc1bee6ecf0615972f1c5b5f8bca5c Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 10 Oct 2023 15:23:09 +0200 Subject: [PATCH 06/53] change fixture --- tests/api/test_project.py | 104 +++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 7e140af7b..a98892931 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1089,16 +1089,16 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"] -def test_extend_deadline_bad_confirmed(module_client, boto3_session): +def test_extend_deadline_bad_confirmed(client, boto3_session): """Try to extend a deadline and send a not boolean for confirmation""" project, _ = get_existing_projects() project_id = project.public_id # Release project - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"new_status": "Available"}, ) @@ -1106,10 +1106,12 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast + # extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={**extend_deadline_data, "confirmed": "true"}, ) @@ -1117,16 +1119,16 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): assert "`confirmed` is a boolean value: True or False." in response.json["message"] -def test_extend_deadline_no_confirmed(module_client, boto3_session): +def test_extend_deadline_no_confirmed(client, boto3_session): """Try to extend a deadline before confirmation should sent a warning and no operation perfrom""" project, _ = get_existing_projects() project_id = project.public_id # Release project - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"new_status": "Available"}, ) @@ -1134,10 +1136,12 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast + # extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=extend_deadline_data_no_confirmed, ) @@ -1148,16 +1152,16 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): assert "Operation must be confirmed before proceding." in response.json["warning"] -def test_extend_deadline_when_busy(module_client, boto3_session): +def test_extend_deadline_when_busy(client, boto3_session): """Request should not be possible when project is busy.""" project, _ = get_existing_projects() project_id = project.public_id # Release project - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"new_status": "Available"}, ) @@ -1173,9 +1177,9 @@ def test_extend_deadline_when_busy(module_client, boto3_session): time.sleep(1) # tests are too fast # attempt to extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1191,16 +1195,16 @@ def test_extend_deadline_when_busy(module_client, boto3_session): ) -def test_extend_deadline_project_not_available(module_client, boto3_session): +def test_extend_deadline_project_not_available(client, boto3_session): """Is not possible to extend deadline to project in other status than available.""" project, _ = get_existing_projects() project_id = project.public_id # attempt to extend deadline - project is in progress - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1212,16 +1216,16 @@ def test_extend_deadline_project_not_available(module_client, boto3_session): ) -def test_extend_deadline_no_deadline(module_client, boto3_session): +def test_extend_deadline_no_deadline(client, boto3_session): """If no deadline has been provided it should fail""" project, _ = get_existing_projects() project_id = project.public_id # Release project - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"new_status": "Available"}, ) @@ -1229,10 +1233,12 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast + # try to extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"extend_deadline": True, "confirmed": True}, ) @@ -1240,7 +1246,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): assert "No new deadline provived, cannot perform operation." in response.json["message"] -def test_extend_deadline_not_enough_time_left(module_client, boto3_session): +def test_extend_deadline_not_enough_time_left(client, boto3_session): """If there are more than 10 days left, extend deadline should not be possible""" project, _ = get_existing_projects() @@ -1248,9 +1254,9 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session): # Release project with a big deadline current_deadline = 30 - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"new_status": "Available", "deadline": current_deadline}, ) @@ -1258,10 +1264,12 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast + # try to extend upon such deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, ) @@ -1272,16 +1280,16 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session): ) -def test_extend_deadline_too_much_days(module_client, boto3_session): +def test_extend_deadline_too_much_days(client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" project, _ = get_existing_projects() project_id = project.public_id # Release project with a small deadline - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=release_project_small_deadline, ) @@ -1289,8 +1297,10 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast + # try to extend deadline a lot of days - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, @@ -1302,16 +1312,16 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): ) -def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): +def test_extend_deadline_maxium_number_available_exceded(client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" project, _ = get_existing_projects() project_id = project.public_id # Release project with a small deadline - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=release_project_small_deadline, ) @@ -1326,9 +1336,9 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se time.sleep(1) # tests are too fast # extend deadline with a small new deadline so we can do it several times - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json={**extend_deadline_data, "new_deadline_in": new_deadline_in}, ) @@ -1347,16 +1357,16 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se ) -def test_extend_deadline_ok(module_client, boto3_session): +def test_extend_deadline_ok(client, boto3_session): """Extend a project deadline of a project already release""" project, _ = get_existing_projects() project_id = project.public_id # Release project with a small deadline - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=release_project_small_deadline, ) @@ -1368,9 +1378,9 @@ def test_extend_deadline_ok(module_client, boto3_session): time.sleep(1) # tests are too fast # extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1384,16 +1394,16 @@ def test_extend_deadline_ok(module_client, boto3_session): assert "An e-mail notification has not been sent." in response.json["message"] -def test_extend_deadline_mock_database_error(module_client, boto3_session): +def test_extend_deadline_mock_database_error(client, boto3_session): """Mock error when updating the database""" project, _ = get_existing_projects() project_id = project.public_id # Release project with a small deadline - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=release_project_small_deadline, ) @@ -1403,10 +1413,10 @@ def test_extend_deadline_mock_database_error(module_client, boto3_session): time.sleep(1) # tests are too fast - token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client) + token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client) with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror): # extend deadline - response = module_client.patch( + response = client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=token, query_string={"project": project_id}, From 996cc0813b73d8844b5bc52f34fa46471b400909 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 10 Oct 2023 16:19:31 +0200 Subject: [PATCH 07/53] go back to first approach of test --- tests/api/test_project.py | 324 +++++++++++++++++++++++++++----------- 1 file changed, 233 insertions(+), 91 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index a98892931..076a78abe 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -55,6 +55,39 @@ extend_deadline_data = {**extend_deadline_data_no_confirmed, "confirmed": True} +# HELPER FUNCTIONS ################################################################################## CONFIG # + + +def create_and_release_project(module_client, proj_data, release_data): + """Helper function that creates a project and set it ups as available""" + + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) + + # Release project + response = module_client.post( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=release_data, + ) + assert response.status_code == http.HTTPStatus.OK + + return project_id, project + + @pytest.fixture(scope="module") def test_project(module_client): """Create a shared test project""" @@ -1089,29 +1122,40 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"] -def test_extend_deadline_bad_confirmed(client, boto3_session): +def test_extend_deadline_bad_confirmed(module_client, boto3_session): """Try to extend a deadline and send a not boolean for confirmation""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project - response = client.post( + release_data = {"new_status": "Available"} + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available"}, + json=release_data, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast # extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json={**extend_deadline_data, "confirmed": "true"}, ) @@ -1119,29 +1163,40 @@ def test_extend_deadline_bad_confirmed(client, boto3_session): assert "`confirmed` is a boolean value: True or False." in response.json["message"] -def test_extend_deadline_no_confirmed(client, boto3_session): +def test_extend_deadline_no_confirmed(module_client, boto3_session): """Try to extend a deadline before confirmation should sent a warning and no operation perfrom""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project - response = client.post( + release_data = {"new_status": "Available"} + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available"}, + json=release_data, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast # extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=extend_deadline_data_no_confirmed, ) @@ -1152,22 +1207,35 @@ def test_extend_deadline_no_confirmed(client, boto3_session): assert "Operation must be confirmed before proceding." in response.json["warning"] -def test_extend_deadline_when_busy(client, boto3_session): +def test_extend_deadline_when_busy(module_client, boto3_session): """Request should not be possible when project is busy.""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project - response = client.post( + release_data = {"new_status": "Available"} + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available"}, + json=release_data, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast # set to busy project.busy = True @@ -1177,9 +1245,9 @@ def test_extend_deadline_when_busy(client, boto3_session): time.sleep(1) # tests are too fast # attempt to extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1195,16 +1263,27 @@ def test_extend_deadline_when_busy(client, boto3_session): ) -def test_extend_deadline_project_not_available(client, boto3_session): +def test_extend_deadline_project_not_available(module_client, boto3_session): """Is not possible to extend deadline to project in other status than available.""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") # attempt to extend deadline - project is in progress - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1216,29 +1295,40 @@ def test_extend_deadline_project_not_available(client, boto3_session): ) -def test_extend_deadline_no_deadline(client, boto3_session): +def test_extend_deadline_no_deadline(module_client, boto3_session): """If no deadline has been provided it should fail""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project - response = client.post( + release_data = {"new_status": "Available"} + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available"}, + json=release_data, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast # try to extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json={"extend_deadline": True, "confirmed": True}, ) @@ -1246,30 +1336,41 @@ def test_extend_deadline_no_deadline(client, boto3_session): assert "No new deadline provived, cannot perform operation." in response.json["message"] -def test_extend_deadline_not_enough_time_left(client, boto3_session): +def test_extend_deadline_not_enough_time_left(module_client, boto3_session): """If there are more than 10 days left, extend deadline should not be possible""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) - # Release project with a big deadline + # Release project current_deadline = 30 - response = client.post( + release_data = {"new_status": "Available", "deadline": current_deadline} + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"new_status": "Available", "deadline": current_deadline}, + json=release_data, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast # try to extend upon such deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, ) @@ -1280,27 +1381,37 @@ def test_extend_deadline_not_enough_time_left(client, boto3_session): ) -def test_extend_deadline_too_much_days(client, boto3_session): +def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project with a small deadline - response = client.post( + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=release_project_small_deadline, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast # try to extend deadline a lot of days - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, @@ -1312,33 +1423,44 @@ def test_extend_deadline_too_much_days(client, boto3_session): ) -def test_extend_deadline_maxium_number_available_exceded(client, boto3_session): +def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project with a small deadline - response = client.post( + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=release_project_small_deadline, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 + time.sleep(1) # tests are too fast deadline = project.current_deadline new_deadline_in = 1 - for i in range(1, 4): time.sleep(1) # tests are too fast # extend deadline with a small new deadline so we can do it several times - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json={**extend_deadline_data, "new_deadline_in": new_deadline_in}, ) @@ -1357,30 +1479,40 @@ def test_extend_deadline_maxium_number_available_exceded(client, boto3_session): ) -def test_extend_deadline_ok(client, boto3_session): +def test_extend_deadline_ok(module_client, boto3_session): """Extend a project deadline of a project already release""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project with a small deadline - response = client.post( + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=release_project_small_deadline, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - - deadline = project.current_deadline time.sleep(1) # tests are too fast + deadline = project.current_deadline # extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=extend_deadline_data, ) @@ -1394,29 +1526,39 @@ def test_extend_deadline_ok(client, boto3_session): assert "An e-mail notification has not been sent." in response.json["message"] -def test_extend_deadline_mock_database_error(client, boto3_session): +def test_extend_deadline_mock_database_error(module_client, boto3_session): """Mock error when updating the database""" - project, _ = get_existing_projects() - project_id = project.public_id + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + if current_unit_admins < 3: + create_unit_admins(num_admins=2) + current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() + assert current_unit_admins >= 3 + + response = module_client.post( + tests.DDSEndpoint.PROJECT_CREATE, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + json=proj_data, + ) + assert response.status_code == http.HTTPStatus.OK + project_id = response.json.get("project_id") + project = project_row(project_id=project_id) # Release project with a small deadline - response = client.post( + response = module_client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=release_project_small_deadline, ) assert response.status_code == http.HTTPStatus.OK - # hasnt been expired or extended deadline yet assert project.times_expired == 0 - time.sleep(1) # tests are too fast - token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client) + token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client) with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror): # extend deadline - response = client.patch( + response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=token, query_string={"project": project_id}, From c410d464dfcd6cdf4f8a1c7f6f0bc1f252cfd38f Mon Sep 17 00:00:00 2001 From: rv0lt Date: Wed, 11 Oct 2023 11:01:35 +0200 Subject: [PATCH 08/53] improved --- tests/api/test_project.py | 242 +++++--------------------------------- 1 file changed, 29 insertions(+), 213 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 076a78abe..e2d96efd0 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -21,7 +21,6 @@ import tests from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins -from tests.api.test_user import get_existing_projects from dds_web.database import models from dds_web.api.project import UserProjects @@ -58,7 +57,7 @@ # HELPER FUNCTIONS ################################################################################## CONFIG # -def create_and_release_project(module_client, proj_data, release_data): +def create_and_release_project(client, proj_data, release_data): """Helper function that creates a project and set it ups as available""" current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() @@ -67,9 +66,9 @@ def create_and_release_project(module_client, proj_data, release_data): current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() assert current_unit_admins >= 3 - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client), json=proj_data, ) assert response.status_code == http.HTTPStatus.OK @@ -77,9 +76,9 @@ def create_and_release_project(module_client, proj_data, release_data): project = project_row(project_id=project_id) # Release project - response = module_client.post( + response = client.post( tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client), query_string={"project": project_id}, json=release_data, ) @@ -1125,30 +1124,9 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session def test_extend_deadline_bad_confirmed(module_client, boto3_session): """Try to extend a deadline and send a not boolean for confirmation""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, - ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project - release_data = {"new_status": "Available"} - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_data, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1166,30 +1144,9 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): def test_extend_deadline_no_confirmed(module_client, boto3_session): """Try to extend a deadline before confirmation should sent a warning and no operation perfrom""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project - release_data = {"new_status": "Available"} - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_data, - ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1210,30 +1167,9 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): def test_extend_deadline_when_busy(module_client, boto3_session): """Request should not be possible when project is busy.""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project - release_data = {"new_status": "Available"} - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_data, - ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1242,8 +1178,6 @@ def test_extend_deadline_when_busy(module_client, boto3_session): db.session.commit() assert project.busy - time.sleep(1) # tests are too fast - # attempt to extend deadline response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, @@ -1266,6 +1200,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session): def test_extend_deadline_project_not_available(module_client, boto3_session): """Is not possible to extend deadline to project in other status than available.""" + # create a new project and never release it current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() if current_unit_admins < 3: create_unit_admins(num_admins=2) @@ -1298,30 +1233,9 @@ def test_extend_deadline_project_not_available(module_client, boto3_session): def test_extend_deadline_no_deadline(module_client, boto3_session): """If no deadline has been provided it should fail""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, - ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project - release_data = {"new_status": "Available"} - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_data, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1339,31 +1253,11 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): def test_extend_deadline_not_enough_time_left(module_client, boto3_session): """If there are more than 10 days left, extend deadline should not be possible""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, + current_deadline = 90 + release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline} + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_data_long_deadline ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project - current_deadline = 30 - release_data = {"new_status": "Available", "deadline": current_deadline} - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_data, - ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1384,29 +1278,9 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session): def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, - ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project with a small deadline - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_project_small_deadline, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1426,34 +1300,14 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, - ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project with a small deadline - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_project_small_deadline, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast deadline = project.current_deadline - new_deadline_in = 1 + new_deadline_in = 1 # small new deadline for i in range(1, 4): time.sleep(1) # tests are too fast @@ -1482,33 +1336,15 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se def test_extend_deadline_ok(module_client, boto3_session): """Extend a project deadline of a project already release""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, + # release with a small deadline + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project with a small deadline - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_project_small_deadline, - ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast deadline = project.current_deadline + # extend deadline response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, @@ -1529,29 +1365,9 @@ def test_extend_deadline_ok(module_client, boto3_session): def test_extend_deadline_mock_database_error(module_client, boto3_session): """Mock error when updating the database""" - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - if current_unit_admins < 3: - create_unit_admins(num_admins=2) - current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() - assert current_unit_admins >= 3 - - response = module_client.post( - tests.DDSEndpoint.PROJECT_CREATE, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client), - json=proj_data, - ) - assert response.status_code == http.HTTPStatus.OK - project_id = response.json.get("project_id") - project = project_row(project_id=project_id) - - # Release project with a small deadline - response = module_client.post( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json=release_project_small_deadline, + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) - assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 time.sleep(1) # tests are too fast From 0ad3d5da9e59b09e2af5f08f68137cacd8903365 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Wed, 11 Oct 2023 14:25:55 +0200 Subject: [PATCH 09/53] better comments --- tests/api/test_project.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 5700e16ce..59146f695 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1130,7 +1130,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): assert project.times_expired == 0 time.sleep(1) # tests are too fast - # extend deadline + # try to extend deadline with a string as confirmed - should fail response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), @@ -1142,7 +1142,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): def test_extend_deadline_no_confirmed(module_client, boto3_session): - """Try to extend a deadline before confirmation should sent a warning and no operation perfrom""" + """Try to extend a deadline before confirmation - should sent a warning and no operation is perfrom""" project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} @@ -1150,14 +1150,14 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): assert project.times_expired == 0 time.sleep(1) # tests are too fast - # extend deadline + # try to extend deadline response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, json=extend_deadline_data_no_confirmed, ) - # status code is ok but no operation perfrom + # status code is ok but no operation perform assert response.status_code == http.HTTPStatus.OK assert project.times_expired == 0 @@ -1198,7 +1198,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session): def test_extend_deadline_project_not_available(module_client, boto3_session): - """Is not possible to extend deadline to project in other status than available.""" + """Is not possible to extend deadline to a project in another status than available.""" # create a new project and never release it current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count() @@ -1239,7 +1239,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): assert project.times_expired == 0 time.sleep(1) # tests are too fast - # try to extend deadline + # try to extend deadline - no new deadline provided response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), @@ -1251,8 +1251,9 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): def test_extend_deadline_not_enough_time_left(module_client, boto3_session): - """If there are more than 10 days left, extend deadline should not be possible""" + """If there are still too much days left, extend deadline should not be yet possible""" + # create and release a new project with a long time left as available current_deadline = 90 release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline} project_id, project = create_and_release_project( @@ -1278,13 +1279,14 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session): def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" + # create project with small deadline so it can be extended project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) assert project.times_expired == 0 time.sleep(1) # tests are too fast - # try to extend deadline a lot of days + # try to extend deadline by a lot of days response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), @@ -1300,18 +1302,18 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" + # create project with small deadline so it can be extended project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) assert project.times_expired == 0 - time.sleep(1) # tests are too fast - - deadline = project.current_deadline + deadline = project.current_deadline # current deadline new_deadline_in = 1 # small new deadline + for i in range(1, 4): time.sleep(1) # tests are too fast - # extend deadline with a small new deadline so we can do it several times + # extend deadline by a small new deadline so we can do it several times response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), @@ -1322,7 +1324,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se assert response.status_code == http.HTTPStatus.OK assert project.times_expired == i assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in) - deadline = project.current_deadline + deadline = project.current_deadline # update current deadline assert f"{project_id} has been given a new deadline" in response.json["message"] assert "An e-mail notification has not been sent." in response.json["message"] else: @@ -1334,16 +1336,16 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se def test_extend_deadline_ok(module_client, boto3_session): - """Extend a project deadline of a project already release""" + """Extend a project deadline of a project - it should work ok""" - # release with a small deadline + # release with a small deadline so it can be extended project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) assert project.times_expired == 0 time.sleep(1) # tests are too fast - deadline = project.current_deadline + deadline = project.current_deadline # save current deadline # extend deadline response = module_client.patch( @@ -1363,7 +1365,7 @@ def test_extend_deadline_ok(module_client, boto3_session): def test_extend_deadline_mock_database_error(module_client, boto3_session): - """Mock error when updating the database""" + """Mock error when performing the request""" project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline From c7976cb12187b569a81ebd31f93d0658baca1904 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Thu, 12 Oct 2023 11:56:23 +0200 Subject: [PATCH 10/53] more coverage --- dds_web/api/project.py | 17 +---------------- tests/api/test_project.py | 8 +++++++- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index a0e24e17f..c6eea1d41 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -252,11 +252,6 @@ def patch(self): message="The new deadline needs to be less than (or equal to) 90 days." ) - if project.times_expired > 2: - raise DDSArgumentError( - "Project availability limit: Project cannot be made Available any more times." - ) - try: # add a fake archived status to mimick a re-release in order to have an udpated deadline new_status_row = self.expire_project( @@ -280,17 +275,7 @@ def patch(self): except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err: flask.current_app.logger.exception(err) db.session.rollback() - raise DatabaseError( - message=str(err), - alt_message=( - "Status was not updated" - + ( - ": Database malfunction." - if isinstance(err, sqlalchemy.exc.OperationalError) - else ": Server Error." - ) - ), - ) from err + raise err return_message = f"{project.public_id} has been given a new deadline" diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 59146f695..ed79d7707 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -4,6 +4,7 @@ import http from sqlite3 import OperationalError import pytest +from _pytest.logging import LogCaptureFixture import datetime import time import unittest.mock @@ -1364,7 +1365,9 @@ def test_extend_deadline_ok(module_client, boto3_session): assert "An e-mail notification has not been sent." in response.json["message"] -def test_extend_deadline_mock_database_error(module_client, boto3_session): +def test_extend_deadline_mock_database_error( + module_client, boto3_session, capfd: LogCaptureFixture +): """Mock error when performing the request""" project_id, project = create_and_release_project( @@ -1385,6 +1388,9 @@ def test_extend_deadline_mock_database_error(module_client, boto3_session): assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR assert "Saving database changes failed." in response.json["message"] + _, err = capfd.readouterr() + assert "500 Internal Server Error: Saving database changes failed." in err + def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session): """Mock the different expections that can occur when deleting project.""" From 19ec5e22ea07d8a84168be7ac5fcd8604bcf80e9 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Thu, 12 Oct 2023 14:34:25 +0200 Subject: [PATCH 11/53] coverage --- dds_web/api/project.py | 2 +- tests/api/test_project.py | 38 +++++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index c6eea1d41..96856b988 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -273,7 +273,7 @@ def patch(self): db.session.commit() except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err: - flask.current_app.logger.exception(err) + flask.current_app.logger.exception("Failed to extend deadline") db.session.rollback() raise err diff --git a/tests/api/test_project.py b/tests/api/test_project.py index ed79d7707..f4c8b3518 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -5,6 +5,7 @@ from sqlite3 import OperationalError import pytest from _pytest.logging import LogCaptureFixture +import logging import datetime import time import unittest.mock @@ -1368,7 +1369,7 @@ def test_extend_deadline_ok(module_client, boto3_session): def test_extend_deadline_mock_database_error( module_client, boto3_session, capfd: LogCaptureFixture ): - """Mock error when performing the request""" + """Operation fails when trying to save in the Database""" project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline @@ -1377,19 +1378,30 @@ def test_extend_deadline_mock_database_error( time.sleep(1) # tests are too fast token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client) - with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror): - # extend deadline - response = module_client.patch( - tests.DDSEndpoint.PROJECT_STATUS, - headers=token, - query_string={"project": project_id}, - json=extend_deadline_data, - ) - assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR - assert "Saving database changes failed." in response.json["message"] - _, err = capfd.readouterr() - assert "500 Internal Server Error: Saving database changes failed." in err + with unittest.mock.patch.object(db.session, "rollback") as rollback: + with unittest.mock.patch("dds_web.db.session.commit") as mock_commit: + # we need this because the first time the commit function is called is when set_busy() + def side_effect_generator(): + yield None # First call, no exception + while True: + yield sqlalchemy.exc.SQLAlchemyError() # Subsequent calls, exception + + mock_commit.side_effect = side_effect_generator() + + # extend deadline + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=token, + query_string={"project": project_id}, + json=extend_deadline_data, + ) + assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR + assert "Saving database changes failed." in response.json["message"] + + assert rollback.called + _, err = capfd.readouterr() + assert "Failed to extend deadline" in err def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session): From fa4c46c63ce14aff39e24d419bfed5d3f0011b27 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 09:23:35 +0200 Subject: [PATCH 12/53] sprintlog --- SPRINTLOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index 8001f5ca5..b6c399177 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -314,3 +314,4 @@ _Nothing merged in CLI during this sprint_ - Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477)) - Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478) - Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479) +- Added new API endpoint ProjectStatus.patch to extend the deadline ([#1480])(https://github.com/ScilifelabDataCentre/dds_web/pull/1480) From 261fbcfc5e48deb6ea64d9b923e6271e05e847e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:27:11 +0200 Subject: [PATCH 13/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 96856b988..c2a3af5af 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -233,7 +233,7 @@ def patch(self): # deadline can only be extended from Available if not project.current_status == "Available": raise DDSArgumentError( - "you can only extend the deadline for a project that has the status Available." + "You can only extend the deadline for a project that has the status 'Available'." ) new_deadline_in = json_input.get("new_deadline_in") From 0b531b57896fe4498649276d7bd1925e4a37a714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:27:22 +0200 Subject: [PATCH 14/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index c2a3af5af..6ed141edb 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -243,10 +243,6 @@ def patch(self): raise DDSArgumentError( message="No new deadline provived, cannot perform operation." ) - if current_deadline > 10: - raise DDSArgumentError( - message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet." - ) if new_deadline_in + current_deadline > 90: raise DDSArgumentError( message="The new deadline needs to be less than (or equal to) 90 days." From 5cca377ce59a17a14736d60cf9536eeca7004520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:27:34 +0200 Subject: [PATCH 15/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 6ed141edb..196ceebff 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -271,7 +271,7 @@ def patch(self): except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err: flask.current_app.logger.exception("Failed to extend deadline") db.session.rollback() - raise err + raise return_message = f"{project.public_id} has been given a new deadline" From a8efc2e19b966cf26cf8203ca0248f44ef3979d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:27:45 +0200 Subject: [PATCH 16/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 196ceebff..4b4c5e23c 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -199,7 +199,7 @@ def patch(self): project = dds_web.utils.collect_project(project_id=project_id) dds_web.utils.verify_project_access(project=project) - # get atributes + # Get json input from request json_input = flask.request.get_json(silent=True) # Already checked by json_required # false by default - operation must be confirmed by the user From 7cb1d43ef175ffe40608f28b09f2675afeceacc6 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 09:28:37 +0200 Subject: [PATCH 17/53] modified test according to comments --- tests/api/test_project.py | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index f4c8b3518..c87e8cc4e 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1227,7 +1227,7 @@ def test_extend_deadline_project_not_available(module_client, boto3_session): assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( - "you can only extend the deadline for a project that has the status Available." + "You can only extend the deadline for a project that has the status 'Available'." in response.json["message"] ) @@ -1251,33 +1251,6 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): assert response.status_code == http.HTTPStatus.BAD_REQUEST assert "No new deadline provived, cannot perform operation." in response.json["message"] - -def test_extend_deadline_not_enough_time_left(module_client, boto3_session): - """If there are still too much days left, extend deadline should not be yet possible""" - - # create and release a new project with a long time left as available - current_deadline = 90 - release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline} - project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data=release_data_long_deadline - ) - assert project.times_expired == 0 - time.sleep(1) # tests are too fast - - # try to extend upon such deadline - response = module_client.patch( - tests.DDSEndpoint.PROJECT_STATUS, - headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), - query_string={"project": project_id}, - json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True}, - ) - assert response.status_code == http.HTTPStatus.BAD_REQUEST - assert ( - f"There are still {current_deadline} days left, it is not possible to extend deadline yet." - in response.json["message"] - ) - - def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" From a3939148d5f4f815c06aacbed59e54cca970b4bc Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 09:28:56 +0200 Subject: [PATCH 18/53] modified test according to comments --- tests/api/test_project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index c87e8cc4e..d437dd592 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1251,6 +1251,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): assert response.status_code == http.HTTPStatus.BAD_REQUEST assert "No new deadline provived, cannot perform operation." in response.json["message"] + def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" From 125e6651b77f581fc539c51f296cc09d20aac39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:29:19 +0200 Subject: [PATCH 19/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 4b4c5e23c..89ddffd3a 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -202,7 +202,7 @@ def patch(self): # Get json input from request json_input = flask.request.get_json(silent=True) # Already checked by json_required - # false by default - operation must be confirmed by the user + # Operation must be confirmed by the user - False by default confirmed_operation = json_input.get("confirmed", False) if not isinstance(confirmed_operation, bool): raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.") From 96e736826f8bfc153c71518e645357664c4e3133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 11:11:03 +0200 Subject: [PATCH 20/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 89ddffd3a..b7f06a443 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -221,6 +221,7 @@ def patch(self): self.set_busy(project=project, busy=True) + # Extend deadline try: extend_deadline = json_input.get("extend_deadline", False) # False by default From 55d40a9f4395aaf69128e8a30ff672b97fe6b79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 13 Oct 2023 11:11:39 +0200 Subject: [PATCH 21/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index b7f06a443..cefdcdd5c 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -246,7 +246,7 @@ def patch(self): ) if new_deadline_in + current_deadline > 90: raise DDSArgumentError( - message="The new deadline needs to be less than (or equal to) 90 days." + message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days." ) try: From 4f26b2dc005015905bbd7a9f2e3adfb738487acd Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 11:45:34 +0200 Subject: [PATCH 22/53] modified acording to comments --- dds_web/api/project.py | 24 ++++++++------- tests/api/test_project.py | 63 +++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index cefdcdd5c..a16f6a726 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -223,32 +223,33 @@ def patch(self): # Extend deadline try: - extend_deadline = json_input.get("extend_deadline", False) # False by default + new_deadline_in = json_input.get( + "new_deadline_in", None + ) # if not provided is None -> deadlie is not updated # some variable definition curr_date = dds_web.utils.current_time() send_email = False - # Update the deadline - if extend_deadline: + # Update the deadline functionality + if new_deadline_in: # deadline can only be extended from Available if not project.current_status == "Available": raise DDSArgumentError( "You can only extend the deadline for a project that has the status 'Available'." ) - new_deadline_in = json_input.get("new_deadline_in") + # it shouldnt surpass 90 days current_deadline = (project.current_deadline - curr_date).days - - if not new_deadline_in: - raise DDSArgumentError( - message="No new deadline provived, cannot perform operation." - ) if new_deadline_in + current_deadline > 90: raise DDSArgumentError( message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days." ) + if type(new_deadline_in) is not int: + raise DDSArgumentError( + message=" The deadline atribute passed should be of type Int (i.e a number)." + ) try: # add a fake archived status to mimick a re-release in order to have an udpated deadline new_status_row = self.expire_project( @@ -257,7 +258,6 @@ def patch(self): deadline_in=project.responsible_unit.days_in_expired, ) project.project_statuses.append(new_status_row) - db.session.commit() new_status_row = self.release_project( project=project, @@ -279,7 +279,9 @@ def patch(self): return_message += ( f". An e-mail notification has{' not ' if not send_email else ' '}been sent." ) - + else: + # leave it for future new functionality of updating the status + return_message = "Nothing to update." except: self.set_busy(project=project, busy=False) raise diff --git a/tests/api/test_project.py b/tests/api/test_project.py index d437dd592..a44b4cedd 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -49,7 +49,6 @@ release_project_small_deadline = {"new_status": "Available", "deadline": 5} extend_deadline_data_no_confirmed = { - "extend_deadline": True, "new_deadline_in": 20, } @@ -1126,6 +1125,7 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session def test_extend_deadline_bad_confirmed(module_client, boto3_session): """Try to extend a deadline and send a not boolean for confirmation""" + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) @@ -1146,6 +1146,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): def test_extend_deadline_no_confirmed(module_client, boto3_session): """Try to extend a deadline before confirmation - should sent a warning and no operation is perfrom""" + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) @@ -1169,6 +1170,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): def test_extend_deadline_when_busy(module_client, boto3_session): """Request should not be possible when project is busy.""" + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} ) @@ -1199,6 +1201,28 @@ def test_extend_deadline_when_busy(module_client, boto3_session): ) +def test_extend_deadline_no_deadline(module_client, boto3_session): + """If no deadline has been provided it should not be executed anything""" + + # create project and release it + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + ) + assert project.times_expired == 0 + time.sleep(1) # tests are too fast + + # try to extend deadline - no new deadline provided + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json={"confirmed": True}, + ) + assert response.status_code == http.HTTPStatus.OK + assert project.times_expired == 0 + assert "Nothing to update." in response.json["message"] + + def test_extend_deadline_project_not_available(module_client, boto3_session): """Is not possible to extend deadline to a project in another status than available.""" @@ -1232,42 +1256,47 @@ def test_extend_deadline_project_not_available(module_client, boto3_session): ) -def test_extend_deadline_no_deadline(module_client, boto3_session): - """If no deadline has been provided it should fail""" +def test_extend_deadline_too_much_days(module_client, boto3_session): + """If the new deadline together with the time left already is more than 90 days it should not work""" + # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) assert project.times_expired == 0 time.sleep(1) # tests are too fast - # try to extend deadline - no new deadline provided + # try to extend deadline by a lot of days + extend_deadline_data = {**extend_deadline_data, "new_deadline_in": 90} response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"extend_deadline": True, "confirmed": True}, + json=extend_deadline_data, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST - assert "No new deadline provived, cannot perform operation." in response.json["message"] + assert ( + "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"] + ) -def test_extend_deadline_too_much_days(module_client, boto3_session): - """If the new deadline together with the time left already is more than 90 days it should not work""" +def test_extend_deadline_bad_new_deadline(module_client, boto3_session): + """If the new deadlien provided is not an integer it should fail""" - # create project with small deadline so it can be extended + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) assert project.times_expired == 0 time.sleep(1) # tests are too fast - # try to extend deadline by a lot of days + # try to extend deadline with a bad new deadline + extend_deadline_data = {**extend_deadline_data, "new_deadline_in": "20"} response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={"extend_deadline": True, "new_deadline_in": 90, "confirmed": True}, + json=extend_deadline_data, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( @@ -1278,7 +1307,7 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" - # create project with small deadline so it can be extended + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) @@ -1290,11 +1319,15 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se time.sleep(1) # tests are too fast # extend deadline by a small new deadline so we can do it several times + extend_deadline_data_small_deadline = { + **extend_deadline_data, + "new_deadline_in": new_deadline_in, + } response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json={**extend_deadline_data, "new_deadline_in": new_deadline_in}, + json=extend_deadline_data_small_deadline, ) if i < 3: assert response.status_code == http.HTTPStatus.OK @@ -1314,7 +1347,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se def test_extend_deadline_ok(module_client, boto3_session): """Extend a project deadline of a project - it should work ok""" - # release with a small deadline so it can be extended + # create project and release it project_id, project = create_and_release_project( client=module_client, proj_data=proj_data, release_data=release_project_small_deadline ) From 4141b12c0178b2cb04e6de2b925223593ead4d77 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 11:52:46 +0200 Subject: [PATCH 23/53] small fix --- dds_web/api/project.py | 9 +++++---- tests/api/test_project.py | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index a16f6a726..e99f090da 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -239,6 +239,11 @@ def patch(self): "You can only extend the deadline for a project that has the status 'Available'." ) + if type(new_deadline_in) is not int: + raise DDSArgumentError( + message="The deadline atribute passed should be of type Int (i.e a number)." + ) + # it shouldnt surpass 90 days current_deadline = (project.current_deadline - curr_date).days if new_deadline_in + current_deadline > 90: @@ -246,10 +251,6 @@ def patch(self): message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days." ) - if type(new_deadline_in) is not int: - raise DDSArgumentError( - message=" The deadline atribute passed should be of type Int (i.e a number)." - ) try: # add a fake archived status to mimick a re-release in order to have an udpated deadline new_status_row = self.expire_project( diff --git a/tests/api/test_project.py b/tests/api/test_project.py index a44b4cedd..9cdfddb00 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1267,12 +1267,12 @@ def test_extend_deadline_too_much_days(module_client, boto3_session): time.sleep(1) # tests are too fast # try to extend deadline by a lot of days - extend_deadline_data = {**extend_deadline_data, "new_deadline_in": 90} + extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 90} response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json=extend_deadline_data, + json=extend_deadline_data_big_deadline, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( @@ -1291,16 +1291,17 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session): time.sleep(1) # tests are too fast # try to extend deadline with a bad new deadline - extend_deadline_data = {**extend_deadline_data, "new_deadline_in": "20"} + extend_deadline_data_bad_deadline = {**extend_deadline_data, "new_deadline_in": "20"} response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), query_string={"project": project_id}, - json=extend_deadline_data, + json=extend_deadline_data_bad_deadline, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( - "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"] + "The deadline atribute passed should be of type Int (i.e a number)." + in response.json["message"] ) From 1d0478cadcf47cd623b6bf1e7d21acb95a2f03c7 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 13 Oct 2023 11:53:10 +0200 Subject: [PATCH 24/53] black --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index e99f090da..977fad7ab 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -243,7 +243,7 @@ def patch(self): raise DDSArgumentError( message="The deadline atribute passed should be of type Int (i.e a number)." ) - + # it shouldnt surpass 90 days current_deadline = (project.current_deadline - curr_date).days if new_deadline_in + current_deadline > 90: From 0f0bb1f9d6b7dbb54cde4691c06a5b591be847b4 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 16 Oct 2023 15:16:05 +0200 Subject: [PATCH 25/53] unconfirmed returns project info --- dds_web/api/project.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 977fad7ab..b40b7df01 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -208,7 +208,15 @@ def patch(self): raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.") if not confirmed_operation: warning_message = "Operation must be confirmed before proceding." - return {"warning": warning_message} + project_info = ProjectInfo().get() + project_status = self.get() + json_returned = { + **project_info, + "project_status": project_status, + "warning": warning_message, + "default_unit_days": project.responsible_unit.days_in_expired, + } + return json_returned # Cannot change project status if project is busy if project.busy: From 0cab328da2c844cf18419a7502b28b58c5c18297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?= Date: Tue, 17 Oct 2023 11:23:37 +0200 Subject: [PATCH 26/53] changelog --- CHANGELOG.rst | 8 ++++++++ SPRINTLOG.md | 4 ++-- doc/procedures/new_release.md | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d9b86cf5f..69752ed7b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Changelog ========== +.. _2.5.2: + +2.5.2 - 2023-10-25 +~~~~~~~~~~~~~~~~~~~~~ + +- Users can revoke project access given to unaccepted invites (e.g. after a mistake). +- Email layout changed. When project is released, important information is now highlighted, and the Project Title is displayed along with the DDS project ID. + .. _2.5.1: 2.5.1 - 2023-09-27 diff --git a/SPRINTLOG.md b/SPRINTLOG.md index 8001f5ca5..cf0c7e19e 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -300,13 +300,13 @@ _Nothing merged in CLI during this sprint_ - Dependency: Bump `MariaDB` to LTS version 10.11.5 ([#1465](https://github.com/ScilifelabDataCentre/dds_web/pull/1465)) - Bug fixed: Row in `ProjectUsers` should also be added if it doesn't exist when giving Researcher access to a specific project ([#1464](https://github.com/ScilifelabDataCentre/dds_web/pull/1464)) - Workflow: Update PR template and clarify sections ([#1467](https://github.com/ScilifelabDataCentre/dds_web/pull/1467)) -- Revoke project access for unaccepted invites ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192) # 2023-09-18 - 2023-09-29 -- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1668])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1668) +- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1469](https://github.com/ScilifelabDataCentre/dds_web/pull/1469)) - Replace expired invites when there's a new invitation attempt ([#1466](https://github.com/ScilifelabDataCentre/dds_web/pull/1466)) - New version: 2.5.1 ([#1471](https://github.com/ScilifelabDataCentre/dds_web/pull/1471)) +- Revoke project access for unaccepted invites ([#1468](https://github.com/ScilifelabDataCentre/dds_web/pull/1468)) # 2023-10-02 - 2023-10-13 diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md index cdb83dc91..8fc7b1387 100644 --- a/doc/procedures/new_release.md +++ b/doc/procedures/new_release.md @@ -1,14 +1,14 @@ # How to create a new release 1. Create a PR from `dev` to `master`: "New release" -2. Confirm that the development instance works and that the newest changes have been deployed +2. Confirm that the development instance works and that the newest changes have been deployed. If not, make a new redeployment of dds-dev (via argocd). 1. _In general_, e.g. that it's up and running 2. _Specific feature has been added or changed:_ Confirm that it also works in the development instance 3. _The change is in the API:_ Confirm that the development instance works together with the CLI -3. Fork a new branch from `dev` -4. Update the version [changelog](../../CHANGELOG.rst) +3. Fork a new branch from `dev` (locally) +4. Update the version [changelog](../../CHANGELOG.rst), located at `dds_web/CHANGELOG.rst` **Tip:** Use the PR to `master` to see all changes since last release. From ee23e090d8f83fad63ed96b2740f3adc9d81a61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?= Date: Tue, 17 Oct 2023 11:27:44 +0200 Subject: [PATCH 27/53] update release info --- doc/procedures/new_release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md index 8fc7b1387..9a62b54d4 100644 --- a/doc/procedures/new_release.md +++ b/doc/procedures/new_release.md @@ -20,7 +20,7 @@ - _Minor changes, e.g. bug fix_: Minor version upgrade, e.g. `1.0.1 --> 1.0.2` - _Small changes, e.g. new feature_: Mid version upgrade, e.g. `1.1.0 --> 1.2.0` - - _Breaking changes or large new feature(s)_: Major version upgrade, e.g. `1.0.0 --> 2.0.0` + - _Breaking changes or large new feature(s)_: Major version upgrade, e.g. `1.0.0 --> 2.0.0` _AVOID THIS -- NEED TO INFORM USERS WELL IN ADVANCE IN THAT CASE SINCE IT WILL BLOCK THE USERS FROM USING ANY OLDER VERSIONS_ > Will break if CLI version not bumped as well From 40696ed1a74bd8a38f40e2f107b57fc1b567fb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?= Date: Tue, 17 Oct 2023 11:31:33 +0200 Subject: [PATCH 28/53] version --- dds_web/version.py | 2 +- tests/test_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dds_web/version.py b/dds_web/version.py index 7a2056f56..667b52f95 100644 --- a/dds_web/version.py +++ b/dds_web/version.py @@ -1 +1 @@ -__version__ = "2.5.1" +__version__ = "2.5.2" diff --git a/tests/test_version.py b/tests/test_version.py index 02a5bc16a..534eab711 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -2,4 +2,4 @@ def test_version(): - assert version.__version__ == "2.5.1" + assert version.__version__ == "2.5.2" From e042eca19cf92ca8662c620a10b88052fbe542d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?= Date: Tue, 17 Oct 2023 11:34:23 +0200 Subject: [PATCH 29/53] sprintlog --- SPRINTLOG.md | 1 + doc/procedures/new_release.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index cf0c7e19e..a5afa6219 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -314,3 +314,4 @@ _Nothing merged in CLI during this sprint_ - Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477)) - Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478) - Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479) +- New version: 2.5.2 ([#1482](https://github.com/ScilifelabDataCentre/dds_web/pull/1482)) diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md index 9a62b54d4..82bd4fbe4 100644 --- a/doc/procedures/new_release.md +++ b/doc/procedures/new_release.md @@ -25,7 +25,7 @@ > Will break if CLI version not bumped as well 6. Push version change to branch -7. Create a new PR from `` to `dev` +7. Create a new PR from `` to `dev`: "New version & changelog" Wait for approval and merge by Product Owner or admin. From 5df20c92a9d25362b317ac900950a62cc870fa4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:42:59 +0200 Subject: [PATCH 30/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index b40b7df01..ff086f0ff 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -233,7 +233,7 @@ def patch(self): try: new_deadline_in = json_input.get( "new_deadline_in", None - ) # if not provided is None -> deadlie is not updated + ) # if not provided --> is None -> deadline is not updated # some variable definition curr_date = dds_web.utils.current_time() From 69693d5c9974e197c594642579ed91374be96f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:43:24 +0200 Subject: [PATCH 31/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index ff086f0ff..044803b72 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -192,6 +192,7 @@ def post(self): @logging_bind_request @json_required @handle_validation_errors + @handle_db_error def patch(self): """Partially update a the project status""" # Get project ID, project and verify access From 470b3a126fb2e3ced68ca6476b44a6b9e5aa5b02 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:11:52 +0200 Subject: [PATCH 32/53] moved set busy --- dds_web/api/project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 044803b72..873c5e661 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -228,10 +228,10 @@ def patch(self): ) ) - self.set_busy(project=project, busy=True) - # Extend deadline try: + self.set_busy(project=project, busy=True) + new_deadline_in = json_input.get( "new_deadline_in", None ) # if not provided --> is None -> deadline is not updated From 29d8eda1b2cf6dd406ebb7936e64a0b5b3bbb62b Mon Sep 17 00:00:00 2001 From: rv0lt Date: Thu, 19 Oct 2023 16:07:40 +0200 Subject: [PATCH 33/53] rollout --- dds_web/api/project.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 873c5e661..af42d42fd 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -209,6 +209,7 @@ def patch(self): raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.") if not confirmed_operation: warning_message = "Operation must be confirmed before proceding." + # When not confirmed, return information about the project project_info = ProjectInfo().get() project_status = self.get() json_returned = { @@ -228,10 +229,10 @@ def patch(self): ) ) + self.set_busy(project=project, busy=True) + # Extend deadline try: - self.set_busy(project=project, busy=True) - new_deadline_in = json_input.get( "new_deadline_in", None ) # if not provided --> is None -> deadline is not updated From 4f4ff9155e71cf84dc7e9bc728242b5993cc819e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 13:40:37 +0200 Subject: [PATCH 34/53] Update SPRINTLOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- SPRINTLOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index b6c399177..065b8b294 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -314,4 +314,7 @@ _Nothing merged in CLI during this sprint_ - Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477)) - Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478) - Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479) + +# 2023-10-16 - 2023-10-27 + - Added new API endpoint ProjectStatus.patch to extend the deadline ([#1480])(https://github.com/ScilifelabDataCentre/dds_web/pull/1480) From ca9709459b3c304c0daaddb2498dc072b52c17e0 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 10:42:48 +0200 Subject: [PATCH 35/53] new exceptions --- dds_web/api/project.py | 9 ++++++- tests/api/test_project.py | 52 +++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index af42d42fd..423e9453b 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -240,6 +240,7 @@ def patch(self): # some variable definition curr_date = dds_web.utils.current_time() send_email = False + default_unit_days = project.responsible_unit.days_in_expired # Update the deadline functionality if new_deadline_in: @@ -254,7 +255,13 @@ def patch(self): message="The deadline atribute passed should be of type Int (i.e a number)." ) - # it shouldnt surpass 90 days + # New deadline shouldnt surpass the default unit days + if new_deadline_in > default_unit_days: + raise DDSArgumentError( + message=f"You requested the deadline to be extended {new_deadline_in}. The number of days has to be lower than the default deadline extension number of {default_unit_days} days" + ) + + # the new deadline + days left shouldnt surpass 90 days current_deadline = (project.current_deadline - curr_date).days if new_deadline_in + current_deadline > 90: raise DDSArgumentError( diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 9cdfddb00..3bfb18519 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -46,7 +46,9 @@ # "date_updated", ] -release_project_small_deadline = {"new_status": "Available", "deadline": 5} +release_project = {"new_status": "Available"} +release_project_small_deadline = {**release_project, "deadline": 5} +release_project_big_deadline = {**release_project, "deadline": 80} extend_deadline_data_no_confirmed = { "new_deadline_in": 20, @@ -1127,7 +1129,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session): # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + client=module_client, proj_data=proj_data, release_data=release_project ) assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1148,7 +1150,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + client=module_client, proj_data=proj_data, release_data=release_project ) assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1172,7 +1174,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session): # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + client=module_client, proj_data=proj_data, release_data=release_project ) assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1206,7 +1208,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session): # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data={"new_status": "Available"} + client=module_client, proj_data=proj_data, release_data=release_project ) assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1259,15 +1261,15 @@ def test_extend_deadline_project_not_available(module_client, boto3_session): def test_extend_deadline_too_much_days(module_client, boto3_session): """If the new deadline together with the time left already is more than 90 days it should not work""" - # create project and release it + # create project and release it with big dealdine project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data=release_project_small_deadline + client=module_client, proj_data=proj_data, release_data=release_project_big_deadline ) assert project.times_expired == 0 time.sleep(1) # tests are too fast - # try to extend deadline by a lot of days - extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 90} + # try to extend deadline -> 80 + 11 > 90 + extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 11} response = module_client.patch( tests.DDSEndpoint.PROJECT_STATUS, headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), @@ -1285,7 +1287,7 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session): # create project and release it project_id, project = create_and_release_project( - client=module_client, proj_data=proj_data, release_data=release_project_small_deadline + client=module_client, proj_data=proj_data, release_data=release_project ) assert project.times_expired == 0 time.sleep(1) # tests are too fast @@ -1305,6 +1307,36 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session): ) +def test_extend_deadline_more_than_default(module_client, boto3_session): + """If the new deadline provided is more than the default unit days to release a project it should fail""" + + # create project and release it + project_id, project = create_and_release_project( + client=module_client, proj_data=proj_data, release_data=release_project_small_deadline + ) + assert project.times_expired == 0 + time.sleep(1) # tests are too fast + + default_unit_days = project.responsible_unit.days_in_expired + + # try to extend deadline with a bigger deadline that it is suppose to have + extend_deadline_data_bad_deadline = { + **extend_deadline_data, + "new_deadline_in": default_unit_days + 1, + } + response = module_client.patch( + tests.DDSEndpoint.PROJECT_STATUS, + headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client), + query_string={"project": project_id}, + json=extend_deadline_data_bad_deadline, + ) + assert response.status_code == http.HTTPStatus.BAD_REQUEST + assert ( + "The number of days has to be lower than the default deadline extension number" + in response.json["message"] + ) + + def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session): """If the deadline has been extended more than 2 times it should not work""" From 7cc7b17ee948fc28f023049c58a20b164918ab7d Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 10:59:02 +0200 Subject: [PATCH 36/53] new exceptions --- dds_web/api/project.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 423e9453b..8967ee650 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -216,7 +216,6 @@ def patch(self): **project_info, "project_status": project_status, "warning": warning_message, - "default_unit_days": project.responsible_unit.days_in_expired, } return json_returned From 806e12cd6a87bfec3540f6600fd5298c69a42874 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 11:20:20 +0200 Subject: [PATCH 37/53] default unti days --- dds_web/api/project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 8967ee650..423e9453b 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -216,6 +216,7 @@ def patch(self): **project_info, "project_status": project_status, "warning": warning_message, + "default_unit_days": project.responsible_unit.days_in_expired, } return json_returned From 181ebbc9c7cbce15bec5b48cc81a17193d662911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:51:30 +0200 Subject: [PATCH 38/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 423e9453b..7f601bc6a 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -252,7 +252,7 @@ def patch(self): if type(new_deadline_in) is not int: raise DDSArgumentError( - message="The deadline atribute passed should be of type Int (i.e a number)." + message="The deadline attribute passed should be of type Int (i.e a number)." ) # New deadline shouldnt surpass the default unit days From 681b4af920349c45acd412cd238477e08afaf205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:51:40 +0200 Subject: [PATCH 39/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 7f601bc6a..7132ccd2d 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -225,7 +225,7 @@ def patch(self): raise ProjectBusyError( message=( f"The deadline for the project '{project_id}' is already in the process of being changed. " - "Please try again later. \n\nIf you know the project is not busy, contact support." + "Please try again later. \n\nIf you know that the project is not busy, contact support." ) ) From de4b7c505f594a66e4004bf898247b7dd2bca676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:51:57 +0200 Subject: [PATCH 40/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 7132ccd2d..3ff013968 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -292,10 +292,9 @@ def patch(self): db.session.rollback() raise - return_message = f"{project.public_id} has been given a new deadline" - - return_message += ( - f". An e-mail notification has{' not ' if not send_email else ' '}been sent." + return_message = ( + f"{project.public_id} has been given a new deadline. " + f"An e-mail notification has{' not ' if not send_email else ' '}been sent." ) else: # leave it for future new functionality of updating the status From 1cc9c4d6f68b1b16747d9c66f8514c0824b21ecf Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 12:56:31 +0200 Subject: [PATCH 41/53] test to match changes --- tests/api/test_project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 3bfb18519..a1479eebc 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1198,7 +1198,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session): in response.json["message"] ) assert ( - "Please try again later. \n\nIf you know the project is not busy, contact support." + "Please try again later. \n\nIf you know that the project is not busy, contact support." in response.json["message"] ) @@ -1302,7 +1302,7 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session): ) assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( - "The deadline atribute passed should be of type Int (i.e a number)." + "The deadline attribute passed should be of type Int (i.e a number)." in response.json["message"] ) From a4fb0f5dec253244399b38c4a546ad7a516b5b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:49:51 +0200 Subject: [PATCH 42/53] Update tests/api/test_project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- tests/api/test_project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index a1479eebc..944683d1d 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1167,6 +1167,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): assert project.times_expired == 0 assert "Operation must be confirmed before proceding." in response.json["warning"] + assert all(item in response.json for item in ["project_info", "project_status", "warning", "default_unit_days"]) def test_extend_deadline_when_busy(module_client, boto3_session): From 703fa91124249cd9d8cb801f8399a4653852c0d9 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 16:14:23 +0200 Subject: [PATCH 43/53] feedback --- dds_web/api/project.py | 9 +++++++-- tests/api/test_project.py | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 3ff013968..d4cc65c43 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -267,9 +267,14 @@ def patch(self): raise DDSArgumentError( message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days." ) - + # the dealine has changed at least two times, next time it expires + # wont change again -> error + if project.times_expired >= 2: + raise DDSArgumentError( + "Project availability limit: Project cannot be made Available any more times" + ) try: - # add a fake archived status to mimick a re-release in order to have an udpated deadline + # add a fake expire status to mimick a re-release in order to have an udpated deadline new_status_row = self.expire_project( project=project, current_time=curr_date, diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 944683d1d..638bb25d9 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1167,7 +1167,10 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session): assert project.times_expired == 0 assert "Operation must be confirmed before proceding." in response.json["warning"] - assert all(item in response.json for item in ["project_info", "project_status", "warning", "default_unit_days"]) + assert all( + item in response.json + for item in ["project_info", "project_status", "warning", "default_unit_days"] + ) def test_extend_deadline_when_busy(module_client, boto3_session): From b3f478b8759ddd7d2a345f9626e8513fb4466913 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 16:27:04 +0200 Subject: [PATCH 44/53] more explicative error --- dds_web/api/project.py | 2 +- tests/api/test_project.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index d4cc65c43..5bacb7069 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -271,7 +271,7 @@ def patch(self): # wont change again -> error if project.times_expired >= 2: raise DDSArgumentError( - "Project availability limit: Project cannot be made Available any more times" + "Project availability limit: The maximun number of changes in data availability has been reached." ) try: # add a fake expire status to mimick a re-release in order to have an udpated deadline diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 638bb25d9..99531c792 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1376,7 +1376,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se else: assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( - "Project availability limit: Project cannot be made Available any more times" + "Project availability limit: The maximun number of changes in data availability has been reached." in response.json["message"] ) From 75987af295a2d89db6ae15075b3231437467f70b Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 16:40:58 +0200 Subject: [PATCH 45/53] move check for data availability --- dds_web/api/project.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 5bacb7069..cb63a2714 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -203,6 +203,13 @@ def patch(self): # Get json input from request json_input = flask.request.get_json(silent=True) # Already checked by json_required + # the status has changed at least two times, + # next time the project expires it wont change again -> error + if project.times_expired >= 2: + raise DDSArgumentError( + "Project availability limit: The maximun number of changes in data availability has been reached." + ) + # Operation must be confirmed by the user - False by default confirmed_operation = json_input.get("confirmed", False) if not isinstance(confirmed_operation, bool): @@ -267,12 +274,6 @@ def patch(self): raise DDSArgumentError( message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days." ) - # the dealine has changed at least two times, next time it expires - # wont change again -> error - if project.times_expired >= 2: - raise DDSArgumentError( - "Project availability limit: The maximun number of changes in data availability has been reached." - ) try: # add a fake expire status to mimick a re-release in order to have an udpated deadline new_status_row = self.expire_project( From bbffbd59396d56abb9379abfb1f271e61fb81e5b Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 16:43:12 +0200 Subject: [PATCH 46/53] typo --- dds_web/api/project.py | 2 +- tests/api/test_project.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index cb63a2714..a2cbf94ff 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -207,7 +207,7 @@ def patch(self): # next time the project expires it wont change again -> error if project.times_expired >= 2: raise DDSArgumentError( - "Project availability limit: The maximun number of changes in data availability has been reached." + "Project availability limit: The maximum number of changes in data availability has been reached." ) # Operation must be confirmed by the user - False by default diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 99531c792..e7d614607 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1376,7 +1376,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se else: assert response.status_code == http.HTTPStatus.BAD_REQUEST assert ( - "Project availability limit: The maximun number of changes in data availability has been reached." + "Project availability limit: The maximum number of changes in data availability has been reached." in response.json["message"] ) From 118708c10246d0546ab46b56c5d4a1c502140ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:09:30 +0200 Subject: [PATCH 47/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index a2cbf94ff..12e6cd421 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -223,7 +223,7 @@ def patch(self): **project_info, "project_status": project_status, "warning": warning_message, - "default_unit_days": project.responsible_unit.days_in_expired, + "default_unit_days": project.responsible_unit.days_in_available, } return json_returned From a5ab01adfb29b740fd97af36de2d3d6408865eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:05:25 +0200 Subject: [PATCH 48/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 12e6cd421..a568dd8de 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -247,7 +247,7 @@ def patch(self): # some variable definition curr_date = dds_web.utils.current_time() send_email = False - default_unit_days = project.responsible_unit.days_in_expired + default_unit_days = project.responsible_unit.days_in_available # Update the deadline functionality if new_deadline_in: From 78f7418e5845fac95f245d8a4d3a126533de8259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:05:35 +0200 Subject: [PATCH 49/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index a568dd8de..541dda856 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -299,7 +299,7 @@ def patch(self): raise return_message = ( - f"{project.public_id} has been given a new deadline. " + f"The project '{project.public_id}' has been given a new deadline. " f"An e-mail notification has{' not ' if not send_email else ' '}been sent." ) else: From dd50770446a60851e830a2b61412573d03e8f40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:05:48 +0200 Subject: [PATCH 50/53] Update dds_web/api/project.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_web/api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 541dda856..9a016c350 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -265,7 +265,7 @@ def patch(self): # New deadline shouldnt surpass the default unit days if new_deadline_in > default_unit_days: raise DDSArgumentError( - message=f"You requested the deadline to be extended {new_deadline_in}. The number of days has to be lower than the default deadline extension number of {default_unit_days} days" + message=f"You requested the deadline to be extended {new_deadline_in} days. The number of days has to be lower than the default deadline extension number of {default_unit_days} days" ) # the new deadline + days left shouldnt surpass 90 days From 33e78efe8094c0276961e25a3529013a00d45102 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 24 Oct 2023 09:49:53 +0200 Subject: [PATCH 51/53] fix tests --- tests/api/test_project.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/api/test_project.py b/tests/api/test_project.py index e7d614607..6baee43af 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1321,7 +1321,7 @@ def test_extend_deadline_more_than_default(module_client, boto3_session): assert project.times_expired == 0 time.sleep(1) # tests are too fast - default_unit_days = project.responsible_unit.days_in_expired + default_unit_days = project.responsible_unit.days_in_available # try to extend deadline with a bigger deadline that it is suppose to have extend_deadline_data_bad_deadline = { @@ -1371,7 +1371,10 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se assert project.times_expired == i assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in) deadline = project.current_deadline # update current deadline - assert f"{project_id} has been given a new deadline" in response.json["message"] + assert ( + f"The project '{project_id}' has been given a new deadline" + in response.json["message"] + ) assert "An e-mail notification has not been sent." in response.json["message"] else: assert response.status_code == http.HTTPStatus.BAD_REQUEST @@ -1406,7 +1409,7 @@ def test_extend_deadline_ok(module_client, boto3_session): days=extend_deadline_data.get("new_deadline_in") ) - assert f"{project_id} has been given a new deadline" in response.json["message"] + assert f"The project '{project_id}' has been given a new deadline" in response.json["message"] assert "An e-mail notification has not been sent." in response.json["message"] From 494b7bc4afbec6e42b6ffb821856280a27b54b25 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 24 Oct 2023 10:32:48 +0200 Subject: [PATCH 52/53] feedback --- dds_web/api/project.py | 10 ++++++++-- tests/api/test_project.py | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 9a016c350..ffe2c95ef 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -245,7 +245,6 @@ def patch(self): ) # if not provided --> is None -> deadline is not updated # some variable definition - curr_date = dds_web.utils.current_time() send_email = False default_unit_days = project.responsible_unit.days_in_available @@ -269,6 +268,7 @@ def patch(self): ) # the new deadline + days left shouldnt surpass 90 days + curr_date = dds_web.utils.current_time() current_deadline = (project.current_deadline - curr_date).days if new_deadline_in + current_deadline > 90: raise DDSArgumentError( @@ -276,13 +276,19 @@ def patch(self): ) try: # add a fake expire status to mimick a re-release in order to have an udpated deadline + curr_date = ( + dds_web.utils.current_time() + ) # call current_time before each call so it is stored with different timestamps new_status_row = self.expire_project( project=project, current_time=curr_date, - deadline_in=project.responsible_unit.days_in_expired, + deadline_in=1, # some dummy deadline bc it will re-release now again ) project.project_statuses.append(new_status_row) + curr_date = ( + dds_web.utils.current_time() + ) # call current_time before each call so it is stored with different timestamps new_status_row = self.release_project( project=project, current_time=curr_date, diff --git a/tests/api/test_project.py b/tests/api/test_project.py index 6baee43af..18fcfcb10 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -1371,6 +1371,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se assert project.times_expired == i assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in) deadline = project.current_deadline # update current deadline + assert project.current_status == "Available" assert ( f"The project '{project_id}' has been given a new deadline" in response.json["message"] @@ -1408,6 +1409,7 @@ def test_extend_deadline_ok(module_client, boto3_session): assert project.current_deadline == deadline + datetime.timedelta( days=extend_deadline_data.get("new_deadline_in") ) + assert project.current_status == "Available" assert f"The project '{project_id}' has been given a new deadline" in response.json["message"] assert "An e-mail notification has not been sent." in response.json["message"] From 59e44d5cf6f4eea1dc5cec8cec5da94d44549362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?= Date: Tue, 24 Oct 2023 11:44:06 +0200 Subject: [PATCH 53/53] changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 69752ed7b..30c19b0dc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,7 @@ Changelog - Users can revoke project access given to unaccepted invites (e.g. after a mistake). - Email layout changed. When project is released, important information is now highlighted, and the Project Title is displayed along with the DDS project ID. +- New endpoint `ProjectStatus.patch`: Unit Admins / Personnel can extend the project deadline. .. _2.5.1: