From c13a5a501bf2f7f220c87933b7473f77ec4df6f9 Mon Sep 17 00:00:00 2001 From: GrimJak Date: Fri, 30 May 2025 10:04:47 +0300 Subject: [PATCH 1/7] =?UTF-8?q?COMMENTS-PRIVATE:=20=D0=A1=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../priv/PrivateCommentController.java | 35 ++++++ .../main/service/CommentService.java | 9 ++ .../main/service/CommentServiceImpl.java | 59 +++++++++ .../priv/PrivateCommentControllerTest.java | 112 +++++++++++++++++ .../main/service/CommentServiceImplTest.java | 117 ++++++++++++++++++ 5 files changed, 332 insertions(+) create mode 100644 main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java create mode 100644 main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java create mode 100644 main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java create mode 100644 main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java create mode 100644 main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java new file mode 100644 index 0000000..bb48d41 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java @@ -0,0 +1,35 @@ +package ru.practicum.explorewithme.main.controller.priv; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.explorewithme.main.dto.CommentDto; +import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.service.CommentService; + +@RestController +@RequestMapping("/users/{userId}/comments") +@RequiredArgsConstructor +@Validated +@Slf4j +public class PrivateCommentController { + + private final CommentService commentService; + + @PostMapping + public ResponseEntity createComment( + @PathVariable @Positive Long userId, + @RequestParam @Positive Long eventId, + @Valid @RequestBody NewCommentDto newCommentDto) { + log.info("Создание нового комментария {} зарегистрированным пользователем c id {} " + + "к событию с id {}", newCommentDto, userId, eventId); + return ResponseEntity.status(HttpStatus.CREATED) + .body(commentService.addComment(userId, eventId, newCommentDto)); + } + +} diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java new file mode 100644 index 0000000..cea4985 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java @@ -0,0 +1,9 @@ +package ru.practicum.explorewithme.main.service; + +import ru.practicum.explorewithme.main.dto.CommentDto; +import ru.practicum.explorewithme.main.dto.NewCommentDto; + +public interface CommentService { + + CommentDto addComment(Long userId, Long eventId, NewCommentDto newCommentDto); +} diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java new file mode 100644 index 0000000..53bb597 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java @@ -0,0 +1,59 @@ +package ru.practicum.explorewithme.main.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.explorewithme.main.dto.CommentDto; +import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.error.BusinessRuleViolationException; +import ru.practicum.explorewithme.main.error.EntityNotFoundException; +import ru.practicum.explorewithme.main.mapper.CommentMapper; +import ru.practicum.explorewithme.main.model.Comment; +import ru.practicum.explorewithme.main.model.Event; +import ru.practicum.explorewithme.main.model.EventState; +import ru.practicum.explorewithme.main.model.User; +import ru.practicum.explorewithme.main.repository.CommentRepository; +import ru.practicum.explorewithme.main.repository.EventRepository; +import ru.practicum.explorewithme.main.repository.UserRepository; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class CommentServiceImpl implements CommentService { + + UserRepository userRepository; + EventRepository eventRepository; + CommentMapper commentMapper; + CommentRepository commentRepository; + + @Override + @Transactional + public CommentDto addComment(Long userId, Long eventId, NewCommentDto newCommentDto) { + + Optional user = userRepository.findById(userId); + if (user.isEmpty()) { + throw new EntityNotFoundException("Пользователь с id " + userId + " не найден"); + } + + Optional event = eventRepository.findById(eventId); + if (event.isEmpty()) { + throw new EntityNotFoundException("Событие с id " + eventId + " не найден"); + } + if (!event.get().getState().equals(EventState.PUBLISHED)) { + throw new BusinessRuleViolationException("Событие еще не опубликовано"); + } + if (!event.get().isCommentsEnabled()) { + throw new BusinessRuleViolationException("Комментарии запрещены"); + } + + Comment comment = commentMapper.toComment(newCommentDto); + + comment.setAuthor(user.get()); + comment.setEvent(event.get()); + + commentRepository.save(comment); + + return commentMapper.toDto(comment); + } +} diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java new file mode 100644 index 0000000..10ef509 --- /dev/null +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -0,0 +1,112 @@ +package ru.practicum.explorewithme.main.controller.priv; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.explorewithme.main.dto.CommentDto; +import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UserShortDto; +import ru.practicum.explorewithme.main.service.CommentService; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; // <-- для post-запроса +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +@WebMvcTest(PrivateCommentController.class) +public class PrivateCommentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private CommentService commentService; + + private ObjectMapper objectMapper; + + private final Long userId = 1L; + private final Long eventId = 100L; + + private NewCommentDto newCommentDto; + private CommentDto commentDto; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + + newCommentDto = NewCommentDto.builder() + .text("Test comment text") + .build(); + + UserShortDto author = UserShortDto.builder() + .id(2L) + .name("testUser") + .build(); + + commentDto = CommentDto.builder() + .id(10L) + .text(newCommentDto.getText()) + .author(author) + .eventId(eventId) + .createdOn(LocalDateTime.now()) + .updatedOn(LocalDateTime.now()) + .isEdited(false) + .build(); + } + + @Test + void createComment_whenValidInput_thenReturnsCreatedComment() throws Exception { + when(commentService.addComment(eq(userId), eq(eventId), any(NewCommentDto.class))) + .thenReturn(commentDto); + + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").value(commentDto.getId())) + .andExpect(jsonPath("$.text").value(commentDto.getText())) + .andExpect(jsonPath("$.eventId").value(eventId)) + .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) + .andExpect(jsonPath("$.author.name").value(commentDto.getAuthor().getName())) + .andExpect(jsonPath("$.isEdited").value(false)); + } + + @Test + void createComment_whenInvalidText_thenReturnsBadRequest() throws Exception { + NewCommentDto invalidDto = NewCommentDto.builder() + .text("") // некорректный: пустая строка + .build(); + + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void createComment_whenNegativeUserId_thenReturnsBadRequest() throws Exception { + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", -1, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void createComment_whenNegativeEventId_thenReturnsBadRequest() throws Exception { + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, -1) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isBadRequest()); + } + +} diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java new file mode 100644 index 0000000..b8f75a5 --- /dev/null +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java @@ -0,0 +1,117 @@ +package ru.practicum.explorewithme.main.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import ru.practicum.explorewithme.main.dto.CommentDto; +import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.error.BusinessRuleViolationException; +import ru.practicum.explorewithme.main.error.EntityNotFoundException; +import ru.practicum.explorewithme.main.mapper.CommentMapper; +import ru.practicum.explorewithme.main.model.Comment; +import ru.practicum.explorewithme.main.model.Event; +import ru.practicum.explorewithme.main.model.EventState; +import ru.practicum.explorewithme.main.model.User; +import ru.practicum.explorewithme.main.repository.CommentRepository; +import ru.practicum.explorewithme.main.repository.EventRepository; +import ru.practicum.explorewithme.main.repository.UserRepository; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CommentServiceImplTest { + + @Mock + private UserRepository userRepository; + @Mock + private EventRepository eventRepository; + @Mock + private CommentMapper commentMapper; + @Mock + private CommentRepository commentRepository; + + @InjectMocks + private CommentServiceImpl commentService; + + private long userId; + private long eventId; + private User user; + private Event event; + + @BeforeEach + void setUp() { + userId = 1L; + eventId = 2L; + user = new User(); + event = new Event(); + } + + @Test + void addComment_success() { + NewCommentDto newCommentDto = new NewCommentDto(); + event.setState(EventState.PUBLISHED); + event.setCommentsEnabled(true); + Comment comment = new Comment(); + CommentDto commentDto = new CommentDto(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + when(commentMapper.toComment(newCommentDto)).thenReturn(comment); + when(commentMapper.toDto(comment)).thenReturn(commentDto); + + CommentDto result = commentService.addComment(userId, eventId, newCommentDto); + + assertEquals(commentDto, result); + verify(commentRepository, times(1)).save(comment); + assertEquals(user, comment.getAuthor()); + assertEquals(event, comment.getEvent()); + } + + @Test + void addComment_userNotFound() { + when(userRepository.findById(userId)).thenReturn(Optional.empty()); + + EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, + () -> commentService.addComment(userId, 2L, new NewCommentDto())); + assertTrue(ex.getMessage().contains("Пользователь с id " + userId + " не найден")); + } + + @Test + void addComment_eventNotFound() { + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.empty()); + + EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertTrue(ex.getMessage().contains("Событие с id " + eventId + " не найден")); + } + + @Test + void addComment_eventNotPublished() { + event.setState(EventState.PENDING); // не опубликовано + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + + BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertEquals("Событие еще не опубликовано", ex.getMessage()); + } + + @Test + void addComment_commentsDisabled() { + event.setState(EventState.PUBLISHED); + event.setCommentsEnabled(false); // Комментарии запрещены + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + + BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertEquals("Комментарии запрещены", ex.getMessage()); + } +} \ No newline at end of file From a927c8f58fa63b8bf320176f1376261ac00d4f1d Mon Sep 17 00:00:00 2001 From: GrimJak Date: Fri, 30 May 2025 16:15:33 +0300 Subject: [PATCH 2/7] =?UTF-8?q?COMMENTS-PRIVATE:=20=D0=9E=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=80=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../priv/PrivateCommentController.java | 15 +++ .../main/service/CommentService.java | 3 + .../main/service/CommentServiceImpl.java | 39 +++++- .../priv/PrivateCommentControllerTest.java | 113 +++++++++++++++++- .../main/service/CommentServiceImplTest.java | 100 +++++++++++++++- 5 files changed, 265 insertions(+), 5 deletions(-) diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java index bb48d41..63658da 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*; import ru.practicum.explorewithme.main.dto.CommentDto; import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UpdateCommentDto; import ru.practicum.explorewithme.main.service.CommentService; @RestController @@ -26,10 +27,24 @@ public ResponseEntity createComment( @PathVariable @Positive Long userId, @RequestParam @Positive Long eventId, @Valid @RequestBody NewCommentDto newCommentDto) { + log.info("Создание нового комментария {} зарегистрированным пользователем c id {} " + "к событию с id {}", newCommentDto, userId, eventId); + return ResponseEntity.status(HttpStatus.CREATED) .body(commentService.addComment(userId, eventId, newCommentDto)); } + @PatchMapping("/{commentId}") + @ResponseStatus(HttpStatus.OK) + public CommentDto updateComment ( + @PathVariable @Positive Long userId, + @PathVariable @Positive Long commentId, + @Valid @RequestBody UpdateCommentDto updateCommentDto) { + + log.info("Обновление комментария c id {} пользователем c id {}," + + " новый комментарий {}", commentId, userId, updateCommentDto); + + return commentService.updateUserComment(userId, commentId, updateCommentDto); + } } diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java index cea4985..b65e2ac 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentService.java @@ -2,8 +2,11 @@ import ru.practicum.explorewithme.main.dto.CommentDto; import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UpdateCommentDto; public interface CommentService { CommentDto addComment(Long userId, Long eventId, NewCommentDto newCommentDto); + + CommentDto updateUserComment(Long userId, Long commentId, UpdateCommentDto updateCommentDto); } diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java index 53bb597..79f30e8 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.transaction.annotation.Transactional; import ru.practicum.explorewithme.main.dto.CommentDto; import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UpdateCommentDto; import ru.practicum.explorewithme.main.error.BusinessRuleViolationException; import ru.practicum.explorewithme.main.error.EntityNotFoundException; import ru.practicum.explorewithme.main.mapper.CommentMapper; @@ -16,6 +17,7 @@ import ru.practicum.explorewithme.main.repository.EventRepository; import ru.practicum.explorewithme.main.repository.UserRepository; +import java.time.LocalDateTime; import java.util.Optional; @Service @@ -32,17 +34,21 @@ public class CommentServiceImpl implements CommentService { public CommentDto addComment(Long userId, Long eventId, NewCommentDto newCommentDto) { Optional user = userRepository.findById(userId); + if (user.isEmpty()) { throw new EntityNotFoundException("Пользователь с id " + userId + " не найден"); } Optional event = eventRepository.findById(eventId); + if (event.isEmpty()) { throw new EntityNotFoundException("Событие с id " + eventId + " не найден"); } + if (!event.get().getState().equals(EventState.PUBLISHED)) { throw new BusinessRuleViolationException("Событие еще не опубликовано"); } + if (!event.get().isCommentsEnabled()) { throw new BusinessRuleViolationException("Комментарии запрещены"); } @@ -52,8 +58,37 @@ public CommentDto addComment(Long userId, Long eventId, NewCommentDto newComment comment.setAuthor(user.get()); comment.setEvent(event.get()); - commentRepository.save(comment); + return commentMapper.toDto(commentRepository.save(comment)); + } + + @Override + @Transactional + public CommentDto updateUserComment(Long userId, Long commentId, UpdateCommentDto updateCommentDto) { + + Optional comment = commentRepository.findById(commentId); + + if (comment.isEmpty()) { + throw new EntityNotFoundException("Комментарий с id" + commentId + " не найден"); + } + + Comment existedComment = comment.get(); + + if (!existedComment.getAuthor().getId().equals(userId)) { + throw new EntityNotFoundException("Искомый комментарий с id " + commentId + " пользователя с id " + userId + "не найден"); + } + + if (existedComment.isDeleted() == true) { + throw new BusinessRuleViolationException("Редактирование невозможно. Комментарий удален"); + } + + if (existedComment.getCreatedOn().isAfter(LocalDateTime.now().minusHours(6))) { + throw new BusinessRuleViolationException("Время для редактирования истекло"); + } + + existedComment.setText(updateCommentDto.getText()); + existedComment.setEdited(true); + existedComment.setUpdatedOn(LocalDateTime.now()); - return commentMapper.toDto(comment); + return commentMapper.toDto(commentRepository.save(existedComment)); } } diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java index 10ef509..b757326 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.test.web.servlet.MockMvc; import ru.practicum.explorewithme.main.dto.CommentDto; import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UpdateCommentDto; import ru.practicum.explorewithme.main.dto.UserShortDto; import ru.practicum.explorewithme.main.service.CommentService; @@ -17,11 +18,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; // <-- для post-запроса import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + @WebMvcTest(PrivateCommentController.class) public class PrivateCommentControllerTest { @@ -64,6 +67,8 @@ void setUp() { .build(); } + // тесты для создания + @Test void createComment_whenValidInput_thenReturnsCreatedComment() throws Exception { when(commentService.addComment(eq(userId), eq(eventId), any(NewCommentDto.class))) @@ -109,4 +114,110 @@ void createComment_whenNegativeEventId_thenReturnsBadRequest() throws Exception .andExpect(status().isBadRequest()); } + //тесты для обновления + + @Test + void updateComment_shouldReturnUpdatedComment_whenInputIsValid() throws Exception { + Long commentId = commentDto.getId(); + + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("Updated text") + .build(); + + CommentDto updatedComment = CommentDto.builder() + .id(commentId) + .text(updateCommentDto.getText()) + .author(commentDto.getAuthor()) + .eventId(eventId) + .createdOn(commentDto.getCreatedOn()) + .updatedOn(commentDto.getUpdatedOn()) + .isEdited(true) + .build(); + + when(commentService.updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class))) + .thenReturn(updatedComment); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(commentId)) + .andExpect(jsonPath("$.text").value(updateCommentDto.getText())) + .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) + .andExpect(jsonPath("$.isEdited").value(true)); + + verify(commentService, times(1)) + .updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class)); + } + + @Test + void updateComment_shouldReturnBadRequest_whenPathVariablesInvalid() throws Exception { + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("Comment text") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", -1, 10) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", 1, -10) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextBlank() throws Exception { + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text(" ") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextTooShort() throws Exception { + + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextTooLong() throws Exception { + String longText = "a".repeat(2001); + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text(longText) + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyEmpty() throws Exception { + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content("{}")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + } } diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java index b8f75a5..14728ef 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java @@ -1,5 +1,6 @@ package ru.practicum.explorewithme.main.service; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -8,6 +9,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import ru.practicum.explorewithme.main.dto.CommentDto; import ru.practicum.explorewithme.main.dto.NewCommentDto; +import ru.practicum.explorewithme.main.dto.UpdateCommentDto; import ru.practicum.explorewithme.main.error.BusinessRuleViolationException; import ru.practicum.explorewithme.main.error.EntityNotFoundException; import ru.practicum.explorewithme.main.mapper.CommentMapper; @@ -19,6 +21,7 @@ import ru.practicum.explorewithme.main.repository.EventRepository; import ru.practicum.explorewithme.main.repository.UserRepository; +import java.time.LocalDateTime; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -41,15 +44,29 @@ class CommentServiceImplTest { private long userId; private long eventId; + private long commentId; private User user; private Event event; + private Comment comment; @BeforeEach void setUp() { userId = 1L; eventId = 2L; + commentId = 10L; user = new User(); + user.setId(userId); + event = new Event(); + event.setId(eventId); + + comment = new Comment(); + comment.setId(commentId); + comment.setAuthor(user); + comment.setDeleted(false); + comment.setEdited(false); + comment.setText("Old text"); + comment.setCreatedOn(LocalDateTime.now().minusHours(7)); } @Test @@ -57,13 +74,14 @@ void addComment_success() { NewCommentDto newCommentDto = new NewCommentDto(); event.setState(EventState.PUBLISHED); event.setCommentsEnabled(true); - Comment comment = new Comment(); + CommentDto commentDto = new CommentDto(); when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); when(commentMapper.toComment(newCommentDto)).thenReturn(comment); - when(commentMapper.toDto(comment)).thenReturn(commentDto); + when(commentRepository.save(any(Comment.class))).thenReturn(comment); + when(commentMapper.toDto(any(Comment.class))).thenReturn(commentDto); CommentDto result = commentService.addComment(userId, eventId, newCommentDto); @@ -114,4 +132,82 @@ void addComment_commentsDisabled() { () -> commentService.addComment(userId, eventId, new NewCommentDto())); assertEquals("Комментарии запрещены", ex.getMessage()); } + + // тесты для обновления + + @Test + void updateUserComment_shouldUpdateCommentAndReturnDto() { + UpdateCommentDto updateCommentDto = new UpdateCommentDto(); + updateCommentDto.setText("Updated text"); + + CommentDto expectedDto = new CommentDto(); + expectedDto.setId(commentId); + expectedDto.setText("Updated text"); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + when(commentMapper.toDto(any(Comment.class))).thenReturn(expectedDto); + when(commentRepository.save(any(Comment.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + CommentDto result = commentService.updateUserComment(userId, commentId, updateCommentDto); + + Assertions.assertEquals("Updated text", result.getText()); + Assertions.assertTrue(comment.isEdited()); + verify(commentRepository).save(comment); + } + + @Test + void updateUserComment_shouldThrowIfCommentNotFound() { + when(commentRepository.findById(commentId)).thenReturn(Optional.empty()); + UpdateCommentDto dto = new UpdateCommentDto(); + + EntityNotFoundException ex = Assertions.assertThrows( + EntityNotFoundException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("не найден")); + } + + @Test + void updateUserComment_shouldThrowIfUserIsNotAuthor() { + User anotherUser = new User(); + anotherUser.setId(111L); + comment.setAuthor(anotherUser); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + EntityNotFoundException ex = Assertions.assertThrows( + EntityNotFoundException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("пользователя с id")); + } + + @Test + void updateUserComment_shouldThrowIfDeleted() { + comment.setDeleted(true); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + BusinessRuleViolationException ex = Assertions.assertThrows( + BusinessRuleViolationException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("удален")); + } + + @Test + void updateUserComment_shouldThrowIfTooLate() { + + comment.setCreatedOn(LocalDateTime.now().minusHours(2)); + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + BusinessRuleViolationException ex = Assertions.assertThrows( + BusinessRuleViolationException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("Время для редактирования истекло")); + } } \ No newline at end of file From efbad805d5a9a3f7a7ca94c6f9cf256e466d2fa5 Mon Sep 17 00:00:00 2001 From: GrimJak Date: Fri, 30 May 2025 16:23:44 +0300 Subject: [PATCH 3/7] fixiki --- .../main/controller/priv/PrivateCommentController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java index 63658da..8946b2d 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java @@ -37,7 +37,7 @@ public ResponseEntity createComment( @PatchMapping("/{commentId}") @ResponseStatus(HttpStatus.OK) - public CommentDto updateComment ( + public CommentDto updateComment( @PathVariable @Positive Long userId, @PathVariable @Positive Long commentId, @Valid @RequestBody UpdateCommentDto updateCommentDto) { From 7fa91a794a0b547abff17172118d6b9469d242d8 Mon Sep 17 00:00:00 2001 From: GrimJak Date: Fri, 30 May 2025 16:35:07 +0300 Subject: [PATCH 4/7] fixiki --- .../main/controller/priv/PrivateCommentControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java index b757326..44ed72f 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -193,7 +193,7 @@ void updateComment_shouldReturnBadRequest_whenBodyTextTooShort() throws Exceptio .content(objectMapper.writeValueAsString(updateCommentDto))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); } @Test From e690288165590e36571ac2ea34871b6763de6ca9 Mon Sep 17 00:00:00 2001 From: GrimJak Date: Sat, 31 May 2025 11:11:01 +0300 Subject: [PATCH 5/7] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../priv/PrivateCommentController.java | 4 +- .../main/service/CommentServiceImpl.java | 33 +- .../priv/PrivateCommentControllerTest.java | 308 +++++++++--------- .../main/service/CommentServiceImplTest.java | 290 +++++++++-------- 4 files changed, 321 insertions(+), 314 deletions(-) diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java index 8946b2d..c14849b 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentController.java @@ -37,7 +37,7 @@ public ResponseEntity createComment( @PatchMapping("/{commentId}") @ResponseStatus(HttpStatus.OK) - public CommentDto updateComment( + public ResponseEntity updateComment( @PathVariable @Positive Long userId, @PathVariable @Positive Long commentId, @Valid @RequestBody UpdateCommentDto updateCommentDto) { @@ -45,6 +45,6 @@ public CommentDto updateComment( log.info("Обновление комментария c id {} пользователем c id {}," + " новый комментарий {}", commentId, userId, updateCommentDto); - return commentService.updateUserComment(userId, commentId, updateCommentDto); + return ResponseEntity.status(HttpStatus.OK).body(commentService.updateUserComment(userId, commentId, updateCommentDto)); } } diff --git a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java index 79f30e8..bf3f383 100644 --- a/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java +++ b/main-service/src/main/java/ru/practicum/explorewithme/main/service/CommentServiceImpl.java @@ -24,39 +24,33 @@ @RequiredArgsConstructor public class CommentServiceImpl implements CommentService { - UserRepository userRepository; - EventRepository eventRepository; - CommentMapper commentMapper; - CommentRepository commentRepository; + private final UserRepository userRepository; + private final EventRepository eventRepository; + private final CommentMapper commentMapper; + private final CommentRepository commentRepository; @Override @Transactional public CommentDto addComment(Long userId, Long eventId, NewCommentDto newCommentDto) { - Optional user = userRepository.findById(userId); + User author = userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException("Пользователь с id " + userId + " не найден")); - if (user.isEmpty()) { - throw new EntityNotFoundException("Пользователь с id " + userId + " не найден"); - } - - Optional event = eventRepository.findById(eventId); - - if (event.isEmpty()) { - throw new EntityNotFoundException("Событие с id " + eventId + " не найден"); - } + Event event = eventRepository.findById(eventId) + .orElseThrow(() -> new EntityNotFoundException("Событие с id " + eventId + " не найдено")); - if (!event.get().getState().equals(EventState.PUBLISHED)) { + if (!event.getState().equals(EventState.PUBLISHED)) { throw new BusinessRuleViolationException("Событие еще не опубликовано"); } - if (!event.get().isCommentsEnabled()) { + if (!event.isCommentsEnabled()) { throw new BusinessRuleViolationException("Комментарии запрещены"); } Comment comment = commentMapper.toComment(newCommentDto); - comment.setAuthor(user.get()); - comment.setEvent(event.get()); + comment.setAuthor(author); + comment.setEvent(event); return commentMapper.toDto(commentRepository.save(comment)); } @@ -81,13 +75,12 @@ public CommentDto updateUserComment(Long userId, Long commentId, UpdateCommentDt throw new BusinessRuleViolationException("Редактирование невозможно. Комментарий удален"); } - if (existedComment.getCreatedOn().isAfter(LocalDateTime.now().minusHours(6))) { + if (existedComment.getCreatedOn().isBefore(LocalDateTime.now().minusHours(6))) { throw new BusinessRuleViolationException("Время для редактирования истекло"); } existedComment.setText(updateCommentDto.getText()); existedComment.setEdited(true); - existedComment.setUpdatedOn(LocalDateTime.now()); return commentMapper.toDto(commentRepository.save(existedComment)); } diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java index 44ed72f..580a186 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -67,157 +69,163 @@ void setUp() { .build(); } - // тесты для создания - - @Test - void createComment_whenValidInput_thenReturnsCreatedComment() throws Exception { - when(commentService.addComment(eq(userId), eq(eventId), any(NewCommentDto.class))) - .thenReturn(commentDto); - - mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(newCommentDto))) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.id").value(commentDto.getId())) - .andExpect(jsonPath("$.text").value(commentDto.getText())) - .andExpect(jsonPath("$.eventId").value(eventId)) - .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) - .andExpect(jsonPath("$.author.name").value(commentDto.getAuthor().getName())) - .andExpect(jsonPath("$.isEdited").value(false)); + @Nested + @DisplayName("Набор тестов для метода createComment") + class createComment { + + @Test + void createComment_whenValidInput_thenReturnsCreatedComment() throws Exception { + when(commentService.addComment(eq(userId), eq(eventId), any(NewCommentDto.class))) + .thenReturn(commentDto); + + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").value(commentDto.getId())) + .andExpect(jsonPath("$.text").value(commentDto.getText())) + .andExpect(jsonPath("$.eventId").value(eventId)) + .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) + .andExpect(jsonPath("$.author.name").value(commentDto.getAuthor().getName())) + .andExpect(jsonPath("$.isEdited").value(false)); + } + + @Test + void createComment_whenInvalidText_thenReturnsBadRequest() throws Exception { + NewCommentDto invalidDto = NewCommentDto.builder() + .text("") + .build(); + + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void createComment_whenNegativeUserId_thenReturnsBadRequest() throws Exception { + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", -1, eventId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void createComment_whenNegativeEventId_thenReturnsBadRequest() throws Exception { + mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, -1) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(newCommentDto))) + .andExpect(status().isBadRequest()); + } } - @Test - void createComment_whenInvalidText_thenReturnsBadRequest() throws Exception { - NewCommentDto invalidDto = NewCommentDto.builder() - .text("") // некорректный: пустая строка - .build(); - - mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, eventId) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDto))) - .andExpect(status().isBadRequest()); - } - - @Test - void createComment_whenNegativeUserId_thenReturnsBadRequest() throws Exception { - mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", -1, eventId) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(newCommentDto))) - .andExpect(status().isBadRequest()); - } - - @Test - void createComment_whenNegativeEventId_thenReturnsBadRequest() throws Exception { - mockMvc.perform(post("/users/{userId}/comments?eventId={eventId}", userId, -1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(newCommentDto))) - .andExpect(status().isBadRequest()); - } - - //тесты для обновления - - @Test - void updateComment_shouldReturnUpdatedComment_whenInputIsValid() throws Exception { - Long commentId = commentDto.getId(); - - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text("Updated text") - .build(); - - CommentDto updatedComment = CommentDto.builder() - .id(commentId) - .text(updateCommentDto.getText()) - .author(commentDto.getAuthor()) - .eventId(eventId) - .createdOn(commentDto.getCreatedOn()) - .updatedOn(commentDto.getUpdatedOn()) - .isEdited(true) - .build(); - - when(commentService.updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class))) - .thenReturn(updatedComment); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentId) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id").value(commentId)) - .andExpect(jsonPath("$.text").value(updateCommentDto.getText())) - .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) - .andExpect(jsonPath("$.isEdited").value(true)); - - verify(commentService, times(1)) - .updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class)); - } - - @Test - void updateComment_shouldReturnBadRequest_whenPathVariablesInvalid() throws Exception { - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text("Comment text") - .build(); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", -1, 10) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", 1, -10) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()); - } - - @Test - void updateComment_shouldReturnBadRequest_whenBodyTextBlank() throws Exception { - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text(" ") - .build(); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); - } - - @Test - void updateComment_shouldReturnBadRequest_whenBodyTextTooShort() throws Exception { - - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text("") - .build(); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); - } - - @Test - void updateComment_shouldReturnBadRequest_whenBodyTextTooLong() throws Exception { - String longText = "a".repeat(2001); - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text(longText) - .build(); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); - } - - @Test - void updateComment_shouldReturnBadRequest_whenBodyEmpty() throws Exception { - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content("{}")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + @Nested + @DisplayName("Набор тестов для метода updateComment") + class updateComment { + + @Test + void updateComment_shouldReturnUpdatedComment_whenInputIsValid() throws Exception { + Long commentId = commentDto.getId(); + + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("Updated text") + .build(); + + CommentDto updatedComment = CommentDto.builder() + .id(commentId) + .text(updateCommentDto.getText()) + .author(commentDto.getAuthor()) + .eventId(eventId) + .createdOn(commentDto.getCreatedOn()) + .updatedOn(commentDto.getUpdatedOn()) + .isEdited(true) + .build(); + + when(commentService.updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class))) + .thenReturn(updatedComment); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(commentId)) + .andExpect(jsonPath("$.text").value(updateCommentDto.getText())) + .andExpect(jsonPath("$.author.id").value(commentDto.getAuthor().getId())) + .andExpect(jsonPath("$.isEdited").value(true)); + + verify(commentService, times(1)) + .updateUserComment(eq(userId), eq(commentId), any(UpdateCommentDto.class)); + } + + @Test + void updateComment_shouldReturnBadRequest_whenPathVariablesInvalid() throws Exception { + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("Comment text") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", -1, 10) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", 1, -10) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextBlank() throws Exception { + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text(" ") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextTooShort() throws Exception { + + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text("") + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyTextTooLong() throws Exception { + String longText = "a".repeat(2001); + UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() + .text(longText) + .build(); + + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateCommentDto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); + } + + @Test + void updateComment_shouldReturnBadRequest_whenBodyEmpty() throws Exception { + mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content("{}")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0]").exists()) + .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); + } } } diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java index 14728ef..0eff074 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java @@ -1,8 +1,6 @@ package ru.practicum.explorewithme.main.service; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -66,148 +64,156 @@ void setUp() { comment.setDeleted(false); comment.setEdited(false); comment.setText("Old text"); - comment.setCreatedOn(LocalDateTime.now().minusHours(7)); + comment.setCreatedOn(LocalDateTime.now().minusHours(5)); } - @Test - void addComment_success() { - NewCommentDto newCommentDto = new NewCommentDto(); - event.setState(EventState.PUBLISHED); - event.setCommentsEnabled(true); - - CommentDto commentDto = new CommentDto(); - - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); - when(commentMapper.toComment(newCommentDto)).thenReturn(comment); - when(commentRepository.save(any(Comment.class))).thenReturn(comment); - when(commentMapper.toDto(any(Comment.class))).thenReturn(commentDto); - - CommentDto result = commentService.addComment(userId, eventId, newCommentDto); - - assertEquals(commentDto, result); - verify(commentRepository, times(1)).save(comment); - assertEquals(user, comment.getAuthor()); - assertEquals(event, comment.getEvent()); - } - - @Test - void addComment_userNotFound() { - when(userRepository.findById(userId)).thenReturn(Optional.empty()); - - EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, - () -> commentService.addComment(userId, 2L, new NewCommentDto())); - assertTrue(ex.getMessage().contains("Пользователь с id " + userId + " не найден")); - } - - @Test - void addComment_eventNotFound() { - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - when(eventRepository.findById(eventId)).thenReturn(Optional.empty()); - - EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, - () -> commentService.addComment(userId, eventId, new NewCommentDto())); - assertTrue(ex.getMessage().contains("Событие с id " + eventId + " не найден")); - } - - @Test - void addComment_eventNotPublished() { - event.setState(EventState.PENDING); // не опубликовано - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); - - BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, - () -> commentService.addComment(userId, eventId, new NewCommentDto())); - assertEquals("Событие еще не опубликовано", ex.getMessage()); - } - - @Test - void addComment_commentsDisabled() { - event.setState(EventState.PUBLISHED); - event.setCommentsEnabled(false); // Комментарии запрещены - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); - - BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, - () -> commentService.addComment(userId, eventId, new NewCommentDto())); - assertEquals("Комментарии запрещены", ex.getMessage()); - } - - // тесты для обновления - - @Test - void updateUserComment_shouldUpdateCommentAndReturnDto() { - UpdateCommentDto updateCommentDto = new UpdateCommentDto(); - updateCommentDto.setText("Updated text"); - - CommentDto expectedDto = new CommentDto(); - expectedDto.setId(commentId); - expectedDto.setText("Updated text"); - - when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); - when(commentMapper.toDto(any(Comment.class))).thenReturn(expectedDto); - when(commentRepository.save(any(Comment.class))).thenAnswer(invocation -> invocation.getArgument(0)); - - CommentDto result = commentService.updateUserComment(userId, commentId, updateCommentDto); - - Assertions.assertEquals("Updated text", result.getText()); - Assertions.assertTrue(comment.isEdited()); - verify(commentRepository).save(comment); - } - - @Test - void updateUserComment_shouldThrowIfCommentNotFound() { - when(commentRepository.findById(commentId)).thenReturn(Optional.empty()); - UpdateCommentDto dto = new UpdateCommentDto(); - - EntityNotFoundException ex = Assertions.assertThrows( - EntityNotFoundException.class, - () -> commentService.updateUserComment(userId, commentId, dto) - ); - Assertions.assertTrue(ex.getMessage().contains("не найден")); - } - - @Test - void updateUserComment_shouldThrowIfUserIsNotAuthor() { - User anotherUser = new User(); - anotherUser.setId(111L); - comment.setAuthor(anotherUser); - - when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); - UpdateCommentDto dto = new UpdateCommentDto(); - - EntityNotFoundException ex = Assertions.assertThrows( - EntityNotFoundException.class, - () -> commentService.updateUserComment(userId, commentId, dto) - ); - Assertions.assertTrue(ex.getMessage().contains("пользователя с id")); + @Nested + @DisplayName("Набор тестов для метода addComment") + class addComment { + + @Test + void addComment_success() { + NewCommentDto newCommentDto = new NewCommentDto(); + event.setState(EventState.PUBLISHED); + event.setCommentsEnabled(true); + + CommentDto commentDto = new CommentDto(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + when(commentMapper.toComment(newCommentDto)).thenReturn(comment); + when(commentRepository.save(any(Comment.class))).thenReturn(comment); + when(commentMapper.toDto(any(Comment.class))).thenReturn(commentDto); + + CommentDto result = commentService.addComment(userId, eventId, newCommentDto); + + assertEquals(commentDto, result); + verify(commentRepository, times(1)).save(comment); + assertEquals(user, comment.getAuthor()); + assertEquals(event, comment.getEvent()); + } + + @Test + void addComment_userNotFound() { + when(userRepository.findById(userId)).thenReturn(Optional.empty()); + + EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, + () -> commentService.addComment(userId, 2L, new NewCommentDto())); + assertTrue(ex.getMessage().contains("Пользователь с id " + userId + " не найден")); + } + + @Test + void addComment_eventNotFound() { + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.empty()); + + EntityNotFoundException ex = assertThrows(EntityNotFoundException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertTrue(ex.getMessage().contains("Событие с id " + eventId + " не найден")); + } + + @Test + void addComment_eventNotPublished() { + event.setState(EventState.PENDING); // не опубликовано + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + + BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertEquals("Событие еще не опубликовано", ex.getMessage()); + } + + @Test + void addComment_commentsDisabled() { + event.setState(EventState.PUBLISHED); + event.setCommentsEnabled(false); // Комментарии запрещены + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(eventRepository.findById(eventId)).thenReturn(Optional.of(event)); + + BusinessRuleViolationException ex = assertThrows(BusinessRuleViolationException.class, + () -> commentService.addComment(userId, eventId, new NewCommentDto())); + assertEquals("Комментарии запрещены", ex.getMessage()); + } } - @Test - void updateUserComment_shouldThrowIfDeleted() { - comment.setDeleted(true); - - when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); - UpdateCommentDto dto = new UpdateCommentDto(); - - BusinessRuleViolationException ex = Assertions.assertThrows( - BusinessRuleViolationException.class, - () -> commentService.updateUserComment(userId, commentId, dto) - ); - Assertions.assertTrue(ex.getMessage().contains("удален")); - } - - @Test - void updateUserComment_shouldThrowIfTooLate() { - - comment.setCreatedOn(LocalDateTime.now().minusHours(2)); - when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); - UpdateCommentDto dto = new UpdateCommentDto(); - - BusinessRuleViolationException ex = Assertions.assertThrows( - BusinessRuleViolationException.class, - () -> commentService.updateUserComment(userId, commentId, dto) - ); - Assertions.assertTrue(ex.getMessage().contains("Время для редактирования истекло")); + @Nested + @DisplayName("Набор тестов для метода updateUserComment") + class updateUserComment { + + @Test + void updateUserComment_shouldUpdateCommentAndReturnDto() { + UpdateCommentDto updateCommentDto = new UpdateCommentDto(); + updateCommentDto.setText("Updated text"); + + CommentDto expectedDto = new CommentDto(); + expectedDto.setId(commentId); + expectedDto.setText("Updated text"); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + when(commentMapper.toDto(any(Comment.class))).thenReturn(expectedDto); + when(commentRepository.save(any(Comment.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + CommentDto result = commentService.updateUserComment(userId, commentId, updateCommentDto); + + Assertions.assertEquals("Updated text", result.getText()); + Assertions.assertTrue(comment.isEdited()); + verify(commentRepository).save(comment); + } + + @Test + void updateUserComment_shouldThrowIfCommentNotFound() { + when(commentRepository.findById(commentId)).thenReturn(Optional.empty()); + UpdateCommentDto dto = new UpdateCommentDto(); + + EntityNotFoundException ex = Assertions.assertThrows( + EntityNotFoundException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("не найден")); + } + + @Test + void updateUserComment_shouldThrowIfUserIsNotAuthor() { + User anotherUser = new User(); + anotherUser.setId(111L); + comment.setAuthor(anotherUser); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + EntityNotFoundException ex = Assertions.assertThrows( + EntityNotFoundException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("пользователя с id")); + } + + @Test + void updateUserComment_shouldThrowIfDeleted() { + comment.setDeleted(true); + + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + BusinessRuleViolationException ex = Assertions.assertThrows( + BusinessRuleViolationException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("удален")); + } + + @Test + void updateUserComment_shouldThrowIfTooLate() { + + comment.setCreatedOn(LocalDateTime.now().minusHours(7)); + when(commentRepository.findById(commentId)).thenReturn(Optional.of(comment)); + UpdateCommentDto dto = new UpdateCommentDto(); + + BusinessRuleViolationException ex = Assertions.assertThrows( + BusinessRuleViolationException.class, + () -> commentService.updateUserComment(userId, commentId, dto) + ); + Assertions.assertTrue(ex.getMessage().contains("Время для редактирования истекло")); + } } } \ No newline at end of file From 8dbc3eed8cc99ad1f250059491554d14fc4963af Mon Sep 17 00:00:00 2001 From: GrimJak Date: Sat, 31 May 2025 11:35:13 +0300 Subject: [PATCH 6/7] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../priv/PrivateCommentControllerTest.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java index 580a186..a1e6753 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -188,21 +188,6 @@ void updateComment_shouldReturnBadRequest_whenBodyTextBlank() throws Exception { .andExpect(jsonPath("$.errors[0]").value("text: Comment text cannot be blank.")); } - @Test - void updateComment_shouldReturnBadRequest_whenBodyTextTooShort() throws Exception { - - UpdateCommentDto updateCommentDto = UpdateCommentDto.builder() - .text("") - .build(); - - mockMvc.perform(patch("/users/{userId}/comments/{commentId}", userId, commentDto.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateCommentDto))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0]").exists()) - .andExpect(jsonPath("$.errors[0]").value("text: Comment text must be between 1 and 2000 characters.")); - } - @Test void updateComment_shouldReturnBadRequest_whenBodyTextTooLong() throws Exception { String longText = "a".repeat(2001); From d7a83e8c2816160ce2fc7e124aa3d60541498670 Mon Sep 17 00:00:00 2001 From: GrimJak Date: Sat, 31 May 2025 11:41:39 +0300 Subject: [PATCH 7/7] fixiki --- .../main/controller/priv/PrivateCommentControllerTest.java | 4 ++-- .../explorewithme/main/service/CommentServiceImplTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java index a1e6753..d6d7cf5 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/controller/priv/PrivateCommentControllerTest.java @@ -71,7 +71,7 @@ void setUp() { @Nested @DisplayName("Набор тестов для метода createComment") - class createComment { + class CreateComment { @Test void createComment_whenValidInput_thenReturnsCreatedComment() throws Exception { @@ -121,7 +121,7 @@ void createComment_whenNegativeEventId_thenReturnsBadRequest() throws Exception @Nested @DisplayName("Набор тестов для метода updateComment") - class updateComment { + class UpdateComment { @Test void updateComment_shouldReturnUpdatedComment_whenInputIsValid() throws Exception { diff --git a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java index 0eff074..7ebbc11 100644 --- a/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java +++ b/main-service/src/test/java/ru/practicum/explorewithme/main/service/CommentServiceImplTest.java @@ -69,7 +69,7 @@ void setUp() { @Nested @DisplayName("Набор тестов для метода addComment") - class addComment { + class AddComment { @Test void addComment_success() { @@ -138,7 +138,7 @@ void addComment_commentsDisabled() { @Nested @DisplayName("Набор тестов для метода updateUserComment") - class updateUserComment { + class UpdateUserComment { @Test void updateUserComment_shouldUpdateCommentAndReturnDto() {