diff --git a/src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java b/src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java index 8c11daf93..4a7058bea 100644 --- a/src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java +++ b/src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java @@ -1,10 +1,31 @@ package konkuk.thip.comment.adapter.in.web; +import jakarta.validation.Valid; +import konkuk.thip.comment.adapter.in.web.request.CommentCreateRequest; +import konkuk.thip.comment.adapter.in.web.response.CommentIdResponse; +import konkuk.thip.comment.application.port.in.CommentCreateUseCase; +import konkuk.thip.common.dto.BaseResponse; +import konkuk.thip.common.security.annotation.UserId; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor public class CommentCommandController { + private final CommentCreateUseCase commentCreateUseCase; + + + /** + * 댓글/답글 작성 + * parentId:{Long},isReplyRequest:true 답글 + * parentId:null,isReplyRequest:false 댓글 + */ + @PostMapping("/comments/{postId}") + public BaseResponse createComment(@RequestBody @Valid final CommentCreateRequest request, + @PathVariable("postId") final Long postId, + @UserId final Long userId) { + return BaseResponse.ok(CommentIdResponse.of(commentCreateUseCase.createComment(request.toCommand(userId,postId)))); + } + } diff --git a/src/main/java/konkuk/thip/comment/adapter/in/web/request/CommentCreateRequest.java b/src/main/java/konkuk/thip/comment/adapter/in/web/request/CommentCreateRequest.java new file mode 100644 index 000000000..dbc5abc6f --- /dev/null +++ b/src/main/java/konkuk/thip/comment/adapter/in/web/request/CommentCreateRequest.java @@ -0,0 +1,31 @@ +package konkuk.thip.comment.adapter.in.web.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import konkuk.thip.comment.application.port.in.dto.CommentCreateCommand; + +public record CommentCreateRequest( + + @NotBlank(message = "댓글 내용은 필수입니다.") + String content, + + @NotNull(message = "답글 여부는 필수입니다.") + Boolean isReplyRequest, + + Long parentId, + + @NotBlank(message = "게시물 타입은 필수입니다.") + String postType + +) { + public CommentCreateCommand toCommand(Long userId, Long postId) { + return new CommentCreateCommand( + content, + isReplyRequest, + parentId, + postType, + postId, + userId + ); + } +} diff --git a/src/main/java/konkuk/thip/comment/adapter/in/web/request/DummyRequest.java b/src/main/java/konkuk/thip/comment/adapter/in/web/request/DummyRequest.java deleted file mode 100644 index 5721af3a3..000000000 --- a/src/main/java/konkuk/thip/comment/adapter/in/web/request/DummyRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.comment.adapter.in.web.request; - -import lombok.Getter; - -@Getter -public class DummyRequest { -} diff --git a/src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java b/src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java new file mode 100644 index 000000000..c774e8fa1 --- /dev/null +++ b/src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java @@ -0,0 +1,7 @@ +package konkuk.thip.comment.adapter.in.web.response; + +public record CommentIdResponse(Long commentId) { + public static CommentIdResponse of(Long commentId) { + return new CommentIdResponse(commentId); + } +} diff --git a/src/main/java/konkuk/thip/comment/adapter/in/web/response/DummyResponse.java b/src/main/java/konkuk/thip/comment/adapter/in/web/response/DummyResponse.java deleted file mode 100644 index 174514f5c..000000000 --- a/src/main/java/konkuk/thip/comment/adapter/in/web/response/DummyResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.comment.adapter.in.web.response; - -import lombok.Getter; - -@Getter -public class DummyResponse { -} diff --git a/src/main/java/konkuk/thip/comment/adapter/out/jpa/CommentJpaEntity.java b/src/main/java/konkuk/thip/comment/adapter/out/jpa/CommentJpaEntity.java index 807e18556..165d79d53 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/jpa/CommentJpaEntity.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/jpa/CommentJpaEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import konkuk.thip.common.entity.BaseJpaEntity; +import konkuk.thip.common.post.PostType; import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import lombok.*; @@ -26,10 +27,19 @@ public class CommentJpaEntity extends BaseJpaEntity { @Column(name = "report_count", nullable = false) private int reportCount = 0; + @Builder.Default + @Column(name = "like_count", nullable = false) + private int likeCount = 0; + + //TODO 상속구조 해지하면서 postType만 가질지, postId + postType가질지 논의 필요 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id", nullable = false) private PostJpaEntity postJpaEntity; + @Enumerated(EnumType.STRING) + @Column(name = "post_type", nullable = false, length = 10) + private PostType postType; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private UserJpaEntity userJpaEntity; diff --git a/src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java b/src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java index d9ca08cc0..0f37523bc 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java @@ -12,8 +12,10 @@ public class CommentMapper { public CommentJpaEntity toJpaEntity(Comment comment, PostJpaEntity postJpaEntity, UserJpaEntity userJpaEntity, CommentJpaEntity commentJpaEntity) { return CommentJpaEntity.builder() .content(comment.getContent()) + .likeCount(comment.getLikeCount()) .reportCount(comment.getReportCount()) .postJpaEntity(postJpaEntity) + .postType(comment.getPostType()) .userJpaEntity(userJpaEntity) .parent(commentJpaEntity) .build(); @@ -24,7 +26,9 @@ public Comment toDomainEntity(CommentJpaEntity commentJpaEntity) { .id(commentJpaEntity.getCommentId()) .content(commentJpaEntity.getContent()) .reportCount(commentJpaEntity.getReportCount()) + .likeCount(commentJpaEntity.getLikeCount()) .targetPostId(commentJpaEntity.getPostJpaEntity().getPostId()) + .postType(commentJpaEntity.getPostType()) .creatorId(commentJpaEntity.getUserJpaEntity().getUserId()) .parentCommentId(commentJpaEntity.getParent() != null ? commentJpaEntity.getParent().getCommentId() : null) .createdAt(commentJpaEntity.getCreatedAt()) diff --git a/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentCommandPersistenceAdapter.java index e7e42477d..e824a01ab 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentCommandPersistenceAdapter.java @@ -1,12 +1,74 @@ package konkuk.thip.comment.adapter.out.persistence; +import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; +import konkuk.thip.comment.adapter.out.mapper.CommentMapper; +import konkuk.thip.comment.adapter.out.persistence.repository.CommentJpaRepository; import konkuk.thip.comment.application.port.out.CommentCommandPort; +import konkuk.thip.comment.domain.Comment; +import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.common.post.PostType; +import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; +import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; +import konkuk.thip.record.adapter.out.persistence.repository.RecordJpaRepository; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.vote.adapter.out.persistence.repository.VoteJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.Optional; + +import static konkuk.thip.common.exception.code.ErrorCode.*; + @Repository @RequiredArgsConstructor public class CommentCommandPersistenceAdapter implements CommentCommandPort { + private final CommentJpaRepository commentJpaRepository; + private final FeedJpaRepository feedJpaRepository; + private final RecordJpaRepository recordJpaRepository; + private final VoteJpaRepository voteJpaRepository; + private final UserJpaRepository userJpaRepository; + private final CommentMapper commentMapper; + + @Override + public Long save(Comment comment) { + + // 1. 작성자(User) 조회 및 존재 검증 + UserJpaEntity userJpaEntity = userJpaRepository.findById(comment.getCreatorId()).orElseThrow( + () -> new EntityNotFoundException(USER_NOT_FOUND) + ); + + // 2. 게시물(Post) 조회 및 존재 검증 + PostJpaEntity postJpaEntity = findPostJpaEntity(comment.getPostType(), comment.getTargetPostId()); + + // 3. 부모 댓글 조회 (있을 경우) + CommentJpaEntity parentCommentJpaEntity = null; + if (comment.getParentCommentId() != null) { + parentCommentJpaEntity = commentJpaRepository.findById(comment.getParentCommentId()) + .orElseThrow(() -> new EntityNotFoundException(COMMENT_NOT_FOUND)); + } + + return commentJpaRepository.save( + commentMapper.toJpaEntity(comment, postJpaEntity, userJpaEntity,parentCommentJpaEntity) + ).getCommentId(); + } + + private PostJpaEntity findPostJpaEntity(PostType postType, Long postId) { + return switch (postType) { + case FEED -> feedJpaRepository.findById(postId) + .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); + case RECORD -> recordJpaRepository.findById(postId) + .orElseThrow(() -> new EntityNotFoundException(RECORD_NOT_FOUND)); + case VOTE -> voteJpaRepository.findById(postId) + .orElseThrow(() -> new EntityNotFoundException(VOTE_NOT_FOUND)); + }; + } + + @Override + public Optional findById(Long id) { + return commentJpaRepository.findById(id) + .map(commentMapper::toDomainEntity); + } } diff --git a/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java index 1ba9d5d8a..a40d2e714 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java @@ -13,8 +13,4 @@ public class CommentQueryPersistenceAdapter implements CommentQueryPort { private final CommentJpaRepository jpaRepository; private final CommentMapper userMapper; - @Override - public int countByPostId(Long postId) { - return jpaRepository.countByPostJpaEntity_PostId(postId); - } } diff --git a/src/main/java/konkuk/thip/comment/application/port/in/CommentCreateUseCase.java b/src/main/java/konkuk/thip/comment/application/port/in/CommentCreateUseCase.java new file mode 100644 index 000000000..658d4713b --- /dev/null +++ b/src/main/java/konkuk/thip/comment/application/port/in/CommentCreateUseCase.java @@ -0,0 +1,7 @@ +package konkuk.thip.comment.application.port.in; + +import konkuk.thip.comment.application.port.in.dto.CommentCreateCommand; + +public interface CommentCreateUseCase { + Long createComment(CommentCreateCommand command); +} diff --git a/src/main/java/konkuk/thip/comment/application/port/in/DummyUseCase.java b/src/main/java/konkuk/thip/comment/application/port/in/DummyUseCase.java deleted file mode 100644 index 69ed901ce..000000000 --- a/src/main/java/konkuk/thip/comment/application/port/in/DummyUseCase.java +++ /dev/null @@ -1,5 +0,0 @@ -package konkuk.thip.comment.application.port.in; - -public interface DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/comment/application/port/in/dto/CommentCreateCommand.java b/src/main/java/konkuk/thip/comment/application/port/in/dto/CommentCreateCommand.java new file mode 100644 index 000000000..37df203c2 --- /dev/null +++ b/src/main/java/konkuk/thip/comment/application/port/in/dto/CommentCreateCommand.java @@ -0,0 +1,18 @@ +package konkuk.thip.comment.application.port.in.dto; + +public record CommentCreateCommand( + + String content, + + Boolean isReplyRequest, + + Long parentId, + + String postType, + + Long postId, + + Long userId +) +{ +} diff --git a/src/main/java/konkuk/thip/comment/application/port/in/dto/DummyCommand.java b/src/main/java/konkuk/thip/comment/application/port/in/dto/DummyCommand.java deleted file mode 100644 index 33c9022fa..000000000 --- a/src/main/java/konkuk/thip/comment/application/port/in/dto/DummyCommand.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.comment.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyCommand { - -} diff --git a/src/main/java/konkuk/thip/comment/application/port/out/CommentCommandPort.java b/src/main/java/konkuk/thip/comment/application/port/out/CommentCommandPort.java index 9293a61d0..a6e2797c0 100644 --- a/src/main/java/konkuk/thip/comment/application/port/out/CommentCommandPort.java +++ b/src/main/java/konkuk/thip/comment/application/port/out/CommentCommandPort.java @@ -1,6 +1,22 @@ package konkuk.thip.comment.application.port.out; +import konkuk.thip.comment.domain.Comment; +import konkuk.thip.common.exception.EntityNotFoundException; + +import java.util.Optional; + +import static konkuk.thip.common.exception.code.ErrorCode.COMMENT_NOT_FOUND; + public interface CommentCommandPort { + Long save(Comment comment); + + Optional findById(Long id); + + default Comment getByIdOrThrow(Long id) { + return findById(id) + .orElseThrow(() -> new EntityNotFoundException(COMMENT_NOT_FOUND)); + } + } diff --git a/src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java b/src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java index bdefba463..cce75f165 100644 --- a/src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java +++ b/src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java @@ -2,6 +2,4 @@ public interface CommentQueryPort { - int countByPostId(Long postId); - } diff --git a/src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java b/src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java new file mode 100644 index 000000000..9b8af4d1e --- /dev/null +++ b/src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java @@ -0,0 +1,114 @@ +package konkuk.thip.comment.application.service; + +import konkuk.thip.comment.application.port.in.CommentCreateUseCase; +import konkuk.thip.comment.application.port.in.dto.CommentCreateCommand; +import konkuk.thip.comment.application.port.out.CommentCommandPort; +import konkuk.thip.comment.domain.Comment; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.post.CommentCountUpdatable; +import konkuk.thip.feed.application.port.out.FeedCommandPort; +import konkuk.thip.feed.domain.Feed; +import konkuk.thip.common.post.PostType; +import konkuk.thip.record.application.port.out.RecordCommandPort; +import konkuk.thip.record.domain.Record; +import konkuk.thip.room.domain.service.RoomParticipantService; +import konkuk.thip.vote.application.port.out.VoteCommandPort; +import konkuk.thip.vote.domain.Vote; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static konkuk.thip.common.exception.code.ErrorCode.INVALID_COMMENT_CREATE; + + +@Service +@RequiredArgsConstructor +public class CommentCreateService implements CommentCreateUseCase { + + private final CommentCommandPort commentCommandPort; + private final FeedCommandPort feedCommandPort; + private final RecordCommandPort recordCommandPort; + private final VoteCommandPort voteCommandPort; + + private final RoomParticipantService roomParticipantService; + + @Override + @Transactional + public Long createComment(CommentCreateCommand command) { + + // 1. 댓글/답글 생성 선행검증 및 작성하려는 게시글 타입 검증 + Comment.validateCommentCreate(command.isReplyRequest(), command.parentId()); + PostType type = PostType.from(command.postType()); + + // 2. 게시물 타입에 맞게 조회 + CommentCountUpdatable post = findPost(type, command.postId()); + // 2-1. 게시글 타입에 따른 댓글 생성 권한 검증 + validateCommentCreateAuthorization(type, post, command.userId()); + + // TODO 피드: 내 게시글의 댓글, 내 댓글에 대한 답글 알림 전송 + // TODO 기록 및 투표: 모임방의 내 게시글에 대한 댓글, 내 댓글에 대한 답글 알림 전송 + + // 3. 댓글 생성 + Long commentId = createCommentDomain(command); + + // 4. 게시글 댓글 수 증가 + // 4-1. 도메인 게시물 댓글 수 증가 + post.increaseCommentCount(); + // 4-2 Jpa엔티티 게시물 댓글 수 증가 + updatePost(type, post); + + return commentId; + } + + private CommentCountUpdatable findPost(PostType type, Long postId) { + return switch (type) { + case FEED -> feedCommandPort.getByIdOrThrow(postId); + case RECORD -> recordCommandPort.getByIdOrThrow(postId); + case VOTE -> voteCommandPort.getByIdOrThrow(postId); + }; + } + + private void validateCommentCreateAuthorization(PostType type, CommentCountUpdatable post, Long userId) { + // 2-1. RECORD, VOTE는 방 멤버 자격 검증 필요 + if (type == PostType.RECORD || type == PostType.VOTE) { + roomParticipantService.validateUserIsRoomMember(post.getRoomId(), userId); + } + // 2-2. FEED는 비공개 글 일시, 작성자 자격 검증 필요 + else { + Feed feed = (Feed) post; + feed.validateCreateComment(userId); + } + } + + private Long createCommentDomain(CommentCreateCommand command) { + + // 3-1. (답글일 경우) 부모 댓글 조회 + Comment parentComment = null; + if (command.isReplyRequest()) { + parentComment = commentCommandPort.findById(command.parentId()).orElseThrow(() + -> new InvalidStateException(INVALID_COMMENT_CREATE, new IllegalArgumentException("parentId에 해당하는 부모 댓글이 존재해야 합니다."))); + } + + // 3-2. 도메인 댓글 생성 (유효성 검증 포함됨) + Comment comment = Comment.createComment( + command.content(), + command.postId(), + command.userId(), + command.postType(), + command.isReplyRequest(), + command.parentId(), + parentComment + ); + + return commentCommandPort.save(comment); + } + + private void updatePost(PostType type, CommentCountUpdatable post) { + switch (type) { + case FEED -> feedCommandPort.update((Feed) post); + case RECORD -> recordCommandPort.update((Record) post); + case VOTE -> voteCommandPort.updateVote((Vote) post); + } + } + +} diff --git a/src/main/java/konkuk/thip/comment/application/service/CommentService.java b/src/main/java/konkuk/thip/comment/application/service/CommentService.java deleted file mode 100644 index 114b89ded..000000000 --- a/src/main/java/konkuk/thip/comment/application/service/CommentService.java +++ /dev/null @@ -1,11 +0,0 @@ -package konkuk.thip.comment.application.service; - -import konkuk.thip.comment.application.port.in.DummyUseCase; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class CommentService implements DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/comment/domain/Comment.java b/src/main/java/konkuk/thip/comment/domain/Comment.java index 16d17211f..f5134e766 100644 --- a/src/main/java/konkuk/thip/comment/domain/Comment.java +++ b/src/main/java/konkuk/thip/comment/domain/Comment.java @@ -1,9 +1,14 @@ package konkuk.thip.comment.domain; import konkuk.thip.common.entity.BaseDomainEntity; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.post.PostType; +import lombok.Builder; import lombok.Getter; import lombok.experimental.SuperBuilder; +import static konkuk.thip.common.exception.code.ErrorCode.INVALID_COMMENT_CREATE; + @Getter @SuperBuilder public class Comment extends BaseDomainEntity { @@ -12,7 +17,11 @@ public class Comment extends BaseDomainEntity { private String content; - private int reportCount; + @Builder.Default + private Integer reportCount = 0; + + @Builder.Default + private Integer likeCount = 0; private Long targetPostId; @@ -20,4 +29,71 @@ public class Comment extends BaseDomainEntity { private Long parentCommentId; + private PostType postType; + + public static Comment createComment(String content, Long postId, Long creatorId, String type, + boolean isReplyRequest, Long parentId, Comment parent) { + + // 댓글/답글 생성 검증 + validateCommentCreate(isReplyRequest,parentId); + PostType postType = PostType.from(type); + + if (isReplyRequest) { + // 답글 생성 검증 + validateReplyCommentCreate(postId, parent); + return withoutIdReplyComment(content, postId, creatorId, parent, postType); + } + return withoutIdRootComment(content, postId, creatorId, postType); + } + + + private static Comment withoutIdRootComment(String content, Long targetPostId, Long creatorId, PostType postType) { + return Comment.builder() + .id(null) + .content(content) + .targetPostId(targetPostId) + .creatorId(creatorId) + .parentCommentId(null) + .postType(postType) + .reportCount(0) + .likeCount(0) + .build(); + } + + private static Comment withoutIdReplyComment(String content, Long targetPostId, Long creatorId, Comment parentComment, PostType postType) { + return Comment.builder() + .id(null) + .content(content) + .targetPostId(targetPostId) + .creatorId(creatorId) + .parentCommentId(parentComment.getId()) + .postType(postType) + .reportCount(0) + .likeCount(0) + .build(); + } + + private static void validateReplyCommentCreate(Long targetPostId, Comment parentComment) { + if (parentComment == null) { + throw new InvalidStateException( + INVALID_COMMENT_CREATE,new IllegalArgumentException("parentId에 해당하는 부모 댓글이 존재해야 합니다.")); + } + if (!targetPostId.equals(parentComment.getTargetPostId())) { + throw new InvalidStateException( + INVALID_COMMENT_CREATE,new IllegalArgumentException("댓글과 부모 댓글의 게시글이 일치하지 않습니다.")); + } + } + + public static void validateCommentCreate(boolean isReplyRequest, Long parentId) { + if (isReplyRequest && parentId == null) { + throw new InvalidStateException( + INVALID_COMMENT_CREATE, new IllegalArgumentException("답글 작성 시 parentId는 필수입니다.")); + + } + if (!isReplyRequest && parentId != null) { + throw new InvalidStateException( + INVALID_COMMENT_CREATE, new IllegalArgumentException("일반 댓글에는 parentId가 없어야 합니다.")); + } + } + } diff --git a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java index 0da903803..8d1617152 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -117,6 +117,7 @@ public enum ErrorCode implements ResponseCode { USER_NOT_PARTICIPATED_CANNOT_CANCEL(HttpStatus.BAD_REQUEST, 140006, "사용자가 방에 참여하지 않은 상태에서 취소하기는 불가합니다."), HOST_CANNOT_CANCEL(HttpStatus.BAD_REQUEST, 140007, "방장은 참여 취소를 할 수 없습니다."), ROOM_RECRUIT_CANNOT_CLOSED(HttpStatus.BAD_REQUEST, 140008, "방 모집 마감을 할 수 없습니다."), + ROOM_ACCESS_FORBIDDEN(HttpStatus.FORBIDDEN, 140009, "방 접근 권한이 없습니다."), /** * 150000 : Category error @@ -131,7 +132,7 @@ public enum ErrorCode implements ResponseCode { TAG_NAME_NOT_MATCH(HttpStatus.BAD_REQUEST, 160001, "일치하는 태그 이름이 없습니다."), TAG_NOT_FOUND(HttpStatus.NOT_FOUND, 160002, "존재하지 않는 TAG 입니다."), INVALID_FEED_COMMAND(HttpStatus.BAD_REQUEST, 160003, "유효하지 않은 FEED 생성/수정 요청 입니다."), - FEED_UPDATE_FORBIDDEN(HttpStatus.FORBIDDEN, 160004, "피드 수정 권한이 없습니다."), + FEED_ACCESS_FORBIDDEN(HttpStatus.FORBIDDEN, 160004, "피드 접근 권한이 없습니다."), DUPLICATED_FEEDS_IN_COLLECTION(HttpStatus.INTERNAL_SERVER_ERROR, 160005, "중복된 피드가 존재합니다."), FEED_ALREADY_SAVED(HttpStatus.BAD_REQUEST, 160006, "사용자가 이미 저장한 피드입니다."), FEED_NOT_SAVED_CANNOT_DELETE(HttpStatus.BAD_REQUEST, 160007, "사용자가 저장하지 않은 피드는 저장삭제 할 수 없습니다."), @@ -142,7 +143,20 @@ public enum ErrorCode implements ResponseCode { EMPTY_FILE_EXCEPTION(HttpStatus.BAD_REQUEST, 170001, "업로드하려는 이미지가 비어있습니다."), EXCEPTION_ON_IMAGE_UPLOAD(HttpStatus.BAD_REQUEST, 170002, "이미지 업로드에 실패하였습니다."), INVALID_FILE_EXTENSION(HttpStatus.BAD_REQUEST, 170003, "올바르지 않은 파일 형식입니다."), - IO_EXCEPTION_ON_IMAGE_DELETE(HttpStatus.BAD_REQUEST, 170004, "파일 삭제에 실패하였습니다.") + IO_EXCEPTION_ON_IMAGE_DELETE(HttpStatus.BAD_REQUEST, 170004, "파일 삭제에 실패하였습니다."), + + /** + * 180000 : Post error + */ + POST_TYPE_NOT_MATCH(HttpStatus.BAD_REQUEST, 180000, "일치하는 게시물 타입 이름이 없습니다. [feed, record, vote] 중 하나여야 합니다."), + + /** + * 190000 : Comment error + */ + COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, 190000, "존재하지 않는 COMMENT 입니다."), + INVALID_COMMENT_CREATE(HttpStatus.BAD_REQUEST, 190001, "유효하지 않은 COMMENT 생성 요청 입니다."), + + ; private final HttpStatus httpStatus; diff --git a/src/main/java/konkuk/thip/common/post/CommentCountUpdatable.java b/src/main/java/konkuk/thip/common/post/CommentCountUpdatable.java new file mode 100644 index 000000000..cbc52b22e --- /dev/null +++ b/src/main/java/konkuk/thip/common/post/CommentCountUpdatable.java @@ -0,0 +1,6 @@ +package konkuk.thip.common.post; + +public interface CommentCountUpdatable { + void increaseCommentCount(); + Long getRoomId(); +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/common/post/PostType.java b/src/main/java/konkuk/thip/common/post/PostType.java new file mode 100644 index 000000000..05176a47c --- /dev/null +++ b/src/main/java/konkuk/thip/common/post/PostType.java @@ -0,0 +1,41 @@ +package konkuk.thip.common.post; + +import konkuk.thip.common.exception.InvalidStateException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +import static konkuk.thip.common.exception.code.ErrorCode.POST_TYPE_NOT_MATCH; + +@Getter +@RequiredArgsConstructor +public enum PostType { + + FEED("feed"), + RECORD("record"), + VOTE("vote"); + + private final String type; + + public static PostType from(String type) { + return Arrays.stream(PostType.values()) + .filter(p -> p.getType().equalsIgnoreCase(type)) + .findFirst() + .orElseThrow(() -> + new InvalidStateException(POST_TYPE_NOT_MATCH) + ); + } + + public boolean isFeed() { + return this == FEED; + } + + public boolean isRecord() { + return this == RECORD; + } + + public boolean isVote() { + return this == VOTE; + } +} diff --git a/src/main/java/konkuk/thip/feed/domain/Feed.java b/src/main/java/konkuk/thip/feed/domain/Feed.java index eb0ab0447..b5947b597 100644 --- a/src/main/java/konkuk/thip/feed/domain/Feed.java +++ b/src/main/java/konkuk/thip/feed/domain/Feed.java @@ -2,6 +2,7 @@ import konkuk.thip.common.entity.BaseDomainEntity; import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.post.CommentCountUpdatable; import lombok.Builder; import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -16,7 +17,7 @@ @Getter @SuperBuilder -public class Feed extends BaseDomainEntity { +public class Feed extends BaseDomainEntity implements CommentCountUpdatable { private Long id; @@ -106,9 +107,16 @@ public static void validateImageCount(int imageSize) { } } - public void validateCreator(Long userId) { + public void validateCreateComment(Long userId){ + if (!this.isPublic && !this.creatorId.equals(userId)) { + validateCreator(userId); + throw new InvalidStateException(FEED_ACCESS_FORBIDDEN, new IllegalArgumentException("비공개 글은 작성자만 댓글을 쓸 수 있습니다.")); + } + } + + private void validateCreator(Long userId) { if (!this.creatorId.equals(userId)) { - throw new InvalidStateException(FEED_UPDATE_FORBIDDEN); + throw new InvalidStateException(FEED_ACCESS_FORBIDDEN); } } @@ -148,4 +156,14 @@ public void validateOwnsImages(List candidateImageUrls) { } } + @Override + public void increaseCommentCount() { + commentCount++; + } + + @Override + //Feed는 RoomId 없음 + public Long getRoomId() { + return null; + } } diff --git a/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java b/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java index b18654d09..48ce26a0c 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java +++ b/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java @@ -1,6 +1,7 @@ package konkuk.thip.record.adapter.out.jpa; import jakarta.persistence.*; +import konkuk.thip.record.domain.Record; import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -33,5 +34,14 @@ public RecordJpaEntity(String content, Integer likeCount, Integer commentCount, this.roomJpaEntity = roomJpaEntity; } + public RecordJpaEntity updateFrom(Record record) { + this.content = record.getContent(); + this.likeCount = record.getLikeCount(); + this.commentCount = record.getCommentCount(); + this.page = record.getPage(); + this.isOverview = record.isOverview(); + return this; + } + } diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java index 5dfebdc44..d1170074c 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java @@ -1,6 +1,7 @@ package konkuk.thip.record.adapter.out.persistence; import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; import konkuk.thip.record.adapter.out.mapper.RecordMapper; import konkuk.thip.record.adapter.out.persistence.repository.RecordJpaRepository; import konkuk.thip.record.application.port.out.RecordCommandPort; @@ -12,8 +13,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import static konkuk.thip.common.exception.code.ErrorCode.ROOM_NOT_FOUND; -import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; +import java.util.Optional; + +import static konkuk.thip.common.exception.code.ErrorCode.*; @Repository @RequiredArgsConstructor @@ -39,4 +41,19 @@ public Long saveRecord(Record record) { ).getPostId(); } + @Override + public Optional findById(Long id) { + return recordJpaRepository.findById(id) + .map(recordMapper::toDomainEntity); + } + + @Override + public void update(Record record) { + RecordJpaEntity recordJpaEntity = recordJpaRepository.findById(record.getId()).orElseThrow( + () -> new EntityNotFoundException(RECORD_NOT_FOUND) + ); + + recordJpaRepository.save(recordJpaEntity.updateFrom(record)); + } + } diff --git a/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java b/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java index d57a5d6c3..a662daf86 100644 --- a/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java +++ b/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java @@ -1,10 +1,23 @@ package konkuk.thip.record.application.port.out; +import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.record.domain.Record; +import java.util.Optional; + +import static konkuk.thip.common.exception.code.ErrorCode.RECORD_NOT_FOUND; + public interface RecordCommandPort { Long saveRecord(Record record); + void update(Record record); + + Optional findById(Long id); + + default Record getByIdOrThrow(Long id) { + return findById(id) + .orElseThrow(() -> new EntityNotFoundException(RECORD_NOT_FOUND)); + } } diff --git a/src/main/java/konkuk/thip/record/domain/Record.java b/src/main/java/konkuk/thip/record/domain/Record.java index 4bcf99ef0..e860fc8c9 100644 --- a/src/main/java/konkuk/thip/record/domain/Record.java +++ b/src/main/java/konkuk/thip/record/domain/Record.java @@ -2,6 +2,7 @@ import konkuk.thip.common.entity.BaseDomainEntity; import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.post.CommentCountUpdatable; import lombok.Builder; import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -11,7 +12,7 @@ @Getter @SuperBuilder -public class Record extends BaseDomainEntity { +public class Record extends BaseDomainEntity implements CommentCountUpdatable { private Long id; @@ -72,4 +73,9 @@ public void validatePage(int totalPageCount) { ); } } + + @Override + public void increaseCommentCount() { + commentCount++; + } } diff --git a/src/main/java/konkuk/thip/room/domain/service/RoomParticipantService.java b/src/main/java/konkuk/thip/room/domain/service/RoomParticipantService.java new file mode 100644 index 000000000..d6aa73f09 --- /dev/null +++ b/src/main/java/konkuk/thip/room/domain/service/RoomParticipantService.java @@ -0,0 +1,22 @@ +package konkuk.thip.room.domain.service; + +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.room.application.port.out.RoomParticipantQueryPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import static konkuk.thip.common.exception.code.ErrorCode.ROOM_ACCESS_FORBIDDEN; + +@Component +@RequiredArgsConstructor +public class RoomParticipantService { + private final RoomParticipantQueryPort participantPort; + + // 사용자가 방에 속해있는지 검증 + public void validateUserIsRoomMember(Long roomId, Long userId) { + if (!participantPort.existByUserIdAndRoomId(roomId, userId)) { + throw new InvalidStateException(ROOM_ACCESS_FORBIDDEN, + new IllegalArgumentException("사용자가 이 방의 참가자가 아닙니다. roomId=" + roomId + ", userId=" + userId)); + } + } +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java b/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java index d109c2eee..2d8a28378 100644 --- a/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java +++ b/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java @@ -4,6 +4,7 @@ import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.vote.domain.Vote; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -32,4 +33,13 @@ public VoteJpaEntity(String content, Integer likeCount, Integer commentCount, Us this.isOverview = isOverview; this.roomJpaEntity = roomJpaEntity; } + + public VoteJpaEntity updateFrom(Vote vote) { + this.content = vote.getContent(); + this.likeCount = vote.getLikeCount(); + this.commentCount = vote.getCommentCount(); + this.page = vote.getPage(); + this.isOverview = vote.isOverview(); + return this; + } } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java index 0f9ba1bc7..162d51cce 100644 --- a/src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java @@ -19,6 +19,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import static konkuk.thip.common.exception.code.ErrorCode.*; @@ -76,4 +77,20 @@ public List findVoteItemsByVoteId(Long voteId) { return voteItems; } + @Override + public Optional findById(Long id) { + return voteJpaRepository.findById(id) + .map(voteMapper::toDomainEntity); + } + + + @Override + public void updateVote(Vote vote) { + VoteJpaEntity voteJpaEntity = voteJpaRepository.findById(vote.getId()).orElseThrow( + () -> new EntityNotFoundException(VOTE_NOT_FOUND) + ); + + voteJpaRepository.save(voteJpaEntity.updateFrom(vote)); + } + } diff --git a/src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java b/src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java index 2874b7a1a..9742cddb6 100644 --- a/src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java +++ b/src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java @@ -1,15 +1,28 @@ package konkuk.thip.vote.application.port.out; +import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.vote.domain.Vote; import konkuk.thip.vote.domain.VoteItem; import java.util.List; +import java.util.Optional; + +import static konkuk.thip.common.exception.code.ErrorCode.VOTE_NOT_FOUND; public interface VoteCommandPort { Long saveVote(Vote vote); + void updateVote(Vote vote); + void saveAllVoteItems(List voteItems); List findVoteItemsByVoteId(Long voteId); + + Optional findById(Long id); + + default Vote getByIdOrThrow(Long id) { + return findById(id) + .orElseThrow(() -> new EntityNotFoundException(VOTE_NOT_FOUND)); + } } diff --git a/src/main/java/konkuk/thip/vote/domain/Vote.java b/src/main/java/konkuk/thip/vote/domain/Vote.java index 653710ce9..affeb2da6 100644 --- a/src/main/java/konkuk/thip/vote/domain/Vote.java +++ b/src/main/java/konkuk/thip/vote/domain/Vote.java @@ -2,6 +2,7 @@ import konkuk.thip.common.entity.BaseDomainEntity; import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.post.CommentCountUpdatable; import lombok.Builder; import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -11,7 +12,7 @@ @Getter @SuperBuilder -public class Vote extends BaseDomainEntity { +public class Vote extends BaseDomainEntity implements CommentCountUpdatable { private Long id; @@ -66,4 +67,9 @@ public void validatePage(int totalPageCount) { ); } } + + @Override + public void increaseCommentCount() { + commentCount++; + } } diff --git a/src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java b/src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java new file mode 100644 index 000000000..027d11d13 --- /dev/null +++ b/src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java @@ -0,0 +1,272 @@ +package konkuk.thip.comment.adapter.in.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; +import konkuk.thip.comment.adapter.out.persistence.repository.CommentJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; +import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; +import konkuk.thip.record.adapter.out.persistence.repository.RecordJpaRepository; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; +import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; +import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; +import konkuk.thip.vote.adapter.out.persistence.repository.VoteJpaRepository; +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.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.Map; + +import static konkuk.thip.common.exception.code.ErrorCode.*; +import static konkuk.thip.common.post.PostType.FEED; +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +@Transactional +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@DisplayName("[단위] 댓글 생성 api controller 단위 테스트") +class CommentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired private ObjectMapper objectMapper; + @Autowired private AliasJpaRepository aliasJpaRepository; + @Autowired private UserJpaRepository userJpaRepository; + @Autowired private CategoryJpaRepository categoryJpaRepository; + @Autowired private BookJpaRepository bookJpaRepository; + @Autowired private FeedJpaRepository feedJpaRepository; + @Autowired private VoteJpaRepository voteJpaRepository; + @Autowired private RecordJpaRepository recordJpaRepository; + @Autowired private CommentJpaRepository commentJpaRepository; + @Autowired private RoomJpaRepository roomJpaRepository; + @Autowired private RoomParticipantJpaRepository roomParticipantJpaRepository; + + + private AliasJpaEntity alias; + private UserJpaEntity user; + private CategoryJpaEntity category; + private FeedJpaEntity feed; + private BookJpaEntity book; + private RecordJpaEntity record; + private VoteJpaEntity vote; + private RoomJpaEntity room; + + @BeforeEach + void setUp() { + alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + user = userJpaRepository.save(TestEntityFactory.createUser(alias)); + category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + book = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("9788954682152")); + room = roomJpaRepository.save(TestEntityFactory.createRoom(book,category)); + feed = feedJpaRepository.save(TestEntityFactory.createFeed(user,book, true)); + record = recordJpaRepository.save(TestEntityFactory.createRecord(user,room)); + vote = voteJpaRepository.save(TestEntityFactory.createVote(user,room)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); + } + + + private Map buildValidRequest() { + Map req = new HashMap<>(); + req.put("content", "정상 댓글"); + req.put("isReplyRequest", false); + req.put("parentId", null); + req.put("postType", "feed"); + return req; + } + + private void assertBadRequest(Map req, String expectedMessage) throws Exception { + mockMvc.perform(post("/comments/{postId}", feed.getPostId()) + .requestAttr("userId", user.getUserId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) + .andExpect(jsonPath("$.message", containsString(expectedMessage))); + } + + private void assertBadCommentCreateRequest(Map req, String expectedMessage) throws Exception { + mockMvc.perform(post("/comments/{postId}", feed.getPostId()) + .requestAttr("userId", user.getUserId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(INVALID_COMMENT_CREATE.getCode())) + .andExpect(jsonPath("$.message", containsString(expectedMessage))); + } + + @Nested + @DisplayName("댓글 내용(content) 검증") + class ContentValidation { + @Test + @DisplayName("빈 문자열일 때 400 error") + void blankContent() throws Exception { + Map req = buildValidRequest(); + req.put("content", ""); + assertBadRequest(req, "댓글 내용은 필수입니다."); + } + } + + @Nested + @DisplayName("isReplyRequest(답글 여부) 검증") + class IsReplyRequestValidation { + @Test + @DisplayName("누락될 경우 400 error") + void missingIsReplyRequest() throws Exception { + Map req = buildValidRequest(); + req.remove("isReplyRequest"); + assertBadRequest(req, "답글 여부는 필수입니다."); + } + } + + @Nested + @DisplayName("postType(게시물 타입) 검증") + class PostTypeValidation { + + @Test + @DisplayName("빈 문자열일 때 400 error") + void blankPostType() throws Exception { + Map req = buildValidRequest(); + req.put("postType", ""); + assertBadRequest(req, "게시물 타입은 필수입니다."); + } + + @Test + @DisplayName("지원하지 않는 게시물 타입 입력 시 400 반환") + void invalidPostType() throws Exception { + Map req = buildValidRequest(); + req.put("postType", "invalidType"); + mockMvc.perform(post("/comments/{postId}", feed.getPostId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req)) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(POST_TYPE_NOT_MATCH.getCode())) + .andExpect(jsonPath("$.message", containsString("일치하는 게시물 타입 이름이 없습니다."))); + } + } + + @Nested + @DisplayName("예외 상황 검증") + class CommentExceptionValidation { + + @Test + @DisplayName("존재하지 않는 FEED일 경우 404 반환") + void feedNotFound() throws Exception { + Map req = buildValidRequest(); + mockMvc.perform(post("/comments/{postId}", 99999L) // 없는 ID + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req)) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.code").value(FEED_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message", containsString("존재하지 않는 FEED 입니다."))); + } + + @Test + @DisplayName("존재하지 않는 RECORD일 경우 404 반환") + void recordNotFound() throws Exception { + Map req = buildValidRequest(); + req.put("postType", "record"); + mockMvc.perform(post("/comments/{postId}", 99999L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req)) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.code").value(RECORD_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message", containsString("존재하지 않는 RECORD 입니다."))); + } + + @Test + @DisplayName("존재하지 않는 VOTE일 경우 404 반환") + void voteNotFound() throws Exception { + Map req = buildValidRequest(); + req.put("postType", "vote"); + mockMvc.perform(post("/comments/{postId}", 99999L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req)) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.code").value(VOTE_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message", containsString("존재하지 않는 VOTE 입니다."))); + } + + @Test + @DisplayName("답글인데 parentId가 null일 경우 400 반환") + void replyWithoutParentId() throws Exception { + Map req = buildValidRequest(); + req.put("isReplyRequest", true); + req.put("parentId", null); // 필수인데 없음 + assertBadCommentCreateRequest(req, "답글 작성 시 parentId는 필수입니다."); + } + + @Test + @DisplayName("일반 댓글인데 parentId가 존재할 경우 400 반환") + void rootCommentWithParentId() throws Exception { + Map req = buildValidRequest(); + req.put("isReplyRequest", false); + req.put("parentId", 1L); // 있으면 안 됨 + assertBadCommentCreateRequest(req, "일반 댓글에는 parentId가 없어야 합니다."); + } + + @Test + @DisplayName("parentId가 존재하지만 댓글이 실제 존재하지 않을 때 400 반환") + void replyToNonExistentParent() throws Exception { + Map req = buildValidRequest(); + req.put("isReplyRequest", true); + req.put("parentId", 99999L); // 존재하지 않는 parent + assertBadCommentCreateRequest(req, "parentId에 해당하는 부모 댓글이 존재해야 합니다."); + } + + @Test + @DisplayName("댓글과 부모 댓글의 게시글이 일치하지 않을 경우 400 반환") + void parentPostMismatch() throws Exception { + + // 1. 부모 댓글을 FEED에 작성 + CommentJpaEntity parentComment = commentJpaRepository.save( + TestEntityFactory.createComment(feed, user, FEED) + ); + + // 2. 답글 요청은 RECORD에 대해 요청 + Map req = new HashMap<>(); + req.put("content", "게시글 불일치"); + req.put("isReplyRequest", true); + req.put("parentId", parentComment.getCommentId()); + req.put("postType", "record"); + + mockMvc.perform(post("/comments/{postId}", record.getPostId()) + .requestAttr("userId", user.getUserId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(req))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(INVALID_COMMENT_CREATE.getCode())) + .andExpect(jsonPath("$.message", containsString("댓글과 부모 댓글의 게시글이 일치하지 않습니다."))); + } + } +} diff --git a/src/test/java/konkuk/thip/comment/adapter/in/web/CommentCreateAPITest.java b/src/test/java/konkuk/thip/comment/adapter/in/web/CommentCreateAPITest.java new file mode 100644 index 000000000..9515f17c6 --- /dev/null +++ b/src/test/java/konkuk/thip/comment/adapter/in/web/CommentCreateAPITest.java @@ -0,0 +1,150 @@ +package konkuk.thip.comment.adapter.in.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.comment.adapter.out.persistence.repository.CommentJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; +import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; +import konkuk.thip.record.adapter.out.persistence.repository.RecordJpaRepository; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; +import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; +import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; +import konkuk.thip.vote.adapter.out.persistence.repository.VoteJpaRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.Map; + +import static konkuk.thip.common.post.PostType.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +@Transactional +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@DisplayName("[통합] 댓글 생성 api 통합 테스트") +class CommentCreateAPITest { + + @Autowired + private MockMvc mockMvc; + + @Autowired private ObjectMapper objectMapper; + @Autowired private AliasJpaRepository aliasJpaRepository; + @Autowired private UserJpaRepository userJpaRepository; + @Autowired private CategoryJpaRepository categoryJpaRepository; + @Autowired private BookJpaRepository bookJpaRepository; + @Autowired private FeedJpaRepository feedJpaRepository; + @Autowired private VoteJpaRepository voteJpaRepository; + @Autowired private RecordJpaRepository recordJpaRepository; + @Autowired private CommentJpaRepository commentJpaRepository; + @Autowired private RoomJpaRepository roomJpaRepository; + @Autowired private RoomParticipantJpaRepository roomParticipantJpaRepository; + + + + private AliasJpaEntity alias; + private UserJpaEntity user; + private CategoryJpaEntity category; + private FeedJpaEntity feed; + private BookJpaEntity book; + private RecordJpaEntity record; + private VoteJpaEntity vote; + private RoomJpaEntity room; + + @BeforeEach + void setUp() { + alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + user = userJpaRepository.save(TestEntityFactory.createUser(alias)); + category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + book = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("9788954682152")); + room = roomJpaRepository.save(TestEntityFactory.createRoom(book,category)); + feed = feedJpaRepository.save(TestEntityFactory.createFeed(user,book, true)); + record = recordJpaRepository.save(TestEntityFactory.createRecord(user,room)); + vote = voteJpaRepository.save(TestEntityFactory.createVote(user,room)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); + } + + // 공통 JSON 생성 함수 + private String toJson(String content, boolean isReply, Long parentId, String postType) throws Exception { + Map req = new HashMap<>(); + req.put("content", content); + req.put("isReplyRequest", isReply); + req.put("parentId", parentId); + req.put("postType", postType); + return objectMapper.writeValueAsString(req); + } + + @Test + @DisplayName("각 게시물 타입별로 존재하는 게시물에 대해 (루트)댓글 생성을 할 수 있다.") + void createRootCommentEachPostType() throws Exception { + + // given + String[] postTypes = {"feed", "record", "vote"}; + Long[] postIds = {feed.getPostId(), record.getPostId(), vote.getPostId()}; + + // when & then + for (int i = 0; i < postTypes.length; i++) { + mockMvc.perform(post("/comments/{postId}", postIds[i]) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson("루트 댓글입니다", false, null, postTypes[i])) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.commentId").exists()); + } + } + + + @Test + @DisplayName("각 게시물 타입별로 존재하는 게시물 및 댓글에 대해 답글 생성을 할 수 있다.") + void createReplyCommentEachPostType() throws Exception { + + // given + //부모 댓글 생성 + Long feedParentId = commentJpaRepository.save(TestEntityFactory.createComment(feed,user,FEED)).getCommentId(); + Long recordParentId = commentJpaRepository.save(TestEntityFactory.createComment(record,user,RECORD)).getCommentId(); + Long voteParentId = commentJpaRepository.save(TestEntityFactory.createComment(vote,user,VOTE)).getCommentId(); + + // 답글 생성 요청 + Map[] replyRequests = new Map[]{ + Map.of("content", "Feed 답글", "isReplyRequest", true, "parentId", feedParentId, "postType", "feed"), + Map.of("content", "Record 답글", "isReplyRequest", true, "parentId", recordParentId, "postType", "record"), + Map.of("content", "Vote 답글", "isReplyRequest", true, "parentId", voteParentId, "postType", "vote") + }; + + Long[] postIds = {feed.getPostId(), record.getPostId(), vote.getPostId()}; + + for (int i = 0; i < replyRequests.length; i++) { + mockMvc.perform(post("/comments/{postId}", postIds[i]) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(replyRequests[i])) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.commentId").exists()); + } + } + +} diff --git a/src/test/java/konkuk/thip/comment/domain/CommentTest.java b/src/test/java/konkuk/thip/comment/domain/CommentTest.java new file mode 100644 index 000000000..cbd213c95 --- /dev/null +++ b/src/test/java/konkuk/thip/comment/domain/CommentTest.java @@ -0,0 +1,136 @@ +package konkuk.thip.comment.domain; + +import konkuk.thip.common.exception.InvalidStateException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static konkuk.thip.common.post.PostType.FEED; +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("[단위] Comment 단위 테스트") +class CommentTest { + + private final String CONTENT = "댓글 본문"; + private final Long CREATOR_ID = 1L; + private final Long POST_ID = 100L; + + private Comment createParentComment(Long postId) { + return Comment.builder() + .id(123L) //ID 임의 주입 + .content(CONTENT) + .targetPostId(postId) + .creatorId(CREATOR_ID) + .postType(FEED) + .parentCommentId(null) + .reportCount(0) + .likeCount(0) + .build(); + } + + @Test + @DisplayName("createComment: 일반 댓글 생성 시 parentId는 null이면 정상적으로 Comment가 생성된다.") + void createRootComment_valid() { + Comment comment = Comment.createComment( + CONTENT, + POST_ID, + CREATOR_ID, + "feed", + false, + null, + null + ); + + assertNotNull(comment); + assertNull(comment.getParentCommentId()); + assertEquals(FEED, comment.getPostType()); + assertEquals(POST_ID, comment.getTargetPostId()); + assertEquals(CONTENT, comment.getContent()); + } + + @Test + @DisplayName("createComment: 답글 생성 시 parentComment 존재 + 게시글 ID 일치하면 정상적으로 Comment가 생성된다.") + void createReplyComment_valid() { + Comment parent = createParentComment(POST_ID); + + Comment reply = Comment.createComment( + "답글입니다.", + POST_ID, + CREATOR_ID, + "feed", + true, + parent.getId(), + parent + ); + + assertNotNull(reply); + assertEquals(parent.getId(), reply.getParentCommentId()); + assertEquals(FEED, reply.getPostType()); + } + + @Test + @DisplayName("createComment: 일반 댓글 생성 시 parentId가 존재하면 InvalidStateException 이 발생한다.") + void createRootComment_withParentId_shouldFail() { + InvalidStateException ex = assertThrows(InvalidStateException.class, () -> Comment.createComment( + CONTENT, + POST_ID, + CREATOR_ID, + "feed", + false, + 99L, + null + )); + + assertEquals("일반 댓글에는 parentId가 없어야 합니다.", ex.getCause().getMessage()); + } + + @Test + @DisplayName("createComment: 답글 생성 시 parentId가 null이면 InvalidStateException 이 발생한다.") + void createReplyComment_missingParentId_shouldFail() { + InvalidStateException ex = assertThrows(InvalidStateException.class, () -> Comment.createComment( + CONTENT, + POST_ID, + CREATOR_ID, + "feed", + true, + null, + null + )); + + assertEquals("답글 작성 시 parentId는 필수입니다.", ex.getCause().getMessage()); + } + + @Test + @DisplayName("createComment: 답글 생성 시 parentComment 가 null 이면 InvalidStateException 이 발생한다.") + void createReplyComment_missingParentComment_shouldFail() { + InvalidStateException ex = assertThrows(InvalidStateException.class, () -> Comment.createComment( + CONTENT, + POST_ID, + CREATOR_ID, + "feed", + true, + 1L, + null // parentComment 누락 + )); + + assertEquals("parentId에 해당하는 부모 댓글이 존재해야 합니다.", ex.getCause().getMessage()); + } + + @Test + @DisplayName("createComment: 답글 생성 시 부모 댓글과 게시글 ID가 일치하지 않으면 InvalidStateException 이 발생한다.") + void createReplyComment_parentPostMismatch_shouldFail() { + Comment parent = createParentComment(999L); // 다른 postId + + InvalidStateException ex = assertThrows(InvalidStateException.class, () -> Comment.createComment( + CONTENT, + POST_ID, + CREATOR_ID, + "feed", + true, + parent.getId(), + parent + )); + + assertEquals("댓글과 부모 댓글의 게시글이 일치하지 않습니다.", ex.getCause().getMessage()); + } + +} diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index 240df2cd5..870c6784c 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -2,6 +2,7 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; +import konkuk.thip.common.post.PostType; import konkuk.thip.feed.adapter.out.jpa.ContentJpaEntity; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity; @@ -124,7 +125,7 @@ public static RoomJpaEntity createCustomRoom(BookJpaEntity book, CategoryJpaEnti .build(); } - public static RoomParticipantJpaEntity createUserRoom(RoomJpaEntity room, UserJpaEntity user, RoomParticipantRole roomParticipantRole, double userPercentage) { + public static RoomParticipantJpaEntity createRoomParticipant(RoomJpaEntity room, UserJpaEntity user, RoomParticipantRole roomParticipantRole, double userPercentage) { return RoomParticipantJpaEntity.builder() .userJpaEntity(user) .roomJpaEntity(room) @@ -140,6 +141,8 @@ public static RecordJpaEntity createRecord(UserJpaEntity user, RoomJpaEntity roo .userJpaEntity(user) .page(22) .isOverview(false) + .commentCount(0) + .likeCount(0) .roomJpaEntity(room) .build(); } @@ -150,15 +153,20 @@ public static VoteJpaEntity createVote(UserJpaEntity user, RoomJpaEntity room) { .userJpaEntity(user) .page(33) .isOverview(true) + .commentCount(0) + .likeCount(0) .roomJpaEntity(room) .build(); } - public static CommentJpaEntity createComment(PostJpaEntity post, UserJpaEntity user) { + public static CommentJpaEntity createComment(PostJpaEntity post, UserJpaEntity user,PostType postType) { return CommentJpaEntity.builder() .content("댓글 내용") .postJpaEntity(post) .userJpaEntity(user) + .likeCount(0) + .reportCount(0) + .postType(postType) .build(); } diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedUpdateControllerTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedUpdateControllerTest.java index 1b466ed7b..fec5d80e1 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedUpdateControllerTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedUpdateControllerTest.java @@ -118,8 +118,8 @@ void unauthorizedFeedEdit() throws Exception { .content(objectMapper.writeValueAsBytes(req)) ) .andExpect(status().isForbidden()) - .andExpect(jsonPath("$.code").value(FEED_UPDATE_FORBIDDEN.getCode())) - .andExpect(jsonPath("$.message", containsString("피드 수정 권한이 없습니다."))); + .andExpect(jsonPath("$.code").value(FEED_ACCESS_FORBIDDEN.getCode())) + .andExpect(jsonPath("$.message", containsString("피드 접근 권한이 없습니다."))); } } diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java index e1741a92c..a7bca2dfc 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java @@ -96,8 +96,8 @@ void setup() { .categoryJpaEntity(category) .build()); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room, host, RoomParticipantRole.HOST, 0.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room, member, RoomParticipantRole.MEMBER, 0.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, host, RoomParticipantRole.HOST, 0.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, member, RoomParticipantRole.MEMBER, 0.0)); } @AfterEach diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetHomeJoinedRoomsApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetHomeJoinedRoomsApiTest.java index 058cdd1e7..cf5f2ff6f 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetHomeJoinedRoomsApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetHomeJoinedRoomsApiTest.java @@ -84,11 +84,11 @@ void setUp() { room2 = roomJpaRepository.save(TestEntityFactory.createRoom(book, category)); // 1번방에 유저 1이 호스트, 유저2가 멤버 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1,user1, RoomParticipantRole.HOST, 80.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1,user2, RoomParticipantRole.MEMBER, 60.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1,user1, RoomParticipantRole.HOST, 80.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1,user2, RoomParticipantRole.MEMBER, 60.0)); // 2번방에 유저 1이 호스트 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room2,user1, RoomParticipantRole.HOST,60.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room2,user1, RoomParticipantRole.HOST,60.0)); } @AfterEach @@ -151,9 +151,9 @@ void getHomeJoinedRooms_sortByStartDateWhenUserPercentageEquals() throws Excepti ); // 모두 동일한 진행률(70%)로 참여 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1, newUser, RoomParticipantRole.MEMBER, 70.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room2, newUser, RoomParticipantRole.MEMBER, 70.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room3, newUser, RoomParticipantRole.MEMBER, 70.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1, newUser, RoomParticipantRole.MEMBER, 70.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room2, newUser, RoomParticipantRole.MEMBER, 70.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room3, newUser, RoomParticipantRole.MEMBER, 70.0)); Long userId = newUser.getUserId(); @@ -194,8 +194,8 @@ void getHomeJoinedRooms_excludeRecruitingRooms() throws Exception { TestEntityFactory.createCustomRoom(book, category, LocalDate.now().minusDays(1), LocalDate.now().plusDays(2)) ); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(recruitRoom, newUser, RoomParticipantRole.MEMBER, 20.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(activeRoom, newUser, RoomParticipantRole.MEMBER, 50.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(recruitRoom, newUser, RoomParticipantRole.MEMBER, 20.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(activeRoom, newUser, RoomParticipantRole.MEMBER, 50.0)); // when diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java index 8f58228f7..58617147f 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java @@ -107,9 +107,9 @@ void setUp() { room1 = roomJpaRepository.save(TestEntityFactory.createRoom(book, category)); // 유저1(호스트), 유저2(멤버), 유저3(멤버)로 참여 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1, user1, RoomParticipantRole.HOST, 80.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1, user2, RoomParticipantRole.MEMBER, 60.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1, user3, RoomParticipantRole.MEMBER, 50.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1, user1, RoomParticipantRole.HOST, 80.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1, user2, RoomParticipantRole.MEMBER, 60.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1, user3, RoomParticipantRole.MEMBER, 50.0)); // 팔로잉 관계 설정 @@ -201,7 +201,7 @@ void getRoomMemberList_subscriberCount() throws Exception { void getRoomMemberList_noSubscriber() throws Exception { //given UserJpaEntity userNoFollower = userJpaRepository.save(TestEntityFactory.createUser(aliasJpaRepository.findAll().get(0))); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room1, userNoFollower, RoomParticipantRole.MEMBER, 10.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room1, userNoFollower, RoomParticipantRole.MEMBER, 10.0)); Long roomId = room1.getRoomId(); //when diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java index 80c3a9d9d..5df681964 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java @@ -63,7 +63,7 @@ private void setUpWithOnlyHost() { BookJpaEntity book = bookJpaRepository.save(TestEntityFactory.createBook()); CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); createRoom(book, category,1); // 방장만 포함 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room, host, RoomParticipantRole.HOST, 0.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, host, RoomParticipantRole.HOST, 0.0)); } private void setUpWithParticipant() { @@ -73,8 +73,8 @@ private void setUpWithParticipant() { BookJpaEntity book = bookJpaRepository.save(TestEntityFactory.createBook()); CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); createRoom(book, category,2); // 방장과 참여자 포함 - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room, host, RoomParticipantRole.HOST, 0.0)); - roomParticipantJpaRepository.save(TestEntityFactory.createUserRoom(room, participant, RoomParticipantRole.MEMBER, 0.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, host, RoomParticipantRole.HOST, 0.0)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, participant, RoomParticipantRole.MEMBER, 0.0)); } private void createRoom(BookJpaEntity book, CategoryJpaEntity category, int memberCount) {