feat(backend): add V17 migration, @mention storage, MentionDTO, user search endpoint, and tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ public class CommentController {
|
||||
@RequestBody CreateCommentDTO dto,
|
||||
Authentication authentication) {
|
||||
AppUser author = resolveUser(authentication);
|
||||
return commentService.postComment(documentId, null, dto.getContent(), author);
|
||||
return commentService.postComment(documentId, null, dto.getContent(), dto.getMentionedUserIds(), author);
|
||||
}
|
||||
|
||||
@PostMapping("/api/documents/{documentId}/comments/{commentId}/replies")
|
||||
@@ -51,7 +51,7 @@ public class CommentController {
|
||||
@RequestBody CreateCommentDTO dto,
|
||||
Authentication authentication) {
|
||||
AppUser author = resolveUser(authentication);
|
||||
return commentService.replyToComment(documentId, commentId, dto.getContent(), author);
|
||||
return commentService.replyToComment(documentId, commentId, dto.getContent(), dto.getMentionedUserIds(), author);
|
||||
}
|
||||
|
||||
// ─── Annotation comments ──────────────────────────────────────────────────
|
||||
@@ -70,7 +70,7 @@ public class CommentController {
|
||||
@RequestBody CreateCommentDTO dto,
|
||||
Authentication authentication) {
|
||||
AppUser author = resolveUser(authentication);
|
||||
return commentService.postComment(documentId, annotationId, dto.getContent(), author);
|
||||
return commentService.postComment(documentId, annotationId, dto.getContent(), dto.getMentionedUserIds(), author);
|
||||
}
|
||||
|
||||
@PostMapping("/api/documents/{documentId}/annotations/{annotationId}/comments/{commentId}/replies")
|
||||
@@ -82,7 +82,7 @@ public class CommentController {
|
||||
@RequestBody CreateCommentDTO dto,
|
||||
Authentication authentication) {
|
||||
AppUser author = resolveUser(authentication);
|
||||
return commentService.replyToComment(documentId, commentId, dto.getContent(), author);
|
||||
return commentService.replyToComment(documentId, commentId, dto.getContent(), dto.getMentionedUserIds(), author);
|
||||
}
|
||||
|
||||
// ─── Edit and delete (shared) ─────────────────────────────────────────────
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.raddatz.familienarchiv.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.raddatz.familienarchiv.dto.MentionDTO;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.service.UserSearchService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class UserSearchController {
|
||||
|
||||
private final UserSearchService userSearchService;
|
||||
|
||||
@GetMapping("/api/users/search")
|
||||
public List<MentionDTO> search(@RequestParam(defaultValue = "") String q) {
|
||||
return userSearchService.search(q).stream()
|
||||
.map(this::toMentionDTO)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private MentionDTO toMentionDTO(AppUser user) {
|
||||
return new MentionDTO(user.getId(), user.getFirstName(), user.getLastName());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,12 @@ package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class CreateCommentDTO {
|
||||
private String content;
|
||||
private List<UUID> mentionedUserIds = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record MentionDTO(
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID id,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String firstName,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String lastName
|
||||
) {}
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.raddatz.familienarchiv.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.raddatz.familienarchiv.dto.MentionDTO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@@ -60,4 +62,21 @@ public class DocumentComment {
|
||||
@Builder.Default
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<DocumentComment> replies = new ArrayList<>();
|
||||
|
||||
// JPA join table for structured mention references — not serialized directly
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "comment_mentions",
|
||||
joinColumns = @JoinColumn(name = "comment_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "user_id")
|
||||
)
|
||||
@JsonIgnore
|
||||
@Builder.Default
|
||||
private List<AppUser> mentions = new ArrayList<>();
|
||||
|
||||
// Populated by CommentService before serialization — not persisted.
|
||||
@Transient
|
||||
@Builder.Default
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<MentionDTO> mentionDTOs = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package org.raddatz.familienarchiv.repository;
|
||||
|
||||
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -12,4 +15,9 @@ import java.util.UUID;
|
||||
public interface AppUserRepository extends JpaRepository<AppUser, UUID> {
|
||||
Optional<AppUser> findByUsername(String username);
|
||||
Optional<AppUser> findByEmail(String email);
|
||||
|
||||
@Query("SELECT u FROM AppUser u WHERE " +
|
||||
"LOWER(COALESCE(u.firstName, '') || ' ' || COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) " +
|
||||
"OR LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%'))")
|
||||
List<AppUser> searchByNameOrUsername(@Param("q") String q, Pageable pageable);
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.raddatz.familienarchiv.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.raddatz.familienarchiv.dto.MentionDTO;
|
||||
import org.raddatz.familienarchiv.exception.DomainException;
|
||||
import org.raddatz.familienarchiv.exception.ErrorCode;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.model.DocumentComment;
|
||||
import org.raddatz.familienarchiv.repository.AppUserRepository;
|
||||
import org.raddatz.familienarchiv.repository.CommentRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -17,21 +19,23 @@ import java.util.UUID;
|
||||
public class CommentService {
|
||||
|
||||
private final CommentRepository commentRepository;
|
||||
private final AppUserRepository userRepository;
|
||||
private final NotificationService notificationService;
|
||||
|
||||
public List<DocumentComment> getCommentsForDocument(UUID documentId) {
|
||||
List<DocumentComment> roots =
|
||||
commentRepository.findByDocumentIdAndAnnotationIdIsNullAndParentIdIsNull(documentId);
|
||||
return withReplies(roots);
|
||||
return withRepliesAndMentions(roots);
|
||||
}
|
||||
|
||||
public List<DocumentComment> getCommentsForAnnotation(UUID annotationId) {
|
||||
List<DocumentComment> roots = commentRepository.findByAnnotationIdAndParentIdIsNull(annotationId);
|
||||
return withReplies(roots);
|
||||
return withRepliesAndMentions(roots);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DocumentComment postComment(UUID documentId, UUID annotationId, String content, AppUser author) {
|
||||
public DocumentComment postComment(UUID documentId, UUID annotationId, String content,
|
||||
List<UUID> mentionedUserIds, AppUser author) {
|
||||
DocumentComment comment = DocumentComment.builder()
|
||||
.documentId(documentId)
|
||||
.annotationId(annotationId)
|
||||
@@ -39,11 +43,16 @@ public class CommentService {
|
||||
.authorId(author.getId())
|
||||
.authorName(resolveAuthorName(author))
|
||||
.build();
|
||||
return commentRepository.save(comment);
|
||||
saveMentions(comment, mentionedUserIds);
|
||||
DocumentComment saved = commentRepository.save(comment);
|
||||
withMentionDTOs(saved);
|
||||
notificationService.notifyMentions(mentionedUserIds, saved);
|
||||
return saved;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DocumentComment replyToComment(UUID documentId, UUID commentId, String content, AppUser author) {
|
||||
public DocumentComment replyToComment(UUID documentId, UUID commentId, String content,
|
||||
List<UUID> mentionedUserIds, AppUser author) {
|
||||
DocumentComment target = commentRepository.findById(commentId)
|
||||
.orElseThrow(() -> DomainException.notFound(
|
||||
ErrorCode.COMMENT_NOT_FOUND, "Comment not found: " + commentId));
|
||||
@@ -61,8 +70,11 @@ public class CommentService {
|
||||
.authorId(author.getId())
|
||||
.authorName(resolveAuthorName(author))
|
||||
.build();
|
||||
saveMentions(reply, mentionedUserIds);
|
||||
DocumentComment saved = commentRepository.save(reply);
|
||||
withMentionDTOs(saved);
|
||||
notificationService.notifyReply(saved, root);
|
||||
notificationService.notifyMentions(mentionedUserIds, saved);
|
||||
return saved;
|
||||
}
|
||||
|
||||
@@ -89,11 +101,29 @@ public class CommentService {
|
||||
|
||||
// ─── private helpers ──────────────────────────────────────────────────────
|
||||
|
||||
private List<DocumentComment> withReplies(List<DocumentComment> roots) {
|
||||
roots.forEach(root -> root.setReplies(commentRepository.findByParentId(root.getId())));
|
||||
private List<DocumentComment> withRepliesAndMentions(List<DocumentComment> roots) {
|
||||
roots.forEach(root -> {
|
||||
List<DocumentComment> replies = commentRepository.findByParentId(root.getId());
|
||||
replies.forEach(this::withMentionDTOs);
|
||||
root.setReplies(replies);
|
||||
withMentionDTOs(root);
|
||||
});
|
||||
return roots;
|
||||
}
|
||||
|
||||
private void saveMentions(DocumentComment comment, List<UUID> mentionedUserIds) {
|
||||
if (mentionedUserIds == null || mentionedUserIds.isEmpty()) return;
|
||||
List<AppUser> users = userRepository.findAllById(mentionedUserIds);
|
||||
comment.setMentions(users);
|
||||
}
|
||||
|
||||
private void withMentionDTOs(DocumentComment comment) {
|
||||
List<MentionDTO> dtos = comment.getMentions().stream()
|
||||
.map(u -> new MentionDTO(u.getId(), u.getFirstName(), u.getLastName()))
|
||||
.toList();
|
||||
comment.setMentionDTOs(dtos);
|
||||
}
|
||||
|
||||
private DocumentComment findComment(UUID documentId, UUID commentId) {
|
||||
return commentRepository.findById(commentId)
|
||||
.filter(c -> documentId.equals(c.getDocumentId()))
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.raddatz.familienarchiv.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.repository.AppUserRepository;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserSearchService {
|
||||
|
||||
private static final int MAX_RESULTS = 10;
|
||||
|
||||
private final AppUserRepository userRepository;
|
||||
|
||||
public List<AppUser> search(String query) {
|
||||
if (query == null || query.isBlank()) return List.of();
|
||||
return userRepository.searchByNameOrUsername(query.trim(), PageRequest.of(0, MAX_RESULTS));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE comment_mentions (
|
||||
comment_id UUID NOT NULL REFERENCES document_comments(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (comment_id, user_id)
|
||||
);
|
||||
@@ -81,7 +81,7 @@ class CommentControllerTest {
|
||||
void postDocumentComment_returns201_whenHasPermission() throws Exception {
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(COMMENT_ID).documentId(DOC_ID).authorName("Hans").content("Test comment").build();
|
||||
when(commentService.postComment(any(), any(), any(), any())).thenReturn(saved);
|
||||
when(commentService.postComment(any(), any(), any(), any(), any())).thenReturn(saved);
|
||||
|
||||
mockMvc.perform(post("/api/documents/" + DOC_ID + "/comments")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(COMMENT_JSON))
|
||||
@@ -104,7 +104,7 @@ class CommentControllerTest {
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(DOC_ID).parentId(COMMENT_ID)
|
||||
.authorName("Anna").content("Test comment").build();
|
||||
when(commentService.replyToComment(any(), any(), any(), any())).thenReturn(saved);
|
||||
when(commentService.replyToComment(any(), any(), any(), any(), any())).thenReturn(saved);
|
||||
|
||||
mockMvc.perform(post("/api/documents/" + DOC_ID + "/comments/" + COMMENT_ID + "/replies")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(COMMENT_JSON))
|
||||
@@ -179,7 +179,7 @@ class CommentControllerTest {
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(DOC_ID).annotationId(ANN_ID)
|
||||
.authorName("Hans").content("Test comment").build();
|
||||
when(commentService.postComment(any(), any(), any(), any())).thenReturn(saved);
|
||||
when(commentService.postComment(any(), any(), any(), any(), any())).thenReturn(saved);
|
||||
|
||||
mockMvc.perform(post("/api/documents/" + DOC_ID + "/annotations/" + ANN_ID + "/comments")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(COMMENT_JSON))
|
||||
@@ -194,7 +194,7 @@ class CommentControllerTest {
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(DOC_ID).annotationId(ANN_ID)
|
||||
.parentId(COMMENT_ID).authorName("Anna").content("Test comment").build();
|
||||
when(commentService.replyToComment(any(), any(), any(), any())).thenReturn(saved);
|
||||
when(commentService.replyToComment(any(), any(), any(), any(), any())).thenReturn(saved);
|
||||
|
||||
mockMvc.perform(post("/api/documents/" + DOC_ID + "/annotations/" + ANN_ID + "/comments/" + COMMENT_ID + "/replies")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(COMMENT_JSON))
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.raddatz.familienarchiv.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.raddatz.familienarchiv.config.SecurityConfig;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.security.PermissionAspect;
|
||||
import org.raddatz.familienarchiv.service.CustomUserDetailsService;
|
||||
import org.raddatz.familienarchiv.service.UserSearchService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(UserSearchController.class)
|
||||
@Import({SecurityConfig.class, PermissionAspect.class, AopAutoConfiguration.class})
|
||||
class UserSearchControllerTest {
|
||||
|
||||
@Autowired MockMvc mockMvc;
|
||||
|
||||
@MockitoBean UserSearchService userSearchService;
|
||||
@MockitoBean CustomUserDetailsService customUserDetailsService;
|
||||
|
||||
@Test
|
||||
void search_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(get("/api/users/search").param("q", "Hans"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void search_returns200_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID())
|
||||
.firstName("Hans").lastName("Mueller").username("hans").build();
|
||||
when(userSearchService.search("Hans")).thenReturn(List.of(user));
|
||||
|
||||
mockMvc.perform(get("/api/users/search").param("q", "Hans"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].firstName").value("Hans"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void search_returnsEmptyList_whenQueryIsEmpty() throws Exception {
|
||||
when(userSearchService.search("")).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/users/search").param("q", ""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void search_returnsAtMostTenResults() throws Exception {
|
||||
when(userSearchService.search(anyString())).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/users/search").param("q", "a"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.raddatz.familienarchiv.exception.DomainException;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.model.DocumentComment;
|
||||
import org.raddatz.familienarchiv.model.UserGroup;
|
||||
import org.raddatz.familienarchiv.repository.AppUserRepository;
|
||||
import org.raddatz.familienarchiv.repository.CommentRepository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -31,6 +32,7 @@ import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
class CommentServiceTest {
|
||||
|
||||
@Mock CommentRepository commentRepository;
|
||||
@Mock AppUserRepository userRepository;
|
||||
@Mock NotificationService notificationService;
|
||||
@InjectMocks CommentService commentService;
|
||||
|
||||
@@ -45,7 +47,7 @@ class CommentServiceTest {
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Hans Müller").content("Test").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
|
||||
DocumentComment result = commentService.postComment(docId, null, "Test", author);
|
||||
DocumentComment result = commentService.postComment(docId, null, "Test", List.of(), author);
|
||||
|
||||
assertThat(result.getAuthorName()).isEqualTo("Hans Müller");
|
||||
}
|
||||
@@ -58,7 +60,7 @@ class CommentServiceTest {
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("hans42").content("Test").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
|
||||
DocumentComment result = commentService.postComment(docId, null, "Test", author);
|
||||
DocumentComment result = commentService.postComment(docId, null, "Test", List.of(), author);
|
||||
|
||||
assertThat(result.getAuthorName()).isEqualTo("hans42");
|
||||
}
|
||||
@@ -72,7 +74,7 @@ class CommentServiceTest {
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
when(commentRepository.findById(commentId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> commentService.replyToComment(docId, commentId, "Reply", author))
|
||||
assertThatThrownBy(() -> commentService.replyToComment(docId, commentId, "Reply", List.of(), author))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.satisfies(e -> assertThat(((DomainException) e).getStatus()).isEqualTo(NOT_FOUND));
|
||||
|
||||
@@ -97,7 +99,7 @@ class CommentServiceTest {
|
||||
.id(UUID.randomUUID()).documentId(docId).parentId(rootId).content("Reply2").authorName("anna").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
|
||||
DocumentComment result = commentService.replyToComment(docId, replyId, "Reply2", author);
|
||||
DocumentComment result = commentService.replyToComment(docId, replyId, "Reply2", List.of(), author);
|
||||
|
||||
assertThat(result.getParentId()).isEqualTo(rootId);
|
||||
}
|
||||
@@ -116,7 +118,7 @@ class CommentServiceTest {
|
||||
.id(UUID.randomUUID()).documentId(docId).parentId(rootId).content("Reply").authorName("anna").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
|
||||
DocumentComment result = commentService.replyToComment(docId, rootId, "Reply", author);
|
||||
DocumentComment result = commentService.replyToComment(docId, rootId, "Reply", List.of(), author);
|
||||
|
||||
assertThat(result.getParentId()).isEqualTo(rootId);
|
||||
}
|
||||
@@ -135,7 +137,7 @@ class CommentServiceTest {
|
||||
when(commentRepository.findById(rootId)).thenReturn(Optional.of(root));
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
|
||||
commentService.replyToComment(docId, rootId, "Reply", author);
|
||||
commentService.replyToComment(docId, rootId, "Reply", List.of(), author);
|
||||
|
||||
verify(notificationService).notifyReply(eq(saved), eq(root));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user