From 25e8205423ab9cd977408a2f32a031409d0ee292 Mon Sep 17 00:00:00 2001 From: nemaego Date: Thu, 25 Jun 2026 13:24:38 +0400 Subject: [PATCH 1/2] YH-2129: add error handling to deleteQuestionMock --- .../__mocks__/helpers/getMockAuthProfile.ts | 7 ++ src/entities/auth/api/__mocks__/index.ts | 3 + src/entities/auth/index.ts | 2 + .../api/__mocks__/deleteQuestionMock.ts | 95 ++++++++++++++++--- 4 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 src/entities/auth/api/__mocks__/helpers/getMockAuthProfile.ts diff --git a/src/entities/auth/api/__mocks__/helpers/getMockAuthProfile.ts b/src/entities/auth/api/__mocks__/helpers/getMockAuthProfile.ts new file mode 100644 index 000000000..0dfdb92c0 --- /dev/null +++ b/src/entities/auth/api/__mocks__/helpers/getMockAuthProfile.ts @@ -0,0 +1,7 @@ +import { authMockProfilesByAccessToken } from '../data'; + +export const getMockAuthProfile = (request: Request) => { + const authorizationHeader = request.headers.get('Authorization') ?? ''; + const accessToken = authorizationHeader.replace(/^Bearer\s+/i, ''); + return authMockProfilesByAccessToken[accessToken]; +}; diff --git a/src/entities/auth/api/__mocks__/index.ts b/src/entities/auth/api/__mocks__/index.ts index 4b90c9eb2..f55bdd051 100644 --- a/src/entities/auth/api/__mocks__/index.ts +++ b/src/entities/auth/api/__mocks__/index.ts @@ -11,3 +11,6 @@ export const authHandlers = [ authRefreshQueryMock(), authSignupMutationMock(), ]; + +export { authMockProfilesByAccessToken } from './data'; +export { getMockAuthProfile } from './helpers/getMockAuthProfile'; diff --git a/src/entities/auth/index.ts b/src/entities/auth/index.ts index 129b9777f..3ba6009d5 100644 --- a/src/entities/auth/index.ts +++ b/src/entities/auth/index.ts @@ -26,3 +26,5 @@ export { authHandlers } from './api/__mocks__'; export { refreshMiddleware } from './api/refreshMiddleware'; export { RegistrationLabel } from './ui/RegistrationLabel/RegistrationLabel'; export { TelegramWidget } from './ui/TelegramWidget/TelegramWidget'; +export { authMockProfilesByAccessToken } from './api/__mocks__'; +export { getMockAuthProfile } from './api/__mocks__'; diff --git a/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts b/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts index cd74f6b3e..1453a34a9 100644 --- a/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts +++ b/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts @@ -1,20 +1,91 @@ -import { http } from 'msw'; +import { DefaultBodyType, http, HttpResponse } from 'msw'; +import { getMockAuthProfile } from '@/entities/auth'; +import { collectionsMock } from '@/entities/collection'; import { questionsMock } from '@/entities/question'; import { deleteQuestionApiUrls } from '../../model/constants/deleteQuestionConstants'; +import { DeleteQuestionError } from '../../model/types/deleteQuestionTypes'; -export const deleteQuestionMock = http.delete( - process.env.API_URL + deleteQuestionApiUrls.deleteQuestion, - ({ params }) => { - const questionId = Number(params.questionId); +export const deleteQuestionMock = http.delete< + { questionId: string }, + DefaultBodyType, + ApiErrorData +>(process.env.API_URL + deleteQuestionApiUrls.deleteQuestion, ({ params, request }) => { + const questionId = Number(params.questionId); - const index = questionsMock.data.findIndex((question) => question.id === questionId); + const profileMockResponse = getMockAuthProfile(request); - if (index !== 1) { - questionsMock.data.splice(index, 1); - } + if (!profileMockResponse) { + return HttpResponse.json( + { + message: 'auth.auth.unauthorized', + statusCode: 401, + description: 'Authentication failed', + }, + { status: 401 }, + ); + } - return new Response(); - }, -); + if (!profileMockResponse.isVerified) { + return HttpResponse.json( + { + message: 'auth.user.verified', + statusCode: 403, + description: 'Route is available for verified users!', + }, + { status: 403 }, + ); + } + + const currentQuestion = questionsMock.data.find((question) => question.id === questionId); + + if (!currentQuestion) { + return HttpResponse.json( + { + message: 'question.question.not_found', + statusCode: 404, + description: 'Question not found', + }, + { status: 404 }, + ); + } + + const isAdmin = profileMockResponse.userRoles.some((role) => role.name === 'admin'); + + const isOwner = currentQuestion.createdBy.id === profileMockResponse.id; + + if (!isAdmin && !isOwner) { + return HttpResponse.json( + { + message: 'auth.roles.author_can_change_only_own', + statusCode: 403, + description: 'Author can change only own data', + }, + { status: 403 }, + ); + } + + const hasQuestionInCollection = collectionsMock.data.some((collection) => + collection.questions?.some((question) => question.id === currentQuestion.id), + ); + + if (hasQuestionInCollection) { + return HttpResponse.json( + { + message: 'question.collection.question_in_collection', + statusCode: 409, + description: 'Question is in collection and cannot be deleted', + }, + { status: 409 }, + ); + } + + const questionIndex = questionsMock.data.findIndex((question) => question.id === questionId); + + if (questionIndex !== -1) { + questionsMock.data.splice(questionIndex, 1); + } + + return new HttpResponse(null, { status: 200 }); +}); From f95e6b06e0b069c63c755fe6230eefa01583599f Mon Sep 17 00:00:00 2001 From: nemaego Date: Wed, 1 Jul 2026 13:52:23 +0400 Subject: [PATCH 2/2] YH-2129: add author role check --- .../deleteQuestion/api/__mocks__/deleteQuestionMock.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts b/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts index 1453a34a9..dba165f77 100644 --- a/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts +++ b/src/features/question/deleteQuestion/api/__mocks__/deleteQuestionMock.ts @@ -52,10 +52,10 @@ export const deleteQuestionMock = http.delete< } const isAdmin = profileMockResponse.userRoles.some((role) => role.name === 'admin'); - + const isAuthor = profileMockResponse.userRoles.some((role) => role.name === 'author'); const isOwner = currentQuestion.createdBy.id === profileMockResponse.id; - if (!isAdmin && !isOwner) { + if (!isAdmin && !(isAuthor && isOwner)) { return HttpResponse.json( { message: 'auth.roles.author_can_change_only_own',