From 8c1a8baeced3ec8e430c79f1ec9d18fcbb65e0a4 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Fri, 15 Nov 2024 18:32:45 +0000 Subject: [PATCH 01/23] added create_ts and update_ts to create_comment --- rating_api/routes/comment.py | 33 ++++++++++++++++++++++++--------- rating_api/schemas/models.py | 2 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index c0cbcf4..8f52ba9 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -20,6 +20,7 @@ @comment.post("", response_model=CommentGet) async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depends(UnionAuth())) -> CommentGet: """ + Scopes: `["rating.comment.review"]` Создает комментарий к преподавателю в базе данных RatingAPI Для создания комментария нужно быть авторизованным """ @@ -27,18 +28,32 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) + if comment_info.create_ts or comment_info.update_ts: + if not "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + raise ForbiddenAction(Comment) + else: + if not comment_info.create_ts: + comment_info.create_ts = datetime.datetime.utcnow() + if not comment_info.update_ts: + comment_info.update_ts = datetime.datetime.utcnow() + else: + comment_info.create_ts = datetime.datetime.utcnow() + comment_info.update_ts = datetime.datetime.utcnow() + user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() ) - for user_comment in user_comments: - if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( - minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES - ): - raise TooManyCommentRequests( - dtime=user_comment.update_ts - + datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES) - - datetime.datetime.utcnow() - ) + + if not "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + for user_comment in user_comments: + if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( + minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES + ): + raise TooManyCommentRequests( + dtime=user_comment.update_ts + + datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES) + - datetime.datetime.utcnow() + ) LecturerUserComment.create(session=db.session, lecturer_id=lecturer_id, user_id=user.get('id')) new_comment = Comment.create( diff --git a/rating_api/schemas/models.py b/rating_api/schemas/models.py index de510a8..56deaa7 100644 --- a/rating_api/schemas/models.py +++ b/rating_api/schemas/models.py @@ -23,6 +23,8 @@ class CommentGet(Base): class CommentPost(Base): subject: str text: str + create_ts: datetime.datetime | None = None + update_ts: datetime.datetime | None = None mark_kindness: int mark_freebie: int mark_clarity: int From debd1dea22e46bf96588e26e79c171cac4d75d93 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 11 Nov 2024 20:45:40 +0000 Subject: [PATCH 02/23] fixtures+ tests --- rating_api/utils/utils.py | 15 +++ tests/conftest.py | 86 ++++++++++++- tests/test_routes/test_comment.py | 131 +++++++++----------- tests/test_routes/test_lecturer.py | 189 ++++++++++------------------- 4 files changed, 219 insertions(+), 202 deletions(-) create mode 100644 rating_api/utils/utils.py diff --git a/rating_api/utils/utils.py b/rating_api/utils/utils.py new file mode 100644 index 0000000..fb0a47f --- /dev/null +++ b/rating_api/utils/utils.py @@ -0,0 +1,15 @@ +import random +import string + + +def random_string(length: int = 12): + """ + Сгенерировать рандомную строку + :param length: длина строки(по умолчанию 12) + :return: Сгенериированную строку + """ + return "".join([random.choice(string.ascii_lowercase) for _ in range(length)]) + + +def random_mark(): + return random.randint(-2, 2) diff --git a/tests/conftest.py b/tests/conftest.py index cc467ce..1a76254 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,9 +2,10 @@ from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker - +from rating_api.models.db import * from rating_api.routes import app from rating_api.settings import Settings +from rating_api.utils.utils import random_string, random_mark @pytest.fixture @@ -28,3 +29,86 @@ def dbsession() -> Session: engine = create_engine(str(settings.DB_DSN), pool_pre_ping=True) TestingSessionLocal = sessionmaker(bind=engine) yield TestingSessionLocal() + + +@pytest.fixture +def lecturer(dbsession): + """ + Вызов фабрики создает препода и возвращает его + ``` + def test(lecturer): + lecturer1 = lecturer() + lecturer2 = lecturer() + ``` + """ + lecturers = [] + + def _lecturer(first_name: str | None = None, last_name: str | None = None, middle_name: str | None = None): + nonlocal lecturers + first_name = f"test_fname{random_string()}" if first_name is None else first_name + last_name = f"test_lname{random_string()}" if last_name is None else last_name + middle_name = f"test_mname{random_string()}" if middle_name is None else middle_name + __lecturer = Lecturer( + first_name=first_name, last_name=last_name, middle_name=middle_name, timetable_id=len(lecturers) + ) + dbsession.add(__lecturer) + dbsession.commit() + lecturers.append(__lecturer) + return __lecturer + + yield _lecturer + dbsession.expire_all() + dbsession.commit() + for row in lecturers: + dbsession.delete(row) + dbsession.commit() + + +@pytest.fixture +def comment(dbsession, lecturer): + """ " + Вызов фабрики создает комментарий к преподавателю и возвращает его + ``` + def test(comment): + comment1 = comment() + comment2 = comment() + ``` + """ + comments = [] + + def _comment( + lecturer_id: int | None = None, + user_id: int | None = None, + review_status: ReviewStatus = ReviewStatus.APPROVED, + subject: str | None = None, + ): + nonlocal comments + text = random_string() + subject = random_string() if subject is None else subject + mark_kindness = random_mark() + mark_freebie = random_mark() + mark_clarity = random_mark() + if lecturer_id is None: + lecturer = lecturer() + lecturer_id = lecturer.id + __comment = Comment( + subject=subject, + text=text, + mark_kindness=mark_kindness, + mark_clarity=mark_clarity, + mark_freebie=mark_freebie, + lecturer_id=lecturer_id, + user_id=user_id, + review_status=review_status, + ) + dbsession.add(__comment) + dbsession.commit() + comments.append(__comment) + return __comment + + yield _comment + dbsession.expire_all() + dbsession.commit() + for row in comments: + dbsession.delete(row) + dbsession.commit() diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index d31c912..5c16517 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -13,12 +13,8 @@ settings = get_settings() -def test_create_comment(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - +def test_create_comment(client, dbsession, comment, lecturer): + lecturer = lecturer() body = { "subject": "Физика", "text": "Хороший препод", @@ -28,7 +24,6 @@ def test_create_comment(client, dbsession): } params = {"lecturer_id": lecturer.id} post_response = client.post(url, json=body, params=params) - print(post_response.json()) assert post_response.status_code == status.HTTP_200_OK json_response = post_response.json() comment = Comment.query(session=dbsession).filter(Comment.uuid == json_response["uuid"]).one_or_none() @@ -41,17 +36,14 @@ def test_create_comment(client, dbsession): assert user_comment is not None dbsession.delete(user_comment) dbsession.delete(comment) - dbsession.delete(lecturer) dbsession.commit() + params = {"lecturer_id": 1} post_response = client.post(url, json=body, params=params) assert post_response.status_code == status.HTTP_404_NOT_FOUND -def test_post_bad_mark(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() +def test_post_bad_mark(client, lecturer): + test_lecturer = lecturer() body = { "subject": "Физика", @@ -60,76 +52,69 @@ def test_post_bad_mark(client, dbsession): "mark_freebie": -2, "mark_clarity": 0, } - params = {"lecturer_id": lecturer.id} + params = {"lecturer_id": test_lecturer.id} post_response = client.post(url, json=body, params=params) assert post_response.status_code == status.HTTP_400_BAD_REQUEST - dbsession.delete(lecturer) - dbsession.commit() -def test_get_comment(client, dbsession): - body = { - "first_name": 'Иван', - "last_name": 'Иванов', - "middle_name": 'Иванович', - "timetable_id": 0, - } - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - - body = { - "lecturer_id": lecturer.id, - "subject": "Физика", - "text": "Хороший препод", - "mark_kindness": 1, - "mark_freebie": -2, - "mark_clarity": 0, - "review_status": ReviewStatus.APPROVED, - } - comment: Comment = Comment(**body) - dbsession.add(comment) - dbsession.commit() - response_comment = client.get(f'{url}/{comment.uuid}') - assert response_comment.status_code == status.HTTP_200_OK +def test_get_comment(client, dbsession, lecturer, comment): + test_lecturer = lecturer() + test_comment = comment(lecturer_id=test_lecturer.id) random_uuid = uuid.uuid4() response = client.get(f'{url}/{random_uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND - comment = Comment.query(session=dbsession).filter(Comment.uuid == response_comment.json()["uuid"]).one_or_none() - assert comment is not None - dbsession.delete(comment) - dbsession.delete(lecturer) - dbsession.commit() - - -def test_delete_comment(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - - body = { - "lecturer_id": lecturer.id, - "subject": "Физика", - "text": "Хороший препод", - "mark_kindness": 1, - "mark_freebie": -2, - "mark_clarity": 0, - "review_status": ReviewStatus.APPROVED, - } - comment: Comment = Comment(**body) - dbsession.add(comment) - dbsession.commit() - response = client.delete(f'{url}/{comment.uuid}') + response = client.get(f'{url}/{test_comment.uuid}') + assert response.status_code == status.HTTP_200_OK + comment1 = Comment.query(session=dbsession).filter(Comment.uuid == response.json()["uuid"]).one_or_none() + assert comment1 is not None + + +def test_get_comments_by_lecturer_id(client, lecturer, comment): + lecturer1 = lecturer() + lecturer2 = lecturer() + comment1_1 = comment(lecturer_id=lecturer1.id) + comment1_2 = comment(lecturer_id=lecturer1.id) + comment2_1 = comment(lecturer_id=lecturer2.id) + query = {"lecturer_id": lecturer1.id} + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_200_OK + json_response = response.json() + assert json_response["total"] == 2 + query = {"lecturer_id": lecturer2.id} + response = client.get(f'{url}', params=query) assert response.status_code == status.HTTP_200_OK - response = client.get(f'{url}/{comment.uuid}') + json_response = response.json() + assert json_response["total"] == 1 + query = {"lecturer_id": 2} + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_200_OK + json_response = response.json() + assert json_response["total"] == 0 + + +def test_get_comments_unreviewed(client, lecturer, comment): + lecturer1 = lecturer() + lecturer2 = lecturer() + comment1_1 = comment(lecturer_id=lecturer1.id, review_status=ReviewStatus.APPROVED) + comment1_2 = comment(lecturer_id=lecturer1.id, review_status=ReviewStatus.PENDING) + comment2_1 = comment(lecturer_id=lecturer2.id, review_status=ReviewStatus.PENDING) + comment2_2 = comment(lecturer_id=lecturer2.id, review_status=ReviewStatus.DISMISSED) + query = {'unreviewed': True} + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_403_FORBIDDEN + + +def test_delete_comment(client, dbsession, lecturer, comment): + test_lecturer = lecturer() + test_comment = comment(lecturer_id=test_lecturer.id) + response = client.delete(f'{url}/{test_comment.uuid}') + dbsession.refresh(test_comment) + assert response.status_code == status.HTTP_200_OK + response = client.get(f'{url}/{test_comment.uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND random_uuid = uuid.uuid4() response = client.delete(f'{url}/{random_uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND - comment1 = Comment.query(session=dbsession).filter(Comment.uuid == comment.uuid).one_or_none() + comment1 = Comment.query(session=dbsession).filter(Comment.uuid == test_comment.uuid).one_or_none() assert comment1 is None - comment.is_deleted = True - dbsession.delete(comment) - dbsession.delete(lecturer) - dbsession.commit() + assert test_comment.is_deleted diff --git a/tests/test_routes/test_lecturer.py b/tests/test_routes/test_lecturer.py index 39027d0..c02742e 100644 --- a/tests/test_routes/test_lecturer.py +++ b/tests/test_routes/test_lecturer.py @@ -1,7 +1,7 @@ import logging from starlette import status - +import pytest from rating_api.models import Comment, Lecturer, ReviewStatus from rating_api.settings import get_settings @@ -26,14 +26,9 @@ def test_create_lecturer(client, dbsession): assert lecturer is None -def test_get_lecturer(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - db_lecturer: Lecturer = Lecturer.query(session=dbsession).filter(Lecturer.timetable_id == 0).one_or_none() - assert db_lecturer is not None - get_response = client.get(f'{url}/{db_lecturer.id}') +def test_get_lecturer(client, lecturer): + lecturer = lecturer() + get_response = client.get(f'{url}/{lecturer.id}') print(get_response.json()) assert get_response.status_code == status.HTTP_200_OK json_response = get_response.json() @@ -42,122 +37,77 @@ def test_get_lecturer(client, dbsession): assert json_response["mark_clarity"] is None assert json_response["mark_general"] is None assert json_response["comments"] is None - dbsession.delete(lecturer) - dbsession.commit() -def test_get_lecturer_with_comments(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - db_lecturer: Lecturer = Lecturer.query(session=dbsession).filter(Lecturer.timetable_id == 0).one_or_none() - assert db_lecturer is not None - - comment1: dict = { - "subject": "Физика", - "text": "Хороший преподаватель", - "mark_kindness": 2, - "mark_freebie": 0, - "mark_clarity": 2, - "lecturer_id": db_lecturer.id, - "review_status": ReviewStatus.APPROVED, - } - comment2: dict = { - "subject": "Физика", - "text": "Средне", - "mark_kindness": -1, - "mark_freebie": 1, - "mark_clarity": -1, - "lecturer_id": db_lecturer.id, - "review_status": ReviewStatus.APPROVED, - } - comment3: dict = { - "subject": "Физика", - "text": "Средне", - "mark_kindness": 2, - "mark_freebie": 2, - "mark_clarity": 2, - "lecturer_id": db_lecturer.id, - "review_status": ReviewStatus.PENDING, - } - comment1: Comment = Comment.create(session=dbsession, **comment1) - comment2: Comment = Comment.create(session=dbsession, **comment2) - comment3: Comment = Comment.create(session=dbsession, **comment3) - dbsession.commit() - assert comment1 is not None - assert comment2 is not None - assert comment3 is not None +def test_get_lecturer_with_comments(client, lecturer, comment): + test_lecturer = lecturer() + comment1 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.APPROVED) + comment2 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.PENDING) + comment3 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.DISMISSED) query = { "info": ['comments', 'mark'], } - response = client.get(f'{url}/{db_lecturer.id}', params=query) - print(response.json()) + response = client.get(f'{url}/{test_lecturer.id}', params=query) assert response.status_code == status.HTTP_200_OK json_response = response.json() - assert json_response["mark_kindness"] == 0.5 - assert json_response["mark_freebie"] == 0.5 - assert json_response["mark_clarity"] == 0.5 - assert json_response["mark_general"] == 0.5 - assert "Физика" in json_response["subjects"] - assert len(json_response["comments"]) != 0 - dbsession.delete(comment1) - dbsession.delete(comment2) - dbsession.delete(comment3) - dbsession.delete(lecturer) - dbsession.commit() - - -def test_get_lecturers_by_name(client, dbsession): - body_list = [ - {"first_name": 'Алиса', "last_name": 'Селезнёва', "middle_name": 'Ивановна', "timetable_id": 0}, - {"first_name": 'Марат', "last_name": 'Сельков', "middle_name": 'Анатольевич', "timetable_id": 1}, - {"first_name": 'М.', "last_name": 'Измайлов', "middle_name": 'Р.', "timetable_id": 2}, - {"first_name": 'Михаил', "last_name": 'Измайлов', "middle_name": 'Ильич', "timetable_id": 3}, - ] - lecturer_list: list[Lecturer] = [Lecturer(**body_list[i]) for i in range(4)] - for lecturer in lecturer_list: - dbsession.add(lecturer) - dbsession.commit() - db_lecturer: Lecturer = Lecturer.query(session=dbsession).filter(Lecturer.timetable_id == 0).one_or_none() - assert db_lecturer is not None + assert json_response["mark_kindness"] == comment1.mark_kindness + assert json_response["mark_freebie"] == comment1.mark_freebie + assert json_response["mark_clarity"] == comment1.mark_clarity + assert json_response["mark_general"] == (comment1.mark_kindness + comment1.mark_freebie + comment1.mark_clarity) / 3 + assert len(json_response["comments"]) == 1 + assert json_response["comments"][0]["text"] == comment1.text + + +def test_get_lecturers_by_name(client, lecturer): + lecturer1 = lecturer(first_name='Алиса', last_name='Селезнёва', middle_name='Ивановна') + lecturer2 = lecturer(first_name='Марат', last_name='Сельков', middle_name='Анатольевич') + lecturer3 = lecturer(first_name='М', last_name='Измайлов', middle_name='Р') + lecturer4 = lecturer(first_name='Михаил', last_name='Измайлов', middle_name='Ильич') query = {"name": "Селезнёва"} - get_response = client.get(f'{url}', params=query) - assert get_response.status_code == status.HTTP_200_OK - json_response = get_response.json() + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_200_OK + json_response = response.json() assert json_response["total"] == 1 assert json_response["lecturers"][0]["first_name"] == "Алиса" query = {"name": "Сел"} - get_response = client.get(f'{url}', params=query) - assert get_response.status_code == status.HTTP_200_OK - json_response = get_response.json() + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_200_OK + json_response = response.json() assert json_response["total"] == 2 assert json_response["lecturers"][0]["first_name"] == "Алиса" assert json_response["lecturers"][1]["first_name"] == "Марат" query = {"name": "Измайлова"} - get_response = client.get(f'{url}', params=query) - assert get_response.status_code == status.HTTP_404_NOT_FOUND + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_404_NOT_FOUND - for lecturer in lecturer_list: - dbsession.delete(lecturer) - dbsession.commit() +def test_get_lecturers_by_subject(client, lecturer, comment): + lecturer1 = lecturer() + comment1 = comment(lecturer_id=lecturer1.id, subject="bibki") + lecturer2 = lecturer() + comment2 = comment(lecturer_id=lecturer2.id, subject="bibki") + lecturer3 = lecturer() + comment3 = comment(lecturer_id=lecturer2.id, subject="bobki") + query = {"subject": "bibki"} + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_200_OK + json_response = response.json() + assert json_response["total"] == 2 + query = {"subject": "bibobki"} + response = client.get(f'{url}', params=query) + assert response.status_code == status.HTTP_404_NOT_FOUND -def test_update_lecturer(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() + +def test_update_lecturer(client, lecturer): + test_lecturer = lecturer() body = { "first_name": 'Алексей', "last_name": 'Алексеев', "middle_name": 'Алексеевич', } - db_lecturer: Lecturer = Lecturer.query(session=dbsession).filter(Lecturer.timetable_id == 0).one_or_none() - assert db_lecturer is not None - response = client.patch(f"{url}/{db_lecturer.id}", json=body) + response = client.patch(f"{url}/{test_lecturer.id}", json=body) assert response.status_code == status.HTTP_200_OK json_response = response.json() assert json_response["first_name"] == 'Алексей' @@ -168,40 +118,23 @@ def test_update_lecturer(client, dbsession): "last_name": 'Иванов', "middle_name": 'Иванович', } - response = client.patch(f"{url}/{db_lecturer.id}", json=body) + response = client.patch(f"{url}/{test_lecturer.id}", json=body) assert response.status_code == status.HTTP_200_OK json_response = response.json() assert json_response["first_name"] == 'Иван' assert json_response["last_name"] == 'Иванов' assert json_response["middle_name"] == "Иванович" - dbsession.delete(lecturer) - dbsession.commit() -def test_delete_lecturer(client, dbsession): - body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} - lecturer: Lecturer = Lecturer(**body) - dbsession.add(lecturer) - dbsession.commit() - comment: dict = { - "subject": "Физика", - "text": "Хороший преподаватель", - "mark_kindness": 2, - "mark_freebie": 0, - "mark_clarity": 2, - "lecturer_id": lecturer.id, - "review_status": ReviewStatus.APPROVED, - } - comment: Comment = Comment.create(session=dbsession, **comment) - dbsession.commit() - lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == 0).one_or_none() - assert lecturer is not None - response = client.delete(f"{url}/{lecturer.id}") +def test_delete_lecturer(client, lecturer, comment, dbsession): + test_lecturer = lecturer() + test_comment = comment(lecturer_id=test_lecturer.id) + response = client.delete(f"{url}/{test_lecturer.id}") assert response.status_code == status.HTTP_200_OK - response = client.delete(f"{url}/{lecturer.id}") + response = client.delete(f"{url}/{test_lecturer.id}") + dbsession.refresh(test_lecturer) assert response.status_code == status.HTTP_404_NOT_FOUND - lecturer.is_deleted = True - comment.is_deleted = True - dbsession.delete(comment) - dbsession.delete(lecturer) - dbsession.commit() + response = client.get(f"{url}/{test_lecturer.id}") + assert response.status_code == status.HTTP_404_NOT_FOUND + assert test_comment.is_deleted + assert test_lecturer.is_deleted From 12f13401b1eb8d7648833949e4159528aa67f778 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 11 Nov 2024 20:49:26 +0000 Subject: [PATCH 03/23] lint --- tests/conftest.py | 3 ++- tests/test_routes/test_lecturer.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1a76254..884c224 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,10 +2,11 @@ from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker + from rating_api.models.db import * from rating_api.routes import app from rating_api.settings import Settings -from rating_api.utils.utils import random_string, random_mark +from rating_api.utils.utils import random_mark, random_string @pytest.fixture diff --git a/tests/test_routes/test_lecturer.py b/tests/test_routes/test_lecturer.py index c02742e..13e5728 100644 --- a/tests/test_routes/test_lecturer.py +++ b/tests/test_routes/test_lecturer.py @@ -1,7 +1,8 @@ import logging -from starlette import status import pytest +from starlette import status + from rating_api.models import Comment, Lecturer, ReviewStatus from rating_api.settings import get_settings From 9a4da78b55fd2a2972b2eacea8b0f28cef7a4e1f Mon Sep 17 00:00:00 2001 From: default Date: Tue, 12 Nov 2024 13:45:21 +0000 Subject: [PATCH 04/23] refactoring --- tests/conftest.py | 146 ++++++++++------- tests/test_routes/test_comment.py | 186 +++++++++++---------- tests/test_routes/test_lecturer.py | 251 +++++++++++++++-------------- 3 files changed, 310 insertions(+), 273 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 884c224..a84a9cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,7 +6,6 @@ from rating_api.models.db import * from rating_api.routes import app from rating_api.settings import Settings -from rating_api.utils.utils import random_mark, random_string @pytest.fixture @@ -29,87 +28,110 @@ def dbsession() -> Session: settings = Settings() engine = create_engine(str(settings.DB_DSN), pool_pre_ping=True) TestingSessionLocal = sessionmaker(bind=engine) - yield TestingSessionLocal() + session = TestingSessionLocal() + yield session @pytest.fixture def lecturer(dbsession): + _lecturer = Lecturer(first_name="test_fname", last_name="test_lname", middle_name="test_mname", timetable_id=0) + dbsession.add(_lecturer) + dbsession.commit() + yield _lecturer + dbsession.refresh(_lecturer) + dbsession.delete(_lecturer) + dbsession.commit() + + +@pytest.fixture +def comment(dbsession, lecturer): + _comment = Comment( + subject="test_subject", + text="test_comment", + mark_kindness=1, + mark_clarity=1, + mark_freebie=1, + lecturer_id=lecturer.id, + review_status=ReviewStatus.APPROVED, + ) + dbsession.add(_comment) + dbsession.commit() + yield _comment + dbsession.refresh(_comment) + dbsession.delete(_comment) + dbsession.commit() + + +@pytest.fixture(scope='function') +def lecturers(dbsession): """ - Вызов фабрики создает препода и возвращает его - ``` - def test(lecturer): - lecturer1 = lecturer() - lecturer2 = lecturer() - ``` + Creates 4 lecturers(one with flag is_deleted=True) """ - lecturers = [] - - def _lecturer(first_name: str | None = None, last_name: str | None = None, middle_name: str | None = None): - nonlocal lecturers - first_name = f"test_fname{random_string()}" if first_name is None else first_name - last_name = f"test_lname{random_string()}" if last_name is None else last_name - middle_name = f"test_mname{random_string()}" if middle_name is None else middle_name - __lecturer = Lecturer( - first_name=first_name, last_name=last_name, middle_name=middle_name, timetable_id=len(lecturers) - ) - dbsession.add(__lecturer) - dbsession.commit() - lecturers.append(__lecturer) - return __lecturer + lecturers_data = [ + ("test_fname1", "test_lname1", "test_mname1", 0), + ("test_fname2", "test_lname2", "test_mname2", 1), + ("Bibka", "Bobka", "Bobkovich", 2), + ] - yield _lecturer - dbsession.expire_all() + lecturers = [ + Lecturer(first_name=fname, last_name=lname, middle_name=mname, timetable_id=timetable_id) + for fname, lname, mname, timetable_id in lecturers_data + ] + lecturers.append( + Lecturer(first_name='test_fname3', last_name='test_lname3', middle_name='test_mname3', timetable_id=3) + ) + lecturers[-1].is_deleted = True + for lecturer in lecturers: + dbsession.add(lecturer) dbsession.commit() - for row in lecturers: - dbsession.delete(row) + yield lecturers + for lecturer in lecturers: + dbsession.refresh(lecturer) + for row in lecturer.comments: + dbsession.delete(row) + lecturer_user_comments = dbsession.query(LecturerUserComment).filter( + LecturerUserComment.lecturer_id == lecturer.id + ) + for row in lecturer_user_comments: + dbsession.delete(row) + dbsession.delete(lecturer) dbsession.commit() @pytest.fixture -def comment(dbsession, lecturer): - """ " - Вызов фабрики создает комментарий к преподавателю и возвращает его - ``` - def test(comment): - comment1 = comment() - comment2 = comment() - ``` +def lecturers_with_comments(dbsession, lecturers): + """ + Creates 4 lecturers(one with flag is_deleted=True) with 3 comments to non-deleted lecturers 2 approved and one dismissed. Two of them have alike names. """ - comments = [] - - def _comment( - lecturer_id: int | None = None, - user_id: int | None = None, - review_status: ReviewStatus = ReviewStatus.APPROVED, - subject: str | None = None, - ): - nonlocal comments - text = random_string() - subject = random_string() if subject is None else subject - mark_kindness = random_mark() - mark_freebie = random_mark() - mark_clarity = random_mark() - if lecturer_id is None: - lecturer = lecturer() - lecturer_id = lecturer.id - __comment = Comment( + comments_data = [ + (lecturers[0].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[0].id, 'test_subject1', ReviewStatus.APPROVED, 2, 2, 2), + (lecturers[0].id, 'test_subject2', ReviewStatus.DISMISSED, -1, -1, -1), + (lecturers[1].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[1].id, 'test_subject1', ReviewStatus.APPROVED, -1, -1, -1), + (lecturers[1].id, 'test_subject2', ReviewStatus.DISMISSED, -2, -2, -2), + (lecturers[2].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[2].id, 'test_subject1', ReviewStatus.APPROVED, 0, 0, 0), + (lecturers[2].id, 'test_subject2', ReviewStatus.DISMISSED, 2, 2, 2), + ] + + comments = [ + Comment( subject=subject, - text=text, + text="test_comment", mark_kindness=mark_kindness, mark_clarity=mark_clarity, mark_freebie=mark_freebie, lecturer_id=lecturer_id, - user_id=user_id, review_status=review_status, ) - dbsession.add(__comment) - dbsession.commit() - comments.append(__comment) - return __comment + for lecturer_id, subject, review_status, mark_kindness, mark_clarity, mark_freebie in comments_data + ] - yield _comment - dbsession.expire_all() + dbsession.add_all(comments) dbsession.commit() - for row in comments: - dbsession.delete(row) + yield lecturers, comments + for comment in comments: + dbsession.refresh(comment) + dbsession.delete(comment) dbsession.commit() diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 5c16517..da6b28b 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -1,6 +1,7 @@ import logging import uuid +import pytest from starlette import status from rating_api.models import Comment, Lecturer, LecturerUserComment, ReviewStatus @@ -13,108 +14,105 @@ settings = get_settings() -def test_create_comment(client, dbsession, comment, lecturer): - lecturer = lecturer() - body = { - "subject": "Физика", - "text": "Хороший препод", - "mark_kindness": 1, - "mark_freebie": -2, - "mark_clarity": 0, - } - params = {"lecturer_id": lecturer.id} +@pytest.mark.parametrize( + 'body,timetable_id,response_status', + [ + ( + { + "subject": "test_subject", + "text": "test_text", + "mark_kindness": 1, + "mark_freebie": 0, + "mark_clarity": 0, + }, + 0, + status.HTTP_200_OK, + ), + ( + { + "subject": "test1_subject", + "text": "test_text", + "mark_kindness": -2, + "mark_freebie": -2, + "mark_clarity": -2, + }, + 1, + status.HTTP_200_OK, + ), + ( # bad mark + { + "subject": "test_subject", + "text": "test_text", + "mark_kindness": 5, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 2, + status.HTTP_400_BAD_REQUEST, + ), + ( # deleted lecturer + { + "subject": "test_subject", + "text": "test_text", + "mark_kindness": 5, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 3, + status.HTTP_400_BAD_REQUEST, + ), + ], +) +def test_create_comment(client, dbsession, lecturers, body, timetable_id, response_status): + params = {"lecturer_id": lecturers[timetable_id].id} # Ensure we get the ID of the lecturer post_response = client.post(url, json=body, params=params) - assert post_response.status_code == status.HTTP_200_OK - json_response = post_response.json() - comment = Comment.query(session=dbsession).filter(Comment.uuid == json_response["uuid"]).one_or_none() - assert comment is not None - user_comment = ( - LecturerUserComment.query(session=dbsession) - .filter(LecturerUserComment.lecturer_id == lecturer.id) - .one_or_none() - ) - assert user_comment is not None - dbsession.delete(user_comment) - dbsession.delete(comment) - dbsession.commit() - params = {"lecturer_id": 1} - post_response = client.post(url, json=body, params=params) - assert post_response.status_code == status.HTTP_404_NOT_FOUND - - -def test_post_bad_mark(client, lecturer): - test_lecturer = lecturer() - - body = { - "subject": "Физика", - "text": "Хороший препод", - "mark_kindness": 4, - "mark_freebie": -2, - "mark_clarity": 0, - } - params = {"lecturer_id": test_lecturer.id} - post_response = client.post(url, json=body, params=params) - assert post_response.status_code == status.HTTP_400_BAD_REQUEST - - -def test_get_comment(client, dbsession, lecturer, comment): - test_lecturer = lecturer() - test_comment = comment(lecturer_id=test_lecturer.id) + assert post_response.status_code == response_status + if response_status == status.HTTP_200_OK: + comment = Comment.query(session=dbsession).filter(Comment.uuid == post_response.json()["uuid"]).one_or_none() + assert comment is not None + user_comment = ( + LecturerUserComment.query(session=dbsession) + .filter(LecturerUserComment.lecturer_id == lecturers[timetable_id].id) + .one_or_none() + ) + assert user_comment is not None + + +def test_get_comment(client, comment): + response_comment = client.get(f'{url}/{comment.uuid}') + assert response_comment.status_code == status.HTTP_200_OK random_uuid = uuid.uuid4() response = client.get(f'{url}/{random_uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND - response = client.get(f'{url}/{test_comment.uuid}') - assert response.status_code == status.HTTP_200_OK - comment1 = Comment.query(session=dbsession).filter(Comment.uuid == response.json()["uuid"]).one_or_none() - assert comment1 is not None -def test_get_comments_by_lecturer_id(client, lecturer, comment): - lecturer1 = lecturer() - lecturer2 = lecturer() - comment1_1 = comment(lecturer_id=lecturer1.id) - comment1_2 = comment(lecturer_id=lecturer1.id) - comment2_1 = comment(lecturer_id=lecturer2.id) - query = {"lecturer_id": lecturer1.id} - response = client.get(f'{url}', params=query) +@pytest.mark.parametrize( + 'timetable_id,response_status', [(0, status.HTTP_200_OK), (1, status.HTTP_200_OK), (3, status.HTTP_200_OK)] +) +def test_comments_by_lecturer_id(client, lecturers_with_comments, timetable_id, response_status): + lecturers, comments = lecturers_with_comments + response = client.get(f'{url}', params={"lecturer_id": lecturers[timetable_id].id}) + assert response.status_code == response_status + if response.status_code == status.HTTP_200_OK: + json_response = response.json() + assert len(json_response["comments"]) == len( + [ + comment + for comment in lecturers[timetable_id].comments + if comment.review_status == ReviewStatus.APPROVED and not comment.is_deleted + ] + ) + + +def test_delete_comment(client, dbsession, comment): + response = client.delete(f'{url}/{comment.uuid}') assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["total"] == 2 - query = {"lecturer_id": lecturer2.id} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["total"] == 1 - query = {"lecturer_id": 2} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["total"] == 0 - - -def test_get_comments_unreviewed(client, lecturer, comment): - lecturer1 = lecturer() - lecturer2 = lecturer() - comment1_1 = comment(lecturer_id=lecturer1.id, review_status=ReviewStatus.APPROVED) - comment1_2 = comment(lecturer_id=lecturer1.id, review_status=ReviewStatus.PENDING) - comment2_1 = comment(lecturer_id=lecturer2.id, review_status=ReviewStatus.PENDING) - comment2_2 = comment(lecturer_id=lecturer2.id, review_status=ReviewStatus.DISMISSED) - query = {'unreviewed': True} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_403_FORBIDDEN - - -def test_delete_comment(client, dbsession, lecturer, comment): - test_lecturer = lecturer() - test_comment = comment(lecturer_id=test_lecturer.id) - response = client.delete(f'{url}/{test_comment.uuid}') - dbsession.refresh(test_comment) - assert response.status_code == status.HTTP_200_OK - response = client.get(f'{url}/{test_comment.uuid}') + response = client.get(f'{url}/{comment.uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND random_uuid = uuid.uuid4() response = client.delete(f'{url}/{random_uuid}') assert response.status_code == status.HTTP_404_NOT_FOUND - comment1 = Comment.query(session=dbsession).filter(Comment.uuid == test_comment.uuid).one_or_none() - assert comment1 is None - assert test_comment.is_deleted + dbsession.refresh(comment) + assert comment.is_deleted + response = client.get(f'{url}/{comment.uuid}') + assert response.status_code == status.HTTP_404_NOT_FOUND diff --git a/tests/test_routes/test_lecturer.py b/tests/test_routes/test_lecturer.py index 13e5728..52c251b 100644 --- a/tests/test_routes/test_lecturer.py +++ b/tests/test_routes/test_lecturer.py @@ -13,129 +13,146 @@ settings = get_settings() -def test_create_lecturer(client, dbsession): +@pytest.mark.parametrize('response_status', [status.HTTP_200_OK, status.HTTP_409_CONFLICT]) +def test_create_lecturer(client, dbsession, response_status): body = {"first_name": 'Иван', "last_name": 'Иванов', "middle_name": 'Иванович', "timetable_id": 0} post_response = client.post(url, json=body) - assert post_response.status_code == status.HTTP_200_OK - check_same_response = client.post(url, json=body) - assert check_same_response.status_code == status.HTTP_409_CONFLICT - lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == 0).one_or_none() - assert lecturer is not None - dbsession.delete(lecturer) - dbsession.commit() - lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == 0).one_or_none() - assert lecturer is None - - -def test_get_lecturer(client, lecturer): - lecturer = lecturer() - get_response = client.get(f'{url}/{lecturer.id}') - print(get_response.json()) - assert get_response.status_code == status.HTTP_200_OK - json_response = get_response.json() - assert json_response["mark_kindness"] is None - assert json_response["mark_freebie"] is None - assert json_response["mark_clarity"] is None - assert json_response["mark_general"] is None - assert json_response["comments"] is None - - -def test_get_lecturer_with_comments(client, lecturer, comment): - test_lecturer = lecturer() - comment1 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.APPROVED) - comment2 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.PENDING) - comment3 = comment(lecturer_id=test_lecturer.id, review_status=ReviewStatus.DISMISSED) - query = { - "info": ['comments', 'mark'], - } - response = client.get(f'{url}/{test_lecturer.id}', params=query) + assert post_response.status_code == response_status + # cleanup on a last run + if response_status == status.HTTP_409_CONFLICT: + lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == 0).one_or_none() + assert lecturer is not None + dbsession.delete(lecturer) + dbsession.commit() + lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == 0).one_or_none() + assert lecturer is None + + +@pytest.mark.parametrize( + 'timetable_id,response_status', + [ + (0, status.HTTP_200_OK), + (1, status.HTTP_200_OK), + (2, status.HTTP_200_OK), + (3, status.HTTP_404_NOT_FOUND), + ], +) +def test_get_lecturer(client, dbsession, lecturers, timetable_id, response_status): + lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == timetable_id).one_or_none() + # check non-existing id request + lecturer_id = -1 + if lecturer: + lecturer_id = lecturer.id + get_response = client.get(f'{url}/{lecturer_id}') + assert get_response.status_code == response_status + if response_status == status.HTTP_200_OK: + json_response = get_response.json() + assert json_response["mark_kindness"] is None + assert json_response["mark_freebie"] is None + assert json_response["mark_clarity"] is None + assert json_response["mark_general"] is None + assert json_response["comments"] is None + + +@pytest.mark.parametrize( + 'timetable_id,mark_kindness,mark_freebie,mark_clarity,mark_general', + [(0, 1.5, 1.5, 1.5, 1.5), (1, 0, 0, 0, 0), (2, 0.5, 0.5, 0.5, 0.5)], +) +def test_get_lecturer_with_comments( + client, lecturers_with_comments, timetable_id, mark_kindness, mark_freebie, mark_clarity, mark_general +): + lecturers, comments = lecturers_with_comments + query = {"info": ['comments', 'mark']} + response = client.get(f'{url}/{lecturers[timetable_id].id}', params=query) assert response.status_code == status.HTTP_200_OK json_response = response.json() - assert json_response["mark_kindness"] == comment1.mark_kindness - assert json_response["mark_freebie"] == comment1.mark_freebie - assert json_response["mark_clarity"] == comment1.mark_clarity - assert json_response["mark_general"] == (comment1.mark_kindness + comment1.mark_freebie + comment1.mark_clarity) / 3 - assert len(json_response["comments"]) == 1 - assert json_response["comments"][0]["text"] == comment1.text - - -def test_get_lecturers_by_name(client, lecturer): - lecturer1 = lecturer(first_name='Алиса', last_name='Селезнёва', middle_name='Ивановна') - lecturer2 = lecturer(first_name='Марат', last_name='Сельков', middle_name='Анатольевич') - lecturer3 = lecturer(first_name='М', last_name='Измайлов', middle_name='Р') - lecturer4 = lecturer(first_name='Михаил', last_name='Измайлов', middle_name='Ильич') - query = {"name": "Селезнёва"} - response = client.get(f'{url}', params=query) + assert json_response["mark_kindness"] == mark_kindness + assert json_response["mark_freebie"] == mark_freebie + assert json_response["mark_clarity"] == mark_clarity + assert json_response["mark_general"] == mark_general + assert "test_subject" in json_response["subjects"] + assert "test_subject1" in json_response["subjects"] + assert "test_subject2" not in json_response["subjects"] + assert len(json_response["comments"]) == 2 + + +@pytest.mark.parametrize( + 'query,total,response_status', + [ + ({'name': 'test_lname1'}, 1, status.HTTP_200_OK), + ({'name': 'test'}, 2, status.HTTP_200_OK), + ({'name': 'testlname123'}, 0, status.HTTP_404_NOT_FOUND), + ], +) +def test_get_lecturers_by_name(client, lecturers, query, total, response_status): + get_response = client.get(f'{url}', params=query) + assert get_response.status_code == response_status + if response_status == status.HTTP_200_OK: + json_response = get_response.json() + json_response["total"] == total + assert json_response["lecturers"][0]["first_name"] == lecturers[0].first_name + + +@pytest.mark.parametrize( + 'body,response_status', + [ + ( + { + "first_name": 'Test', + "last_name": 'Testov', + "middle_name": 'Testovich', + }, + status.HTTP_200_OK, + ), + ( + { + "first_name": 'Testa', + "last_name": 'Testova', + "middle_name": 'Testovna', + }, + status.HTTP_200_OK, + ), + ( + { + "first_name": 'Test', + "last_name": 'Testov', + "middle_name": 1, + }, + status.HTTP_422_UNPROCESSABLE_ENTITY, + ), + ], +) +def test_update_lecturer(client, lecturer, body, response_status): + response = client.get(f'{url}/{lecturer.id}') assert response.status_code == status.HTTP_200_OK json_response = response.json() - assert json_response["total"] == 1 - assert json_response["lecturers"][0]["first_name"] == "Алиса" - - query = {"name": "Сел"} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["total"] == 2 - assert json_response["lecturers"][0]["first_name"] == "Алиса" - assert json_response["lecturers"][1]["first_name"] == "Марат" - - query = {"name": "Измайлова"} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -def test_get_lecturers_by_subject(client, lecturer, comment): - lecturer1 = lecturer() - comment1 = comment(lecturer_id=lecturer1.id, subject="bibki") - lecturer2 = lecturer() - comment2 = comment(lecturer_id=lecturer2.id, subject="bibki") - lecturer3 = lecturer() - comment3 = comment(lecturer_id=lecturer2.id, subject="bobki") - query = {"subject": "bibki"} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["total"] == 2 - query = {"subject": "bibobki"} - response = client.get(f'{url}', params=query) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -def test_update_lecturer(client, lecturer): - test_lecturer = lecturer() - body = { - "first_name": 'Алексей', - "last_name": 'Алексеев', - "middle_name": 'Алексеевич', - } - response = client.patch(f"{url}/{test_lecturer.id}", json=body) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["first_name"] == 'Алексей' - assert json_response["last_name"] == 'Алексеев' - assert json_response["middle_name"] == "Алексеевич" - body = { - "first_name": 'Иван', - "last_name": 'Иванов', - "middle_name": 'Иванович', - } - response = client.patch(f"{url}/{test_lecturer.id}", json=body) - assert response.status_code == status.HTTP_200_OK - json_response = response.json() - assert json_response["first_name"] == 'Иван' - assert json_response["last_name"] == 'Иванов' - assert json_response["middle_name"] == "Иванович" - - -def test_delete_lecturer(client, lecturer, comment, dbsession): - test_lecturer = lecturer() - test_comment = comment(lecturer_id=test_lecturer.id) - response = client.delete(f"{url}/{test_lecturer.id}") + assert json_response['first_name'] == 'test_fname' + assert json_response['last_name'] == 'test_lname' + assert json_response['middle_name'] == 'test_mname' + response = client.patch(f"{url}/{lecturer.id}", json=body) + assert response.status_code == response_status + if response_status == status.HTTP_200_OK: + json_response = response.json() + assert json_response["first_name"] == body["first_name"] + assert json_response["last_name"] == body["last_name"] + assert json_response["middle_name"] == body["middle_name"] + + +def test_delete_lecturer(client, dbsession, lecturers_with_comments): + lecturers, comments = lecturers_with_comments + response = client.delete(f"{url}/{lecturers[0].id}") assert response.status_code == status.HTTP_200_OK - response = client.delete(f"{url}/{test_lecturer.id}") - dbsession.refresh(test_lecturer) + # trying to delete deleted + response = client.delete(f"{url}/{lecturers[0].id}") assert response.status_code == status.HTTP_404_NOT_FOUND - response = client.get(f"{url}/{test_lecturer.id}") + dbsession.refresh(comments[0]) + dbsession.refresh(comments[1]) + dbsession.refresh(comments[2]) + dbsession.refresh(lecturers[0]) + assert comments[0].is_deleted + assert comments[1].is_deleted + assert comments[2].is_deleted + assert lecturers[0].is_deleted + # trying to get deleted + response = client.get(f'{url}/{lecturers[0].id}') assert response.status_code == status.HTTP_404_NOT_FOUND - assert test_comment.is_deleted - assert test_lecturer.is_deleted From b49879e38f55d2a7fefc14cb099f9fff527abb12 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 12 Nov 2024 13:50:08 +0000 Subject: [PATCH 05/23] lint --- tests/test_routes/test_comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index da6b28b..7a33de2 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -64,7 +64,7 @@ ], ) def test_create_comment(client, dbsession, lecturers, body, timetable_id, response_status): - params = {"lecturer_id": lecturers[timetable_id].id} # Ensure we get the ID of the lecturer + params = {"lecturer_id": lecturers[timetable_id].id} post_response = client.post(url, json=body, params=params) assert post_response.status_code == response_status if response_status == status.HTTP_200_OK: From ed302302666fee01b25a0c048faf9bf360510254 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 12 Nov 2024 13:51:19 +0000 Subject: [PATCH 06/23] delete file --- rating_api/utils/utils.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 rating_api/utils/utils.py diff --git a/rating_api/utils/utils.py b/rating_api/utils/utils.py deleted file mode 100644 index fb0a47f..0000000 --- a/rating_api/utils/utils.py +++ /dev/null @@ -1,15 +0,0 @@ -import random -import string - - -def random_string(length: int = 12): - """ - Сгенерировать рандомную строку - :param length: длина строки(по умолчанию 12) - :return: Сгенериированную строку - """ - return "".join([random.choice(string.ascii_lowercase) for _ in range(length)]) - - -def random_mark(): - return random.randint(-2, 2) From a35ce8164fa1cb41faa8329ffdf61d34e19ef7c6 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 14 Nov 2024 14:37:43 +0000 Subject: [PATCH 07/23] +test_review_comment --- tests/conftest.py | 47 +++++++++++++++++++++++-------- tests/test_routes/test_comment.py | 17 +++++++++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a84a9cf..369cc80 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -62,6 +62,25 @@ def comment(dbsession, lecturer): dbsession.commit() +@pytest.fixture +def unreviewed_comment(dbsession, lecturer): + _comment = Comment( + subject="test_subject", + text="test_comment", + mark_kindness=1, + mark_clarity=1, + mark_freebie=1, + lecturer_id=lecturer.id, + review_status=ReviewStatus.PENDING, + ) + dbsession.add(_comment) + dbsession.commit() + yield _comment + dbsession.refresh(_comment) + dbsession.delete(_comment) + dbsession.commit() + + @pytest.fixture(scope='function') def lecturers(dbsession): """ @@ -101,18 +120,23 @@ def lecturers(dbsession): @pytest.fixture def lecturers_with_comments(dbsession, lecturers): """ - Creates 4 lecturers(one with flag is_deleted=True) with 3 comments to non-deleted lecturers 2 approved and one dismissed. Two of them have alike names. + Creates 4 lecturers(one with flag is_deleted=True) + with 4 comments to non-deleted lecturers 2 approved and one dismissed and one pending. + Two of them have alike names. """ comments_data = [ - (lecturers[0].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), - (lecturers[0].id, 'test_subject1', ReviewStatus.APPROVED, 2, 2, 2), - (lecturers[0].id, 'test_subject2', ReviewStatus.DISMISSED, -1, -1, -1), - (lecturers[1].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), - (lecturers[1].id, 'test_subject1', ReviewStatus.APPROVED, -1, -1, -1), - (lecturers[1].id, 'test_subject2', ReviewStatus.DISMISSED, -2, -2, -2), - (lecturers[2].id, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), - (lecturers[2].id, 'test_subject1', ReviewStatus.APPROVED, 0, 0, 0), - (lecturers[2].id, 'test_subject2', ReviewStatus.DISMISSED, 2, 2, 2), + (lecturers[0].id, 0, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[0].id, None, 'test_subject1', ReviewStatus.APPROVED, 2, 2, 2), + (lecturers[0].id, 0, 'test_subject2', ReviewStatus.DISMISSED, -1, -1, -1), + (lecturers[0].id, 0, 'test_subject2', ReviewStatus.PENDING, -2, -2, -2), + (lecturers[1].id, 0, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[1].id, None, 'test_subject1', ReviewStatus.APPROVED, -1, -1, -1), + (lecturers[1].id, 0, 'test_subject2', ReviewStatus.DISMISSED, -2, -2, -2), + (lecturers[1].id, 0, 'test_subject2', ReviewStatus.PENDING, -2, -2, -2), + (lecturers[2].id, 0, 'test_subject', ReviewStatus.APPROVED, 1, 1, 1), + (lecturers[2].id, None, 'test_subject1', ReviewStatus.APPROVED, 0, 0, 0), + (lecturers[2].id, 0, 'test_subject2', ReviewStatus.DISMISSED, 2, 2, 2), + (lecturers[2].id, 0, 'test_subject2', ReviewStatus.PENDING, -2, -2, -2), ] comments = [ @@ -123,9 +147,10 @@ def lecturers_with_comments(dbsession, lecturers): mark_clarity=mark_clarity, mark_freebie=mark_freebie, lecturer_id=lecturer_id, + user_id=user_id, review_status=review_status, ) - for lecturer_id, subject, review_status, mark_kindness, mark_clarity, mark_freebie in comments_data + for lecturer_id, user_id, subject, review_status, mark_kindness, mark_clarity, mark_freebie in comments_data ] dbsession.add_all(comments) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 7a33de2..5939f57 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -104,6 +104,23 @@ def test_comments_by_lecturer_id(client, lecturers_with_comments, timetable_id, ) +@pytest.mark.parametrize( + 'review_status, response_status', + [ + ("approved", status.HTTP_200_OK), + ("dismissed", status.HTTP_200_OK), + ("wrong_status", status.HTTP_422_UNPROCESSABLE_ENTITY), + ], +) +def test_review_comment(client, dbsession, unreviewed_comment, review_status, response_status): + query = {"review_status": review_status} + response = client.patch(f"{url}/{unreviewed_comment.uuid}", params=query) + assert response.status_code == response_status + if response.status_code == status.HTTP_200_OK: + dbsession.refresh(unreviewed_comment) + assert unreviewed_comment.review_status == ReviewStatus(review_status) + + def test_delete_comment(client, dbsession, comment): response = client.delete(f'{url}/{comment.uuid}') assert response.status_code == status.HTTP_200_OK From ff2548bc444aeb94fa5e9d3a74d2d5bbb3dbd410 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 15 Nov 2024 15:03:04 +0000 Subject: [PATCH 08/23] fixes + Register check --- tests/conftest.py | 8 +++---- tests/test_routes/test_comment.py | 36 +++++++++++++++++------------- tests/test_routes/test_lecturer.py | 27 ++++++++++++---------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 369cc80..a0a455e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -34,7 +34,7 @@ def dbsession() -> Session: @pytest.fixture def lecturer(dbsession): - _lecturer = Lecturer(first_name="test_fname", last_name="test_lname", middle_name="test_mname", timetable_id=0) + _lecturer = Lecturer(first_name="test_fname", last_name="test_lname", middle_name="test_mname", timetable_id=9900) dbsession.add(_lecturer) dbsession.commit() yield _lecturer @@ -87,9 +87,9 @@ def lecturers(dbsession): Creates 4 lecturers(one with flag is_deleted=True) """ lecturers_data = [ - ("test_fname1", "test_lname1", "test_mname1", 0), - ("test_fname2", "test_lname2", "test_mname2", 1), - ("Bibka", "Bobka", "Bobkovich", 2), + ("test_fname1", "test_lname1", "test_mname1", 9900), + ("test_fname2", "test_lname2", "test_mname2", 9901), + ("Bibka", "Bobka", "Bobkovich", 9902), ] lecturers = [ diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 5939f57..03e7cbd 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -15,7 +15,7 @@ @pytest.mark.parametrize( - 'body,timetable_id,response_status', + 'body,lecturer_n,response_status', [ ( { @@ -63,8 +63,8 @@ ), ], ) -def test_create_comment(client, dbsession, lecturers, body, timetable_id, response_status): - params = {"lecturer_id": lecturers[timetable_id].id} +def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response_status): + params = {"lecturer_id": lecturers[lecturer_n].id} post_response = client.post(url, json=body, params=params) assert post_response.status_code == response_status if response_status == status.HTTP_200_OK: @@ -72,7 +72,7 @@ def test_create_comment(client, dbsession, lecturers, body, timetable_id, respon assert comment is not None user_comment = ( LecturerUserComment.query(session=dbsession) - .filter(LecturerUserComment.lecturer_id == lecturers[timetable_id].id) + .filter(LecturerUserComment.lecturer_id == lecturers[lecturer_n].id) .one_or_none() ) assert user_comment is not None @@ -87,38 +87,42 @@ def test_get_comment(client, comment): @pytest.mark.parametrize( - 'timetable_id,response_status', [(0, status.HTTP_200_OK), (1, status.HTTP_200_OK), (3, status.HTTP_200_OK)] + 'lecturer_n,response_status', [(0, status.HTTP_200_OK), (1, status.HTTP_200_OK), (3, status.HTTP_200_OK)] ) -def test_comments_by_lecturer_id(client, lecturers_with_comments, timetable_id, response_status): +def test_comments_by_lecturer_id(client, lecturers_with_comments, lecturer_n, response_status): lecturers, comments = lecturers_with_comments - response = client.get(f'{url}', params={"lecturer_id": lecturers[timetable_id].id}) + response = client.get(f'{url}', params={"lecturer_id": lecturers[lecturer_n].id}) assert response.status_code == response_status if response.status_code == status.HTTP_200_OK: json_response = response.json() assert len(json_response["comments"]) == len( [ comment - for comment in lecturers[timetable_id].comments + for comment in lecturers[lecturer_n].comments if comment.review_status == ReviewStatus.APPROVED and not comment.is_deleted ] ) @pytest.mark.parametrize( - 'review_status, response_status', + 'review_status, response_status,is_reviewed', [ - ("approved", status.HTTP_200_OK), - ("dismissed", status.HTTP_200_OK), - ("wrong_status", status.HTTP_422_UNPROCESSABLE_ENTITY), + ("approved", status.HTTP_200_OK, True), + ("approved", status.HTTP_200_OK, False), + ("dismissed", status.HTTP_200_OK, True), + ("dismissed", status.HTTP_200_OK, False), + ("wrong_status", status.HTTP_422_UNPROCESSABLE_ENTITY, True), + ("wrong_status", status.HTTP_422_UNPROCESSABLE_ENTITY, False), ], ) -def test_review_comment(client, dbsession, unreviewed_comment, review_status, response_status): +def test_review_comment(client, dbsession, unreviewed_comment, comment, review_status, response_status, is_reviewed): + commment_to_reivew = comment if is_reviewed else unreviewed_comment query = {"review_status": review_status} - response = client.patch(f"{url}/{unreviewed_comment.uuid}", params=query) + response = client.patch(f"{url}/{commment_to_reivew.uuid}", params=query) assert response.status_code == response_status if response.status_code == status.HTTP_200_OK: - dbsession.refresh(unreviewed_comment) - assert unreviewed_comment.review_status == ReviewStatus(review_status) + dbsession.refresh(commment_to_reivew) + assert commment_to_reivew.review_status == ReviewStatus(review_status) def test_delete_comment(client, dbsession, comment): diff --git a/tests/test_routes/test_lecturer.py b/tests/test_routes/test_lecturer.py index 52c251b..81b90cb 100644 --- a/tests/test_routes/test_lecturer.py +++ b/tests/test_routes/test_lecturer.py @@ -29,7 +29,7 @@ def test_create_lecturer(client, dbsession, response_status): @pytest.mark.parametrize( - 'timetable_id,response_status', + 'lecturer_n,response_status', [ (0, status.HTTP_200_OK), (1, status.HTTP_200_OK), @@ -37,8 +37,10 @@ def test_create_lecturer(client, dbsession, response_status): (3, status.HTTP_404_NOT_FOUND), ], ) -def test_get_lecturer(client, dbsession, lecturers, timetable_id, response_status): - lecturer = dbsession.query(Lecturer).filter(Lecturer.timetable_id == timetable_id).one_or_none() +def test_get_lecturer(client, dbsession, lecturers, lecturer_n, response_status): + lecturer = ( + dbsession.query(Lecturer).filter(Lecturer.timetable_id == lecturers[lecturer_n].timetable_id).one_or_none() + ) # check non-existing id request lecturer_id = -1 if lecturer: @@ -55,24 +57,24 @@ def test_get_lecturer(client, dbsession, lecturers, timetable_id, response_statu @pytest.mark.parametrize( - 'timetable_id,mark_kindness,mark_freebie,mark_clarity,mark_general', + 'lecturer_n,mark_kindness,mark_freebie,mark_clarity,mark_general', [(0, 1.5, 1.5, 1.5, 1.5), (1, 0, 0, 0, 0), (2, 0.5, 0.5, 0.5, 0.5)], ) def test_get_lecturer_with_comments( - client, lecturers_with_comments, timetable_id, mark_kindness, mark_freebie, mark_clarity, mark_general + client, lecturers_with_comments, lecturer_n, mark_kindness, mark_freebie, mark_clarity, mark_general ): lecturers, comments = lecturers_with_comments query = {"info": ['comments', 'mark']} - response = client.get(f'{url}/{lecturers[timetable_id].id}', params=query) + response = client.get(f'{url}/{lecturers[lecturer_n].id}', params=query) assert response.status_code == status.HTTP_200_OK json_response = response.json() assert json_response["mark_kindness"] == mark_kindness assert json_response["mark_freebie"] == mark_freebie assert json_response["mark_clarity"] == mark_clarity assert json_response["mark_general"] == mark_general - assert "test_subject" in json_response["subjects"] - assert "test_subject1" in json_response["subjects"] - assert "test_subject2" not in json_response["subjects"] + assert comments[lecturer_n * 4 + 0].subject in json_response["subjects"] + assert comments[lecturer_n * 4 + 1].subject in json_response["subjects"] + assert comments[lecturer_n * 4 + 2].subject not in json_response["subjects"] assert len(json_response["comments"]) == 2 @@ -80,6 +82,7 @@ def test_get_lecturer_with_comments( 'query,total,response_status', [ ({'name': 'test_lname1'}, 1, status.HTTP_200_OK), + ({'name': 'TeSt_LnAmE1'}, 1, status.HTTP_200_OK), ({'name': 'test'}, 2, status.HTTP_200_OK), ({'name': 'testlname123'}, 0, status.HTTP_404_NOT_FOUND), ], @@ -126,9 +129,9 @@ def test_update_lecturer(client, lecturer, body, response_status): response = client.get(f'{url}/{lecturer.id}') assert response.status_code == status.HTTP_200_OK json_response = response.json() - assert json_response['first_name'] == 'test_fname' - assert json_response['last_name'] == 'test_lname' - assert json_response['middle_name'] == 'test_mname' + assert json_response['first_name'] == lecturer.first_name + assert json_response['last_name'] == lecturer.last_name + assert json_response['middle_name'] == lecturer.middle_name response = client.patch(f"{url}/{lecturer.id}", json=body) assert response.status_code == response_status if response_status == status.HTTP_200_OK: From ffba3b58c575f001f45f47caa696306f62129c3b Mon Sep 17 00:00:00 2001 From: default Date: Fri, 15 Nov 2024 15:19:10 +0000 Subject: [PATCH 09/23] redo create comment for deleted lecturer check --- tests/test_routes/test_comment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 03e7cbd..44c3462 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -54,12 +54,12 @@ { "subject": "test_subject", "text": "test_text", - "mark_kindness": 5, + "mark_kindness": 1, "mark_freebie": -2, "mark_clarity": 0, }, 3, - status.HTTP_400_BAD_REQUEST, + status.HTTP_404_NOT_FOUND, ), ], ) From 94e7cb8f27097ca574dbf079f3b3d2e7924808db Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sat, 16 Nov 2024 19:51:27 +0000 Subject: [PATCH 10/23] added tests and refactoring --- rating_api/routes/comment.py | 19 +++++++------ tests/test_routes/test_comment.py | 44 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 8f52ba9..03067a3 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -23,36 +23,39 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen Scopes: `["rating.comment.review"]` Создает комментарий к преподавателю в базе данных RatingAPI Для создания комментария нужно быть авторизованным + + Скоуп нужен для создания комментариев с заданым timestamp """ lecturer = Lecturer.get(session=db.session, id=lecturer_id) if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) + current_time = datetime.datetime.utcnow() if comment_info.create_ts or comment_info.update_ts: - if not "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + if "rating.comment.review" not in [scope['name'] for scope in user.get('session_scopes')]: raise ForbiddenAction(Comment) else: if not comment_info.create_ts: - comment_info.create_ts = datetime.datetime.utcnow() + comment_info.create_ts = current_time if not comment_info.update_ts: - comment_info.update_ts = datetime.datetime.utcnow() + comment_info.update_ts = current_time else: - comment_info.create_ts = datetime.datetime.utcnow() - comment_info.update_ts = datetime.datetime.utcnow() + comment_info.create_ts = current_time + comment_info.update_ts = current_time user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() ) - if not "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + if "rating.comment.review" not in [scope['name'] for scope in user.get('session_scopes')]: for user_comment in user_comments: - if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( + if current_time - user_comment.update_ts < datetime.timedelta( minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES ): raise TooManyCommentRequests( dtime=user_comment.update_ts + datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES) - - datetime.datetime.utcnow() + - current_time ) LecturerUserComment.create(session=db.session, lecturer_id=lecturer_id, user_id=user.get('id')) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 44c3462..890fd1d 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -1,5 +1,6 @@ import logging import uuid +import datetime import pytest from starlette import status @@ -61,6 +62,43 @@ 3, status.HTTP_404_NOT_FOUND, ), + ( + { + "subject": "test_subject", + "text": "test_text", + "create_ts": "2077-11-16T19:15:27.306Z", + "update_ts": "2077-11-16T19:15:27.306Z", + "mark_kindness": 1, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 0, + status.HTTP_200_OK, + ), + ( + { + "subject": "test_subject", + "text": "test_text", + "update_ts": "2077-11-16T19:15:27.306Z", + "mark_kindness": 1, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 0, + status.HTTP_200_OK, + ), + ( + { + "subject": "test_subject", + "text": "test_text", + "create_ts": "2077-11-16T19:15:27.306Z", + "mark_kindness": 1, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 0, + status.HTTP_200_OK, + ) ], ) def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response_status): @@ -70,6 +108,12 @@ def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response if response_status == status.HTTP_200_OK: comment = Comment.query(session=dbsession).filter(Comment.uuid == post_response.json()["uuid"]).one_or_none() assert comment is not None + + if "create_ts" in body: + assert comment.create_ts == datetime.datetime.fromisoformat(body["create_ts"]).replace(tzinfo=None) + if "update_ts" in body: + assert comment.update_ts == datetime.datetime.fromisoformat(body["update_ts"]).replace(tzinfo=None) + user_comment = ( LecturerUserComment.query(session=dbsession) .filter(LecturerUserComment.lecturer_id == lecturers[lecturer_n].id) From 6dce58b062abdd3435383aca34c2b8d38531d28b Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sat, 16 Nov 2024 19:54:35 +0000 Subject: [PATCH 11/23] linting --- tests/test_routes/test_comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 890fd1d..4fe8b89 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -1,6 +1,6 @@ +import datetime import logging import uuid -import datetime import pytest from starlette import status From e0786dbd64bdd5efc0b916a58314308cd1d7233f Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sat, 16 Nov 2024 19:56:15 +0000 Subject: [PATCH 12/23] linting part two --- tests/test_routes/test_comment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 4fe8b89..3ef6fab 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -98,7 +98,7 @@ }, 0, status.HTTP_200_OK, - ) + ), ], ) def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response_status): @@ -108,12 +108,12 @@ def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response if response_status == status.HTTP_200_OK: comment = Comment.query(session=dbsession).filter(Comment.uuid == post_response.json()["uuid"]).one_or_none() assert comment is not None - + if "create_ts" in body: assert comment.create_ts == datetime.datetime.fromisoformat(body["create_ts"]).replace(tzinfo=None) if "update_ts" in body: assert comment.update_ts == datetime.datetime.fromisoformat(body["update_ts"]).replace(tzinfo=None) - + user_comment = ( LecturerUserComment.query(session=dbsession) .filter(LecturerUserComment.lecturer_id == lecturers[lecturer_n].id) From 987636ee4a62d9e16872a1d64fc38ba0d8d89105 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 17 Nov 2024 10:40:21 +0000 Subject: [PATCH 13/23] refactoring --- rating_api/routes/comment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 03067a3..8e61e73 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -31,8 +31,9 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen raise ObjectNotFound(Lecturer, lecturer_id) current_time = datetime.datetime.utcnow() + has_review_scope = "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')] if comment_info.create_ts or comment_info.update_ts: - if "rating.comment.review" not in [scope['name'] for scope in user.get('session_scopes')]: + if not has_review_scope: raise ForbiddenAction(Comment) else: if not comment_info.create_ts: @@ -47,7 +48,7 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() ) - if "rating.comment.review" not in [scope['name'] for scope in user.get('session_scopes')]: + if not has_review_scope: for user_comment in user_comments: if current_time - user_comment.update_ts < datetime.timedelta( minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES From 223c1537888fc38d10439ce88c7139aaebff89e5 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 17 Nov 2024 15:25:33 +0000 Subject: [PATCH 14/23] optimization --- rating_api/routes/comment.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 8e61e73..252dc08 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -32,16 +32,12 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen current_time = datetime.datetime.utcnow() has_review_scope = "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')] - if comment_info.create_ts or comment_info.update_ts: - if not has_review_scope: - raise ForbiddenAction(Comment) - else: - if not comment_info.create_ts: - comment_info.create_ts = current_time - if not comment_info.update_ts: - comment_info.update_ts = current_time - else: + if (comment_info.create_ts or comment_info.update_ts) and not has_review_scope: + raise ForbiddenAction(Comment) + + if not comment_info.create_ts: comment_info.create_ts = current_time + if not comment_info.update_ts: comment_info.update_ts = current_time user_comments: list[LecturerUserComment] = ( From d96ca90f9749238014316821065eb41c2127aa53 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Mon, 18 Nov 2024 00:48:22 +0000 Subject: [PATCH 15/23] lint --- rating_api/routes/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 252dc08..7d50a8f 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -34,7 +34,7 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen has_review_scope = "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_review_scope: raise ForbiddenAction(Comment) - + if not comment_info.create_ts: comment_info.create_ts = current_time if not comment_info.update_ts: From 3045a5f8206ba40be968feae07dfc3876dd7a448 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Mon, 18 Nov 2024 19:40:21 +0000 Subject: [PATCH 16/23] added wrong date test case --- tests/test_routes/test_comment.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 3ef6fab..981a1a9 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -99,6 +99,18 @@ 0, status.HTTP_200_OK, ), + ( # wrong date + { + "subject": "test_subject", + "text": "test_text", + "create_ts": "wasd", + "mark_kindness": 1, + "mark_freebie": -2, + "mark_clarity": 0, + }, + 0, + status.HTTP_422_UNPROCESSABLE_ENTITY, + ), ], ) def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response_status): From c7391d7edd7eb0fb1a00435e4cba19bfc3f49380 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Fri, 22 Nov 2024 18:28:26 +0000 Subject: [PATCH 17/23] reverted current_time and edited doc --- rating_api/routes/comment.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 7d50a8f..fff81d0 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -24,21 +24,20 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen Создает комментарий к преподавателю в базе данных RatingAPI Для создания комментария нужно быть авторизованным - Скоуп нужен для создания комментариев с заданым timestamp + Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.review"] """ lecturer = Lecturer.get(session=db.session, id=lecturer_id) if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) - current_time = datetime.datetime.utcnow() has_review_scope = "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_review_scope: raise ForbiddenAction(Comment) if not comment_info.create_ts: - comment_info.create_ts = current_time + comment_info.create_ts = datetime.datetime.utcnow() if not comment_info.update_ts: - comment_info.update_ts = current_time + comment_info.update_ts = datetime.datetime.utcnow() user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() @@ -46,13 +45,13 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen if not has_review_scope: for user_comment in user_comments: - if current_time - user_comment.update_ts < datetime.timedelta( + if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES ): raise TooManyCommentRequests( dtime=user_comment.update_ts + datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES) - - current_time + - datetime.datetime.utcnow() ) LecturerUserComment.create(session=db.session, lecturer_id=lecturer_id, user_id=user.get('id')) From 7f3291855e3697a23c103a050cd53119380c83a4 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 14:42:15 +0000 Subject: [PATCH 18/23] changed scope and removed redundant checks --- rating_api/routes/comment.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index fff81d0..bd736a3 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -20,30 +20,25 @@ @comment.post("", response_model=CommentGet) async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depends(UnionAuth())) -> CommentGet: """ - Scopes: `["rating.comment.review"]` + Scopes: `["rating.comment.create"]` Создает комментарий к преподавателю в базе данных RatingAPI Для создания комментария нужно быть авторизованным - Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.review"] + Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.create"] """ lecturer = Lecturer.get(session=db.session, id=lecturer_id) if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) - has_review_scope = "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')] - if (comment_info.create_ts or comment_info.update_ts) and not has_review_scope: + has_create_scope = "rating.comment.create" in [scope['name'] for scope in user.get('session_scopes')] + if (comment_info.create_ts or comment_info.update_ts) and not has_create_scope: raise ForbiddenAction(Comment) - if not comment_info.create_ts: - comment_info.create_ts = datetime.datetime.utcnow() - if not comment_info.update_ts: - comment_info.update_ts = datetime.datetime.utcnow() - user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() ) - if not has_review_scope: + if not has_create_scope: for user_comment in user_comments: if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES From e174ee4d27a71b015d80156b84a41ea99149ecd0 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 14:58:59 +0000 Subject: [PATCH 19/23] lint --- tests/test_routes/test_comment.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index 8bcfdec..d093d92 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -102,7 +102,6 @@ 0, status.HTTP_200_OK, ), - ( { "subject": "test_subject", From 5a3d43c01587e9b7b2359ad3001f25c9296e5153 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 17:11:54 +0000 Subject: [PATCH 20/23] changed scope name --- rating_api/routes/comment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 3071c37..cd7dc81 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -20,17 +20,17 @@ @comment.post("", response_model=CommentGet) async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depends(UnionAuth())) -> CommentGet: """ - Scopes: `["rating.comment.create"]` + Scopes: `["rating.comment.import"]` Создает комментарий к преподавателю в базе данных RatingAPI Для создания комментария нужно быть авторизованным - Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.create"] + Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.import"] """ lecturer = Lecturer.get(session=db.session, id=lecturer_id) if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) - has_create_scope = "rating.comment.create" in [scope['name'] for scope in user.get('session_scopes')] + has_create_scope = "rating.comment.import" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_create_scope: raise ForbiddenAction(Comment) From 08e5d57e32be41755d5e18ef4688c4606fc71d1a Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 21:37:29 +0000 Subject: [PATCH 21/23] moved db query --- rating_api/routes/comment.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index cd7dc81..9702393 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -26,13 +26,14 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.import"] """ - lecturer = Lecturer.get(session=db.session, id=lecturer_id) - if not lecturer: - raise ObjectNotFound(Lecturer, lecturer_id) has_create_scope = "rating.comment.import" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_create_scope: raise ForbiddenAction(Comment) + + lecturer = Lecturer.get(session=db.session, id=lecturer_id) + if not lecturer: + raise ObjectNotFound(Lecturer, lecturer_id) user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() From 59c484f5d604a675f438e201af9ef2aebf819c96 Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 21:46:17 +0000 Subject: [PATCH 22/23] fix --- rating_api/routes/comment.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 9702393..3a92c6c 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -30,16 +30,15 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen has_create_scope = "rating.comment.import" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_create_scope: raise ForbiddenAction(Comment) - + lecturer = Lecturer.get(session=db.session, id=lecturer_id) if not lecturer: raise ObjectNotFound(Lecturer, lecturer_id) - user_comments: list[LecturerUserComment] = ( - LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() - ) - if not has_create_scope: + user_comments: list[LecturerUserComment] = ( + LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all() + ) for user_comment in user_comments: if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta( minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES From 91a022d59c6cf0422d549472c77ef389a777993f Mon Sep 17 00:00:00 2001 From: Timofeev Nikita Date: Sun, 24 Nov 2024 21:52:04 +0000 Subject: [PATCH 23/23] fix 2 --- rating_api/routes/comment.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 3a92c6c..1962433 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -26,15 +26,14 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.import"] """ + lecturer = Lecturer.get(session=db.session, id=lecturer_id) + if not lecturer: + raise ObjectNotFound(Lecturer, lecturer_id) has_create_scope = "rating.comment.import" in [scope['name'] for scope in user.get('session_scopes')] if (comment_info.create_ts or comment_info.update_ts) and not has_create_scope: raise ForbiddenAction(Comment) - lecturer = Lecturer.get(session=db.session, id=lecturer_id) - if not lecturer: - raise ObjectNotFound(Lecturer, lecturer_id) - if not has_create_scope: user_comments: list[LecturerUserComment] = ( LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all()