From 544460d60f4c423c5149a2da06512010ca322a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Tue, 13 Aug 2024 22:20:34 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20Question?= =?UTF-8?q?=20-=20=D1=87=D0=B0=D1=81=D1=82=D0=BE=20=D0=B7=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D0=B2=D0=B0=D0=B5=D0=BC=D1=8B=D0=B5=20=D0=B2=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/__init__.py | 1 + app/admin/base.py | 8 ++- app/admin/question.py | 17 +++++++ app/api/endpoints/__init__.py | 1 + app/api/endpoints/question.py | 47 ++++++++++++++++++ app/api/routers.py | 7 ++- app/api_docs_responses/question.py | 80 ++++++++++++++++++++++++++++++ app/api_docs_responses/register.py | 1 - app/crud/__init__.py | 1 + app/crud/question.py | 9 ++++ app/models/__init__.py | 1 + app/models/question.py | 14 ++++++ app/schemas/question.py | 12 +++++ 13 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 app/admin/question.py create mode 100644 app/api/endpoints/question.py create mode 100644 app/api_docs_responses/question.py create mode 100644 app/crud/question.py create mode 100644 app/models/question.py create mode 100644 app/schemas/question.py diff --git a/app/admin/__init__.py b/app/admin/__init__.py index 3b996c5..523f4a6 100644 --- a/app/admin/__init__.py +++ b/app/admin/__init__.py @@ -9,6 +9,7 @@ SubscriptionAdmin, TasksAdmin, ) from app.admin.profile import ProfileAdmin # noqa +from app.admin.question import QuestionAdmin # noqa from app.admin.tariff import TariffAdmin # noqa from app.admin.task import TaskAdmin # noqa from app.admin.user import UserAdmin # noqa diff --git a/app/admin/base.py b/app/admin/base.py index 75f2b6e..1ccd03b 100644 --- a/app/admin/base.py +++ b/app/admin/base.py @@ -2,8 +2,9 @@ AchievementAdmin, AchievementsAdmin, AuthAdmin, CommonAdmin, ContactsAdmin, CourseAdmin, ErrorsAdmin, ExaminationAdmin, GroupAdmin, HeaderAdmin, HelpAdmin, LocaleAdmin, MainAdmin, MonthsAdmin, ProfileAdmin, - ProfileUserAdmin, QuestionBannerAdmin, RestoreAdmin, SecureAdmin, - SubscriptionAdmin, TariffAdmin, TaskAdmin, TasksAdmin, UserAdmin, + ProfileUserAdmin, QuestionAdmin, QuestionBannerAdmin, RestoreAdmin, + SecureAdmin, SubscriptionAdmin, TariffAdmin, TaskAdmin, TasksAdmin, + UserAdmin, ) admin_models = [ @@ -21,6 +22,9 @@ HelpAdmin, MainAdmin, RestoreAdmin, SubscriptionAdmin, ProfileUserAdmin, SecureAdmin, AchievementsAdmin, TasksAdmin, QuestionBannerAdmin, ErrorsAdmin, MonthsAdmin, + + # category = 'HELP' + QuestionAdmin, ] diff --git a/app/admin/question.py b/app/admin/question.py new file mode 100644 index 0000000..44d9989 --- /dev/null +++ b/app/admin/question.py @@ -0,0 +1,17 @@ +from sqladmin import ModelView + +from app.models import Question + + +class QuestionAdmin(ModelView, model=Question): + column_list = [Question.id, Question.problem] + column_searchable_list = [Question.problem] + can_create = True + can_edit = True + can_delete = True + can_view_details = True + name = 'Question' + name_plural = 'Questions' + icon = 'fa-solid fa-question' + category = 'HELP' + column_formatters = {Question.problem: lambda m, a: m.problem[:50]} diff --git a/app/api/endpoints/__init__.py b/app/api/endpoints/__init__.py index 36f0a86..a8fa90d 100644 --- a/app/api/endpoints/__init__.py +++ b/app/api/endpoints/__init__.py @@ -5,6 +5,7 @@ from .locale import router as locale_router # noqa from .notification import router as notification_router # noqa from .profile import router as profile_router # noqa +from .question import router as question_router # noqa from .tariff import router as tariff_router # noqa from .task import router as task_router # noqa from .user import router as user_router # noqa diff --git a/app/api/endpoints/question.py b/app/api/endpoints/question.py new file mode 100644 index 0000000..16a4a41 --- /dev/null +++ b/app/api/endpoints/question.py @@ -0,0 +1,47 @@ +from fastapi import APIRouter, Depends, Path +from sqlalchemy.ext.asyncio import AsyncSession +from typing_extensions import Annotated + +from app.api.validators import check_obj_exists +from app.api_docs_responses.question import GET_QUESTION, GET_QUESTIONS +from app.core.db import get_async_session +from app.crud import question_crud +from app.schemas.question import QuestionRead + +router = APIRouter() + + +@router.get( + '/', + response_model=list[QuestionRead], + **GET_QUESTIONS, +) +async def get_all_questions( + session: AsyncSession = Depends(get_async_session), +) -> list[QuestionRead]: + """ + Получение всех часто задаваемых вопросов, которые есть в БД. + """ + return await question_crud.get_multi(session) + + +@router.get( + '/{question_id}', + response_model=QuestionRead, + **GET_QUESTION, +) +async def get_question( + question_id: Annotated[int, Path(ge=0)], + session: AsyncSession = Depends(get_async_session) +): + """ + Получение вопроса по его id или + получение ошибки 404 в случае отсутствия данного вопроса. + """ + obj = await question_crud.get_by_attr( + attr_name='id', + attr_value=question_id, + session=session, + ) + await check_obj_exists(obj=obj) + return obj diff --git a/app/api/routers.py b/app/api/routers.py index b66e6a4..3f52499 100644 --- a/app/api/routers.py +++ b/app/api/routers.py @@ -2,8 +2,8 @@ from app.api.endpoints import ( achievement_router, course_router, examination_router, group_router, - locale_router, notification_router, profile_router, tariff_router, - task_router, user_router, + locale_router, notification_router, profile_router, question_router, + tariff_router, task_router, user_router, ) main_router = APIRouter() @@ -22,6 +22,9 @@ main_router.include_router( profile_router, prefix='/profiles', tags=['Profiles'] ) +main_router.include_router( + question_router, prefix='/questions', tags=['Questions'] +) main_router.include_router( locale_router, prefix='/locales', tags=['Locales'] ) diff --git a/app/api_docs_responses/question.py b/app/api_docs_responses/question.py new file mode 100644 index 0000000..bca5bbf --- /dev/null +++ b/app/api_docs_responses/question.py @@ -0,0 +1,80 @@ +from app.api_docs_responses.utils_docs import get_200_docs, get_404_docs + +questions_with_all_fields = { + 'id': 0, + 'problem': 'Описание проблемы', + 'solution': 'Решение проблемы', +} + +questions_without_sulution = { + 'id': 0, + 'problem': 'Описание проблемы', +} + +content_questions = { + 'application/json': { + 'examples': { + 'questions_with_all_fields': { + 'summary': 'Вопрос со всеми заполненными полями', + 'value': [questions_with_all_fields], + }, + 'questions_without_sulution': { + 'summary': 'Вопрос без решения', + 'value': [questions_without_sulution], + } + } + } +} + +content_question = { + 'application/json': { + 'examples': { + 'question_with_all_fields': { + 'summary': 'Вопрос со всеми заполненными полями', + 'value': questions_with_all_fields, + }, + 'question_without_sulution': { + 'summary': 'Вопрос без решения', + 'value': questions_without_sulution, + } + } + } +} + +get_questions_response = { + **get_200_docs(content_questions), +} + +get_question_response = { + **get_200_docs(content_question), + **get_404_docs('Объект question не найден.'), +} + + +GET_QUESTIONS = dict( + responses=get_questions_response, + summary='Получение всего списка часто задаваемых вопросов', + description=""" + ## Получение всех часто задаваемых вопросов. + + Permissions: + - Для всех пользователей. + + Returns: + - HTTP 200 OK: Если список часто задаваемых вопросов успешно получен. + """ +) + +GET_QUESTION = dict( + responses=get_question_response, + summary='Получение часто задаваемого вопроса по ID', + description=""" + ## Получение информации о вопросе по её идентификатору. + + Permissions: + - Для всех пользователей. + + Returns: + - HTTP 200 OK: Если вопрос успешно получен. + """ +) diff --git a/app/api_docs_responses/register.py b/app/api_docs_responses/register.py index c690a10..d2a82d6 100644 --- a/app/api_docs_responses/register.py +++ b/app/api_docs_responses/register.py @@ -2,7 +2,6 @@ from app.api_docs_responses.utils_docs import get_201_docs - REGISTER_SUMMARY = 'Регистрация нового пользователя.' CREATE_REGISTER = { diff --git a/app/crud/__init__.py b/app/crud/__init__.py index 64ba990..5d77c16 100644 --- a/app/crud/__init__.py +++ b/app/crud/__init__.py @@ -5,6 +5,7 @@ from .locale import locale_crud # noqa from .notification import notification_crud # noqa from .profile import profile_crud # noqa +from .question import question_crud # noqa from .tariff import tariff_crud # noqa from .task import task_crud # noqa from .user import user_crud # noqa diff --git a/app/crud/question.py b/app/crud/question.py new file mode 100644 index 0000000..8047f6b --- /dev/null +++ b/app/crud/question.py @@ -0,0 +1,9 @@ +from app.crud.base import CRUDBase +from app.models import Question + + +class CRUDQuestion(CRUDBase): + pass + + +question_crud = CRUDQuestion(Question) diff --git a/app/models/__init__.py b/app/models/__init__.py index a91a5c0..93c4f97 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -8,6 +8,7 @@ ) from .notification import Notification # noqa from .profile import Profile # noqa +from .question import Question # noqa from .tariff import Tariff # noqa from .task import Task # noqa from .user import User # noqa diff --git a/app/models/question.py b/app/models/question.py new file mode 100644 index 0000000..fdae99e --- /dev/null +++ b/app/models/question.py @@ -0,0 +1,14 @@ +from sqlalchemy import Column, String, Text + +from app.core.config import settings +from app.core.db import Base + + +class Question(Base): + problem: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) + solution: str = Column(Text) + + def __repr__(self): + return self.name diff --git a/app/schemas/question.py b/app/schemas/question.py new file mode 100644 index 0000000..fc602cc --- /dev/null +++ b/app/schemas/question.py @@ -0,0 +1,12 @@ +from typing import Optional + +from pydantic import BaseModel + + +class QuestionRead(BaseModel): + id: int + problem: Optional[str] + solution: Optional[str] + + class Config: + from_attributes = True From e4fdb2fee0fd345f8f14edd83a003a62e99436ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Tue, 13 Aug 2024 22:21:22 +0300 Subject: [PATCH 2/6] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BA=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20Question?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 1 + tests/fixtures/question.py | 20 ++++++ tests/test_question.py | 140 +++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 tests/fixtures/question.py create mode 100644 tests/test_question.py diff --git a/tests/conftest.py b/tests/conftest.py index 349157f..04f22b1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,7 @@ f'{fixtures}.profile', f'{fixtures}.group', f'{fixtures}.achievement', + f'{fixtures}.question', f'{fixtures}.tariff', f'{fixtures}.task', f'{fixtures}.course', diff --git a/tests/fixtures/question.py b/tests/fixtures/question.py new file mode 100644 index 0000000..0dc5d3e --- /dev/null +++ b/tests/fixtures/question.py @@ -0,0 +1,20 @@ +import pytest_asyncio +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models import Question + +TEST_QUESTION_COUNT = 6 + + +@pytest_asyncio.fixture +async def moc_questions( + db_session: AsyncSession +) -> None: + moc_questions = [ + Question( + problem=f'Problem_{i}', + solution=f'Solution of Problem_{i}' + ) for i in range(TEST_QUESTION_COUNT) + ] + db_session.add_all(moc_questions) + await db_session.commit() diff --git a/tests/test_question.py b/tests/test_question.py new file mode 100644 index 0000000..3084c07 --- /dev/null +++ b/tests/test_question.py @@ -0,0 +1,140 @@ +from fastapi import status +from fastapi.testclient import TestClient +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models import Question + +from .fixtures.question import TEST_QUESTION_COUNT +from .utils import get_obj_count + + +class TestGetQuestion: + async def test_get_questions_nonauth( + self, + moc_questions, + db_session: AsyncSession, + new_client: TestClient + ): + """Тест получения всех часто задаваемых вопросов.""" + questions_count = await get_obj_count(Question, db_session) + response = await new_client.get('/questions/') + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == questions_count + + async def test_get_question_by_id( + self, + moc_questions, + db_session: AsyncSession, + new_client: TestClient + ): + """Тест полученя часто задаваемого вопроса по id.""" + stmt = select(Question).where(Question.id == 1) + question = await db_session.execute(stmt) + question = question.scalar() + response = await new_client.get('/questions/1') + assert response.status_code == status.HTTP_200_OK + result = response.json() + assert result['id'] == question.id + + async def test_get_question_by_nonexist_id( + self, + moc_questions, + db_session: AsyncSession, + new_client: TestClient + ): + """Тест полученя вопроса по несуществующему id.""" + stmt = select(Question).where(Question.id == TEST_QUESTION_COUNT + 1) + question = await db_session.execute(stmt) + question = question.scalar() + response = await new_client.get( + f'/questions/{TEST_QUESTION_COUNT + 1}' + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + + +class TestCreateQuestion: + async def test_method_not_allowed_create_question_nonauth( + self, + moc_questions, + new_client: TestClient + ): + """Тест невозможности создания вопроса неавторизованным.""" + response = await new_client.post('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_create_question_user( + self, + moc_questions, + auth_client: TestClient + ): + """Тест невозможности создания вопроса юзером.""" + response = await auth_client.post('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_create_question_superuser( + self, + moc_questions, + auth_superuser: TestClient + ): + """Тест невозможности создания тарифа суперюзером.""" + response = await auth_superuser.post('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + +class TestUpdateQuestion: + async def test_method_not_allowed_update_question_nonauth( + self, + moc_questions, + new_client: TestClient + ): + """Тест невозможности апдейта вопроса неавторизованным.""" + response = await new_client.patch('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_update_question_user( + self, + moc_questions, + auth_client: TestClient + ): + """Тест невозможности апдейта вопроса юзером.""" + response = await auth_client.patch('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_update_question_superuser( + self, + moc_questions, + auth_superuser: TestClient + ): + """Тест невозможности апдейта тарифа суперюзером.""" + response = await auth_superuser.patch('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + +class TestDeleteQuestion: + async def test_method_not_allowed_delete_question_nonauth( + self, + moc_questions, + new_client: TestClient + ): + """Тест невозможности удаления вопроса неавторизованным.""" + response = await new_client.delete('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_delete_question_user( + self, + moc_questions, + auth_client: TestClient + ): + """Тест невозможности удаления вопроса юзером.""" + response = await auth_client.delete('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + + async def test_method_not_allowed_delete_question_superuser( + self, + moc_questions, + auth_superuser: TestClient + ): + """Тест невозможности удаления вопроса суперюзером.""" + response = await auth_superuser.delete('/questions/1') + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED From a4c7db9930897f5641170706f62413189f2b0359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Tue, 13 Aug 2024 22:25:04 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20Question?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../5a2d8eed5666_add_model_question.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 alembic/versions/5a2d8eed5666_add_model_question.py diff --git a/alembic/versions/5a2d8eed5666_add_model_question.py b/alembic/versions/5a2d8eed5666_add_model_question.py new file mode 100644 index 0000000..62c641e --- /dev/null +++ b/alembic/versions/5a2d8eed5666_add_model_question.py @@ -0,0 +1,42 @@ +"""add model Question + +Revision ID: 5a2d8eed5666 +Revises: 53fadd799e96 +Create Date: 2024-08-13 20:36:59.932459 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5a2d8eed5666' +down_revision = '53fadd799e96' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('question', + sa.Column('problem', sa.String(length=100), nullable=False), + sa.Column('solution', sa.Text(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('problem') + ) + with op.batch_alter_table('auth', schema=None) as batch_op: + batch_op.add_column(sa.Column('register_', sa.String(), nullable=False)) + batch_op.drop_column('register') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('auth', schema=None) as batch_op: + batch_op.add_column(sa.Column('register', sa.VARCHAR(), nullable=False)) + batch_op.drop_column('register_') + + op.drop_table('question') + # ### end Alembic commands ### From e5d53bee0f91e76a24afe59ae32294a0ca186010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Tue, 13 Aug 2024 22:44:09 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BD=D0=B5=D1=82=D0=BE=D1=87=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B2=20docs=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B0=20=D0=BF=D0=BE=20ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api_docs_responses/question.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api_docs_responses/question.py b/app/api_docs_responses/question.py index bca5bbf..fb774e2 100644 --- a/app/api_docs_responses/question.py +++ b/app/api_docs_responses/question.py @@ -69,12 +69,13 @@ responses=get_question_response, summary='Получение часто задаваемого вопроса по ID', description=""" - ## Получение информации о вопросе по её идентификатору. + ## Получение информации о вопросе по его идентификатору. Permissions: - Для всех пользователей. Returns: - HTTP 200 OK: Если вопрос успешно получен. + - HTTP 404 Not Found: Если вопрос с указанным `question_id` не существует. """ ) From e9885ae25a14b0a0e0d39782634aeb6731ccb0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Wed, 14 Aug 2024 08:29:22 +0300 Subject: [PATCH 5/6] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20question?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_question.py | 64 ++---------------------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) diff --git a/tests/test_question.py b/tests/test_question.py index 3084c07..5dab764 100644 --- a/tests/test_question.py +++ b/tests/test_question.py @@ -9,7 +9,7 @@ from .utils import get_obj_count -class TestGetQuestion: +class TestQuestion: async def test_get_questions_nonauth( self, moc_questions, @@ -52,17 +52,6 @@ async def test_get_question_by_nonexist_id( ) assert response.status_code == status.HTTP_404_NOT_FOUND - -class TestCreateQuestion: - async def test_method_not_allowed_create_question_nonauth( - self, - moc_questions, - new_client: TestClient - ): - """Тест невозможности создания вопроса неавторизованным.""" - response = await new_client.post('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - async def test_method_not_allowed_create_question_user( self, moc_questions, @@ -72,27 +61,7 @@ async def test_method_not_allowed_create_question_user( response = await auth_client.post('/questions/1') assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - async def test_method_not_allowed_create_question_superuser( - self, - moc_questions, - auth_superuser: TestClient - ): - """Тест невозможности создания тарифа суперюзером.""" - response = await auth_superuser.post('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - - -class TestUpdateQuestion: - async def test_method_not_allowed_update_question_nonauth( - self, - moc_questions, - new_client: TestClient - ): - """Тест невозможности апдейта вопроса неавторизованным.""" - response = await new_client.patch('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - - async def test_method_not_allowed_update_question_user( + async def test_method_not_allowed_update_question( self, moc_questions, auth_client: TestClient @@ -101,26 +70,6 @@ async def test_method_not_allowed_update_question_user( response = await auth_client.patch('/questions/1') assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - async def test_method_not_allowed_update_question_superuser( - self, - moc_questions, - auth_superuser: TestClient - ): - """Тест невозможности апдейта тарифа суперюзером.""" - response = await auth_superuser.patch('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - - -class TestDeleteQuestion: - async def test_method_not_allowed_delete_question_nonauth( - self, - moc_questions, - new_client: TestClient - ): - """Тест невозможности удаления вопроса неавторизованным.""" - response = await new_client.delete('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - async def test_method_not_allowed_delete_question_user( self, moc_questions, @@ -129,12 +78,3 @@ async def test_method_not_allowed_delete_question_user( """Тест невозможности удаления вопроса юзером.""" response = await auth_client.delete('/questions/1') assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED - - async def test_method_not_allowed_delete_question_superuser( - self, - moc_questions, - auth_superuser: TestClient - ): - """Тест невозможности удаления вопроса суперюзером.""" - response = await auth_superuser.delete('/questions/1') - assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED From 365bec5f12bde33d78319e07539a7d0d52824577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Wed, 14 Aug 2024 21:53:11 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=20?= =?UTF-8?q?=D0=BA=20=D1=87=D0=B0=D1=81=D1=82=D0=BE=20=D0=B7=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D0=B2=D0=B0=D0=B5=D0=BC=D1=8B=D0=BC=20=D0=B2=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B0=D0=BC=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D1=83=20=D0=B0?= =?UTF-8?q?=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D1=8E=D0=B7=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/question.py | 3 +++ app/api_docs_responses/question.py | 8 ++++++- tests/test_question.py | 37 ++++++++++++++++++++---------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/app/api/endpoints/question.py b/app/api/endpoints/question.py index 16a4a41..7c0e21c 100644 --- a/app/api/endpoints/question.py +++ b/app/api/endpoints/question.py @@ -5,6 +5,7 @@ from app.api.validators import check_obj_exists from app.api_docs_responses.question import GET_QUESTION, GET_QUESTIONS from app.core.db import get_async_session +from app.core.user import current_user from app.crud import question_crud from app.schemas.question import QuestionRead @@ -14,6 +15,7 @@ @router.get( '/', response_model=list[QuestionRead], + dependencies=[Depends(current_user)], **GET_QUESTIONS, ) async def get_all_questions( @@ -28,6 +30,7 @@ async def get_all_questions( @router.get( '/{question_id}', response_model=QuestionRead, + dependencies=[Depends(current_user)], **GET_QUESTION, ) async def get_question( diff --git a/app/api_docs_responses/question.py b/app/api_docs_responses/question.py index fb774e2..d172844 100644 --- a/app/api_docs_responses/question.py +++ b/app/api_docs_responses/question.py @@ -1,4 +1,6 @@ -from app.api_docs_responses.utils_docs import get_200_docs, get_404_docs +from app.api_docs_responses.utils_docs import ( + get_200_docs, get_401_docs, get_404_docs, +) questions_with_all_fields = { 'id': 0, @@ -43,10 +45,12 @@ get_questions_response = { **get_200_docs(content_questions), + **get_401_docs(content_questions), } get_question_response = { **get_200_docs(content_question), + **get_401_docs(content_questions), **get_404_docs('Объект question не найден.'), } @@ -62,6 +66,7 @@ Returns: - HTTP 200 OK: Если список часто задаваемых вопросов успешно получен. + - HTTP 401 Unauthorized: Если пользователь не авторизован. """ ) @@ -76,6 +81,7 @@ Returns: - HTTP 200 OK: Если вопрос успешно получен. + - HTTP 401 Unauthorized: Если пользователь не авторизован. - HTTP 404 Not Found: Если вопрос с указанным `question_id` не существует. """ ) diff --git a/tests/test_question.py b/tests/test_question.py index 5dab764..ec9c218 100644 --- a/tests/test_question.py +++ b/tests/test_question.py @@ -11,28 +11,44 @@ class TestQuestion: async def test_get_questions_nonauth( + self, + new_client: TestClient + ): + """Тест получения всех часто задаваемых вопросов неавторизованным.""" + response = await new_client.get('/questions/') + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + async def test_get_questions_user( self, moc_questions, db_session: AsyncSession, - new_client: TestClient + auth_client: TestClient ): """Тест получения всех часто задаваемых вопросов.""" questions_count = await get_obj_count(Question, db_session) - response = await new_client.get('/questions/') + response = await auth_client.get('/questions/') assert response.status_code == status.HTTP_200_OK assert len(response.json()) == questions_count - async def test_get_question_by_id( + async def test_get_question_by_id_nonauth( + self, + new_client: TestClient + ): + """Тест получения часто задаваемого вопроса по id неавторизованным.""" + response = await new_client.get('/questions/1') + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + async def test_get_question_by_id_user( self, moc_questions, db_session: AsyncSession, - new_client: TestClient + auth_client: TestClient ): - """Тест полученя часто задаваемого вопроса по id.""" + """Тест получения часто задаваемого вопроса по id юзером.""" stmt = select(Question).where(Question.id == 1) question = await db_session.execute(stmt) question = question.scalar() - response = await new_client.get('/questions/1') + response = await auth_client.get('/questions/1') assert response.status_code == status.HTTP_200_OK result = response.json() assert result['id'] == question.id @@ -41,20 +57,19 @@ async def test_get_question_by_nonexist_id( self, moc_questions, db_session: AsyncSession, - new_client: TestClient + auth_client: TestClient ): - """Тест полученя вопроса по несуществующему id.""" + """Тест полученя вопроса по несуществующему id юзером.""" stmt = select(Question).where(Question.id == TEST_QUESTION_COUNT + 1) question = await db_session.execute(stmt) question = question.scalar() - response = await new_client.get( + response = await auth_client.get( f'/questions/{TEST_QUESTION_COUNT + 1}' ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_method_not_allowed_create_question_user( self, - moc_questions, auth_client: TestClient ): """Тест невозможности создания вопроса юзером.""" @@ -63,7 +78,6 @@ async def test_method_not_allowed_create_question_user( async def test_method_not_allowed_update_question( self, - moc_questions, auth_client: TestClient ): """Тест невозможности апдейта вопроса юзером.""" @@ -72,7 +86,6 @@ async def test_method_not_allowed_update_question( async def test_method_not_allowed_delete_question_user( self, - moc_questions, auth_client: TestClient ): """Тест невозможности удаления вопроса юзером."""