diff --git a/backend/kirikiri/build.gradle b/backend/kirikiri/build.gradle index 8325548e4..0c94c0ac0 100644 --- a/backend/kirikiri/build.gradle +++ b/backend/kirikiri/build.gradle @@ -71,10 +71,10 @@ jacocoTestReport { classDirectories.setFrom( files(classDirectories.files.collect { fileTree(dir: it, excludes: [ - "co/kirikiri/persistence/QuerydslRepositorySupporter", - "co/kirikiri/domain/**", + "co/kirikiri/common/persistence/QuerydslRepositorySupporter", + "co/kirikiri/common/service/dto/**", "co/kirikiri/**/domain/**", - "co/kirikiri/persistence/goalroom/dto/**", + "co/kirikiri/**/persistence/dto/**", "**/*Application*", "**/*Config*", "**/*Dto*", @@ -113,12 +113,12 @@ jacocoTestCoverageVerification { value = 'COVEREDRATIO' minimum = 0.80 } - + excludes = [ - "co.kirikiri.persistence.QuerydslRepositorySupporter", - "co.kirikiri.domain.**", + "co.kirikiri.common.persistence.QuerydslRepositorySupporter", + "co.kirikiri.common.service.dto.**", "co.kirikiri.**.domain.**", - "co.kirikiri.persistence.goalroom.dto.**", + "co.kirikiri.**.persistence.dto.**", "**.*Application*", "**.*Config*", "**.*Dto*", diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedController.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedController.java new file mode 100644 index 000000000..d8aa7a310 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedController.java @@ -0,0 +1,45 @@ +package co.kirikiri.checkfeed.controller; + +import co.kirikiri.checkfeed.service.GoalRoomCheckFeedService; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import co.kirikiri.common.interceptor.Authenticated; +import co.kirikiri.common.resolver.MemberIdentifier; +import java.net.URI; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/goal-rooms/{goalRoomId}/checkFeeds") +@RequiredArgsConstructor +public class GoalRoomCheckFeedController { + + private final GoalRoomCheckFeedService goalRoomCheckFeedService; + + @PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) + @Authenticated + public ResponseEntity createCheckFeed(@MemberIdentifier final String identifier, + @PathVariable("goalRoomId") final Long goalRoomId, + @ModelAttribute final CheckFeedRequest checkFeedRequest) { + final String imageUrl = goalRoomCheckFeedService.createCheckFeed(identifier, goalRoomId, checkFeedRequest); + return ResponseEntity.created(URI.create(imageUrl)).build(); + } + + @GetMapping + @Authenticated + public ResponseEntity> findGoalRoomCheckFeeds( + @MemberIdentifier final String identifier, + @PathVariable("goalRoomId") final Long goalRoomId) { + final List response = goalRoomCheckFeedService.findGoalRoomCheckFeeds(identifier, + goalRoomId); + return ResponseEntity.ok(response); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/CheckFeed.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/domain/CheckFeed.java similarity index 60% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/CheckFeed.java rename to backend/kirikiri/src/main/java/co/kirikiri/checkfeed/domain/CheckFeed.java index 9c4a6b1e5..0fa5ac4fb 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/CheckFeed.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/domain/CheckFeed.java @@ -1,18 +1,14 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.checkfeed.domain; import co.kirikiri.common.entity.BaseCreatedTimeEntity; import co.kirikiri.common.type.ImageContentType; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -30,31 +26,26 @@ public class CheckFeed extends BaseCreatedTimeEntity { private String description; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "goal_room_roadmap_node_id", nullable = false) - private GoalRoomRoadmapNode goalRoomRoadmapNode; + private Long goalRoomRoadmapNodeId; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) - @JoinColumn(name = "goal_room_member_id", nullable = false) - private GoalRoomMember goalRoomMember; + private Long goalRoomMemberId; public CheckFeed(final String serverFilePath, final ImageContentType imageContentType, final String originalFileName, final String description, - final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember goalRoomMember) { - this(serverFilePath, imageContentType, originalFileName, description, goalRoomRoadmapNode, goalRoomMember, + final Long goalRoomRoadmapNodeId, final Long goalRoomMemberId) { + this(serverFilePath, imageContentType, originalFileName, description, goalRoomRoadmapNodeId, goalRoomMemberId, null); } public CheckFeed(final String serverFilePath, final ImageContentType imageContentType, final String originalFileName, final String description, - final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember goalRoomMember, final - LocalDateTime createdAt) { + final Long goalRoomRoadmapNodeId, final Long goalRoomMemberId, final LocalDateTime createdAt) { this.serverFilePath = serverFilePath; this.imageContentType = imageContentType; this.originalFileName = originalFileName; this.description = description; - this.goalRoomRoadmapNode = goalRoomRoadmapNode; - this.goalRoomMember = goalRoomMember; + this.goalRoomRoadmapNodeId = goalRoomRoadmapNodeId; + this.goalRoomMemberId = goalRoomMemberId; this.createdAt = createdAt; } @@ -66,7 +57,7 @@ public String getDescription() { return description; } - public GoalRoomMember getGoalRoomMember() { - return goalRoomMember; + public Long getGoalRoomMemberId() { + return goalRoomMemberId; } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/persistence/CheckFeedRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/persistence/CheckFeedRepository.java new file mode 100644 index 000000000..9ce009102 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/persistence/CheckFeedRepository.java @@ -0,0 +1,35 @@ +package co.kirikiri.checkfeed.persistence; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface CheckFeedRepository extends JpaRepository { + + @Query("SELECT cf" + + " FROM CheckFeed cf" + + " WHERE cf.goalRoomMemberId = :goalRoomMemberId" + + " AND cf.createdAt >= :start" + + " AND cf.createdAt < :end") + Optional findByGoalRoomMemberIdAndDateTime(final Long goalRoomMemberId, final LocalDateTime start, + final LocalDateTime end); + + @Query("SELECT COUNT(cf)" + + " FROM CheckFeed cf" + + " WHERE cf.goalRoomMemberId = :goalRoomMemberId" + + " AND cf.goalRoomRoadmapNodeId = :goalRoomRoadmapNodeId") + int countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId(final Long goalRoomMemberId, final Long goalRoomRoadmapNodeId); + + @Query(value = "SELECT cf.* FROM check_feed as cf " + + "LEFT JOIN goal_room_member as gm ON cf.goal_room_member_id = gm.id " + + "JOIN goal_room as g ON gm.goal_room_id = g.id " + + "WHERE g.id = :goalRoomId " + + "ORDER BY cf.created_at DESC ", nativeQuery = true) + List findByGoalRoomIdOrderByCreatedAtDesc(@Param("goalRoomId") final Long goalRoomId); + + List findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(final Long goalRoomRoadmapNodeId); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListener.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListener.java new file mode 100644 index 000000000..03810dff2 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListener.java @@ -0,0 +1,74 @@ +package co.kirikiri.checkfeed.service; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.checkfeed.service.event.CheckFeedCreateEvent; +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Service +@RequiredArgsConstructor +@ExceptionConvert +public class CheckFeedCreateEventListener { + + private final CheckFeedRepository checkFeedRepository; + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + @Transactional + public void handleUpdateAccomplishmentRate(final CheckFeedCreateEvent checkFeedCreateEvent) { + final CheckFeed checkFeed = findCheckFeedById(checkFeedCreateEvent.checkFeedId()); + final GoalRoom goalRoom = findGoalRoomById(checkFeedCreateEvent.goalRoomId()); + final GoalRoomMember goalRoomMember = findGoalRoomMemberById(checkFeed.getGoalRoomMemberId()); + final GoalRoomRoadmapNode currentNode = getNodeByDate(goalRoom); + final int currentCheckCount = getCurrentCheckCount(goalRoomMember, currentNode); + + updateAccomplishmentRate(goalRoom, goalRoomMember, currentCheckCount); + } + + private CheckFeed findCheckFeedById(final Long checkFeedId) { + return checkFeedRepository.findById(checkFeedId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 인증피드입니다.")); + } + + private GoalRoom findGoalRoomById(final Long goalRoomId) { + return goalRoomRepository.findById(goalRoomId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다.")); + } + + private GoalRoomMember findGoalRoomMemberById(final Long goalRoomMemberId) { + return goalRoomMemberRepository.findById(goalRoomMemberId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸 멤버입니다.")); + } + + private GoalRoomRoadmapNode getNodeByDate(final GoalRoom goalRoom) { + return goalRoom.findNodeByDate(LocalDate.now()) + .orElseThrow(() -> new BadRequestException("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")); + } + + private int getCurrentCheckCount(final GoalRoomMember goalRoomMember, final GoalRoomRoadmapNode currentNode) { + return checkFeedRepository.countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId( + goalRoomMember.getId(), currentNode.getId()); + } + + private void updateAccomplishmentRate(final GoalRoom goalRoom, final GoalRoomMember goalRoomMember, + final int currentCheckCount) { + final int wholeCheckCount = goalRoom.getAllCheckCount(); + final Double accomplishmentRate = 100 * currentCheckCount / (double) wholeCheckCount; + + goalRoomMember.updateAccomplishmentRate(accomplishmentRate); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/DashBoardCheckFeedServiceImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/DashBoardCheckFeedServiceImpl.java new file mode 100644 index 000000000..39a54fc83 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/DashBoardCheckFeedServiceImpl.java @@ -0,0 +1,65 @@ +package co.kirikiri.checkfeed.service; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.service.FileService; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.service.DashBoardCheckFeedService; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import java.net.URL; +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +@ExceptionConvert +public class DashBoardCheckFeedServiceImpl implements DashBoardCheckFeedService { + + private final CheckFeedRepository checkFeedRepository; + private final FileService fileService; + + @Override + @Transactional(readOnly = true) + public List findCheckFeedsByNodeAndGoalRoomStatus(final GoalRoom goalRoom) { + final Optional currentGoalRoomRoadmapNode = findCurrentGoalRoomNode(goalRoom); + final List checkFeeds = findCheckFeeds(goalRoom, currentGoalRoomRoadmapNode); + return makeCheckFeedResponses(checkFeeds); + } + + private Optional findCurrentGoalRoomNode(final GoalRoom goalRoom) { + return goalRoom.findNodeByDate(LocalDate.now()); + } + + private List findCheckFeeds(final GoalRoom goalRoom, + final Optional currentGoalRoomRoadmapNode) { + if (goalRoom.isCompleted()) { + return checkFeedRepository.findByGoalRoomIdOrderByCreatedAtDesc(goalRoom.getId()); + } + if (goalRoom.isRunning() && currentGoalRoomRoadmapNode.isPresent()) { + return checkFeedRepository.findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc( + currentGoalRoomRoadmapNode.get().getId()); + } + return Collections.emptyList(); + } + + private List makeCheckFeedResponses(final List checkFeeds) { + return checkFeeds.stream() + .map(this::makeCheckFeedResponse) + .toList(); + } + + private DashBoardCheckFeedResponse makeCheckFeedResponse(final CheckFeed checkFeed) { + final URL checkFeedImageUrl = fileService.generateUrl(checkFeed.getServerFilePath(), HttpMethod.GET); + return new DashBoardCheckFeedResponse(checkFeed.getId(), checkFeedImageUrl.toExternalForm(), + checkFeed.getDescription(), checkFeed.getCreatedAt().toLocalDate()); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedService.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedService.java new file mode 100644 index 000000000..eacf550c1 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedService.java @@ -0,0 +1,212 @@ +package co.kirikiri.checkfeed.service; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.checkfeed.service.dto.CheckFeedDto; +import co.kirikiri.checkfeed.service.dto.CheckFeedMemberDto; +import co.kirikiri.checkfeed.service.dto.GoalRoomCheckFeedDto; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import co.kirikiri.checkfeed.service.event.CheckFeedCreateEvent; +import co.kirikiri.checkfeed.service.mapper.CheckFeedMapper; +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.ForbiddenException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.service.FilePathGenerator; +import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.FileInformation; +import co.kirikiri.common.type.ImageContentType; +import co.kirikiri.common.type.ImageDirType; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.persistence.MemberRepository; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Transactional +@RequiredArgsConstructor +@ExceptionConvert +public class GoalRoomCheckFeedService { + + private final CheckFeedRepository checkFeedRepository; + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final MemberRepository memberRepository; + private final FileService fileService; + private final FilePathGenerator filePathGenerator; + private final ApplicationEventPublisher applicationEventPublisher; + + public String createCheckFeed(final String identifier, final Long goalRoomId, + final CheckFeedRequest checkFeedRequest) { + final MultipartFile checkFeedImage = checkFeedRequest.image(); + validateEmptyImage(checkFeedImage); + + final GoalRoom goalRoom = findGoalRoomWithNodesById(goalRoomId); + final GoalRoomMember goalRoomMember = findGoalRoomMemberByGoalRoomAndIdentifier(goalRoom, identifier); + final GoalRoomRoadmapNode currentNode = getNodeByDate(goalRoom); + final int currentMemberCheckCount = getCurrentMemberCheckCount(goalRoomMember, currentNode); + validateCheckCount(currentMemberCheckCount, goalRoomMember, currentNode); + + final String path = filePathGenerator.makeFilePath(ImageDirType.CHECK_FEED, + checkFeedImage.getOriginalFilename()); + final CheckFeed checkFeed = saveCheckFeed(checkFeedRequest, checkFeedImage, goalRoomMember, currentNode, path); + + applicationEventPublisher.publishEvent(new CheckFeedCreateEvent(checkFeed.getId(), goalRoomId)); + + final FileInformation fileInformation = FileInformation.from(checkFeedImage); + fileService.save(path, fileInformation); + return fileService.generateUrl(path, HttpMethod.GET).toExternalForm(); + } + + private void validateEmptyImage(final MultipartFile image) { + if (image.isEmpty()) { + throw new BadRequestException("인증 피드 등록 시 이미지가 반드시 포함되어야 합니다."); + } + + if (image.getOriginalFilename() == null) { + throw new BadRequestException("파일 이름은 반드시 포함되어야 합니다."); + } + } + + private GoalRoom findGoalRoomWithNodesById(final Long goalRoomId) { + return goalRoomRepository.findByIdWithNodes(goalRoomId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); + } + + private GoalRoomMember findGoalRoomMemberByGoalRoomAndIdentifier(final GoalRoom goalRoom, final String identifier) { + final Member member = findMemberByIdentifier(identifier); + return goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, member.getId()) + .orElseThrow(() -> new NotFoundException("골룸에 해당 사용자가 존재하지 않습니다. 사용자 아이디 = " + identifier)); + } + + private Member findMemberByIdentifier(final String identifier) { + return memberRepository.findByIdentifier(new Identifier(identifier)) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다. identifier = " + identifier)); + } + + private GoalRoomRoadmapNode getNodeByDate(final GoalRoom goalRoom) { + return goalRoom.findNodeByDate(LocalDate.now()) + .orElseThrow(() -> new BadRequestException("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")); + } + + private int getCurrentMemberCheckCount(final GoalRoomMember goalRoomMember, final GoalRoomRoadmapNode currentNode) { + return checkFeedRepository.countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId( + goalRoomMember.getId(), currentNode.getId()); + } + + private void validateCheckCount(final int memberCheckCount, final GoalRoomMember member, + final GoalRoomRoadmapNode goalRoomRoadmapNode) { + validateNodeCheckCount(memberCheckCount, goalRoomRoadmapNode); + validateTodayCheckCount(member); + } + + private void validateNodeCheckCount(final int memberCheckCount, + final GoalRoomRoadmapNode goalRoomRoadmapNode) { + if (memberCheckCount >= goalRoomRoadmapNode.getCheckCount()) { + throw new BadRequestException( + "이번 노드에는 최대 " + goalRoomRoadmapNode.getCheckCount() + "번만 인증 피드를 등록할 수 있습니다."); + } + } + + private void validateTodayCheckCount(final GoalRoomMember goalRoomMember) { + final LocalDate today = LocalDate.now(); + final LocalDateTime todayStart = today.atStartOfDay(); + final LocalDateTime todayEnd = today.plusDays(1).atStartOfDay(); + if (checkFeedRepository.findByGoalRoomMemberIdAndDateTime(goalRoomMember.getId(), todayStart, todayEnd) + .isPresent()) { + throw new BadRequestException("이미 오늘 인증 피드를 등록하였습니다."); + } + } + + private CheckFeed saveCheckFeed(final CheckFeedRequest checkFeedRequest, final MultipartFile checkFeedImage, + final GoalRoomMember goalRoomMember, final GoalRoomRoadmapNode currentNode, + final String path) { + return checkFeedRepository.save( + new CheckFeed(path, ImageContentType.findImageContentType(checkFeedImage.getContentType()), + checkFeedImage.getOriginalFilename(), checkFeedRequest.description(), currentNode.getId(), + goalRoomMember.getId())); + } + + @Transactional(readOnly = true) + public List findGoalRoomCheckFeeds(final String identifier, final Long goalRoomId) { + final GoalRoom goalRoom = findGoalRoomWithNodesById(goalRoomId); + validateJoinedMemberInRunningGoalRoom(goalRoom, identifier); + final Optional currentGoalRoomRoadmapNode = findCurrentGoalRoomNode(goalRoom); + final List checkFeeds = findCheckFeedsByGoalRoomRoadmapNode(goalRoom, currentGoalRoomRoadmapNode); + final List goalRoomCheckFeedDtos = makeGoalRoomCheckFeedDtos(checkFeeds); + return CheckFeedMapper.convertToGoalRoomCheckFeedResponses(goalRoomCheckFeedDtos); + } + + private void validateJoinedMemberInRunningGoalRoom(final GoalRoom goalRoom, final String identifier) { + final Member member = findMemberByIdentifier(identifier); + if (goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, member.getId()) + .isEmpty()) { + throw new ForbiddenException("골룸에 참여하지 않은 회원입니다."); + } + } + + private Optional findCurrentGoalRoomNode(final GoalRoom goalRoom) { + return goalRoom.findNodeByDate(LocalDate.now()); + } + + private List findCheckFeedsByGoalRoomRoadmapNode(final GoalRoom goalRoom, + final Optional currentGoalRoomRoadmapNode) { + if (goalRoom.isCompleted()) { + return checkFeedRepository.findByGoalRoomIdOrderByCreatedAtDesc(goalRoom.getId()); + } + if (goalRoom.isRunning() && currentGoalRoomRoadmapNode.isPresent()) { + return checkFeedRepository.findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc( + currentGoalRoomRoadmapNode.get().getId()); + } + return Collections.emptyList(); + } + + private List makeGoalRoomCheckFeedDtos(final List checkFeeds) { + return checkFeeds.stream() + .map(this::makeGoalRoomCheckFeedDto) + .toList(); + } + + private GoalRoomCheckFeedDto makeGoalRoomCheckFeedDto(final CheckFeed checkFeed) { + final GoalRoomMember goalRoomMember = findGoalRoomMemberById(checkFeed.getGoalRoomMemberId()); + final Member member = findMemberWithProfileAndImageById(goalRoomMember.getMemberId()); + + final URL memberImageUrl = fileService.generateUrl(member.getImage().getServerFilePath(), HttpMethod.GET); + + return new GoalRoomCheckFeedDto(new CheckFeedMemberDto(member.getId(), member.getNickname().getValue(), + memberImageUrl.toExternalForm()), makeCheckFeedDto(checkFeed)); + } + + private GoalRoomMember findGoalRoomMemberById(final Long goalRoomMemberId) { + return goalRoomMemberRepository.findById(goalRoomMemberId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸 멤버입니다. goalRoomMemberId = " + goalRoomMemberId)); + } + + private Member findMemberWithProfileAndImageById(final Long memberId) { + return memberRepository.findWithMemberProfileAndImageById(memberId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다. memberId = " + memberId)); + } + + private CheckFeedDto makeCheckFeedDto(final CheckFeed checkFeed) { + final URL checkFeedImageUrl = fileService.generateUrl(checkFeed.getServerFilePath(), HttpMethod.GET); + return new CheckFeedDto(checkFeed.getId(), checkFeedImageUrl.toExternalForm(), + checkFeed.getDescription(), checkFeed.getCreatedAt()); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/CheckFeedDto.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedDto.java similarity index 79% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/CheckFeedDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedDto.java index a1a3279eb..a334af4eb 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/CheckFeedDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.checkfeed.service.dto; import java.time.LocalDateTime; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedMemberDto.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedMemberDto.java new file mode 100644 index 000000000..c1353c618 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/CheckFeedMemberDto.java @@ -0,0 +1,9 @@ +package co.kirikiri.checkfeed.service.dto; + +public record CheckFeedMemberDto( + long id, + String name, + String imageUrl +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/GoalRoomCheckFeedDto.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/GoalRoomCheckFeedDto.java new file mode 100644 index 000000000..62cd956df --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/GoalRoomCheckFeedDto.java @@ -0,0 +1,8 @@ +package co.kirikiri.checkfeed.service.dto; + +public record GoalRoomCheckFeedDto( + CheckFeedMemberDto memberDto, + CheckFeedDto checkFeedDto +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/CheckFeedRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/request/CheckFeedRequest.java similarity index 75% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/CheckFeedRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/request/CheckFeedRequest.java index 439284a88..fb4f8bba4 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/CheckFeedRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/request/CheckFeedRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.request; +package co.kirikiri.checkfeed.service.dto.request; import org.springframework.web.multipart.MultipartFile; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedMemberResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedMemberResponse.java new file mode 100644 index 000000000..27337783b --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedMemberResponse.java @@ -0,0 +1,9 @@ +package co.kirikiri.checkfeed.service.dto.response; + +public record CheckFeedMemberResponse( + long id, + String name, + String imageUrl +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/CheckFeedResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedResponse.java similarity index 76% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/CheckFeedResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedResponse.java index 0b2fa2869..a9a87aa9b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/CheckFeedResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/CheckFeedResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.checkfeed.service.dto.response; import java.time.LocalDate; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/GoalRoomCheckFeedResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/GoalRoomCheckFeedResponse.java new file mode 100644 index 000000000..9e74ccd98 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/dto/response/GoalRoomCheckFeedResponse.java @@ -0,0 +1,8 @@ +package co.kirikiri.checkfeed.service.dto.response; + +public record GoalRoomCheckFeedResponse( + CheckFeedMemberResponse member, + CheckFeedResponse checkFeed +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/event/CheckFeedCreateEvent.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/event/CheckFeedCreateEvent.java new file mode 100644 index 000000000..024471040 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/event/CheckFeedCreateEvent.java @@ -0,0 +1,8 @@ +package co.kirikiri.checkfeed.service.event; + +public record CheckFeedCreateEvent( + Long checkFeedId, + Long goalRoomId +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/mapper/CheckFeedMapper.java b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/mapper/CheckFeedMapper.java new file mode 100644 index 000000000..21cf46e62 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/checkfeed/service/mapper/CheckFeedMapper.java @@ -0,0 +1,35 @@ +package co.kirikiri.checkfeed.service.mapper; + +import co.kirikiri.checkfeed.service.dto.CheckFeedDto; +import co.kirikiri.checkfeed.service.dto.CheckFeedMemberDto; +import co.kirikiri.checkfeed.service.dto.GoalRoomCheckFeedDto; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedMemberResponse; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedResponse; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import java.util.List; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CheckFeedMapper { + + public static List convertToGoalRoomCheckFeedResponses( + final List checkFeeds) { + return checkFeeds.stream() + .map(CheckFeedMapper::convertToGoalRoomCheckFeedResponse) + .toList(); + } + + private static GoalRoomCheckFeedResponse convertToGoalRoomCheckFeedResponse( + final GoalRoomCheckFeedDto goalRoomCheckFeedDto) { + final CheckFeedMemberDto memberDto = goalRoomCheckFeedDto.memberDto(); + final CheckFeedMemberResponse memberResponse = new CheckFeedMemberResponse(memberDto.id(), memberDto.name(), + memberDto.imageUrl()); + + final CheckFeedDto checkFeedDto = goalRoomCheckFeedDto.checkFeedDto(); + final CheckFeedResponse checkFeedResponse = new CheckFeedResponse(checkFeedDto.id(), checkFeedDto.imageUrl(), + checkFeedDto.description(), checkFeedDto.createdAt().toLocalDate()); + + return new GoalRoomCheckFeedResponse(memberResponse, checkFeedResponse); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/config/WebConfig.java b/backend/kirikiri/src/main/java/co/kirikiri/common/config/WebConfig.java index abeebf372..73fca7c9f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/common/config/WebConfig.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/config/WebConfig.java @@ -2,7 +2,7 @@ import co.kirikiri.common.interceptor.AuthInterceptor; import co.kirikiri.common.resolver.MemberIdentifierArgumentResolver; -import co.kirikiri.roadmap.resolver.RoadmapSaveArgumentResolver; +import co.kirikiri.common.resolver.RoadmapSaveArgumentResolver; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/controller/GlobalExceptionHandler.java b/backend/kirikiri/src/main/java/co/kirikiri/common/controller/GlobalExceptionHandler.java index 24aa239ee..f24f97cbd 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/common/controller/GlobalExceptionHandler.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/controller/GlobalExceptionHandler.java @@ -6,8 +6,9 @@ import co.kirikiri.common.exception.ForbiddenException; import co.kirikiri.common.exception.NotFoundException; import co.kirikiri.common.exception.ServerException; -import co.kirikiri.service.dto.ErrorResponse; +import co.kirikiri.common.service.dto.ErrorResponse; import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -15,7 +16,6 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import java.util.List; @RestControllerAdvice public class GlobalExceptionHandler { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/exception/DomainException.java b/backend/kirikiri/src/main/java/co/kirikiri/common/exception/DomainException.java new file mode 100644 index 000000000..e69de29bb diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/infra/AmazonS3FileService.java b/backend/kirikiri/src/main/java/co/kirikiri/common/infra/AmazonS3FileService.java index 072106573..e603b2d28 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/common/infra/AmazonS3FileService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/infra/AmazonS3FileService.java @@ -2,18 +2,18 @@ import co.kirikiri.common.exception.ServerException; import co.kirikiri.common.service.FileService; -import co.kirikiri.service.dto.FileInformation; +import co.kirikiri.common.service.dto.FileInformation; import com.amazonaws.SdkClientException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.amazonaws.services.s3.model.ObjectMetadata; +import java.io.InputStream; +import java.net.URL; +import java.util.Date; import lombok.RequiredArgsConstructor; import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; -import java.io.InputStream; -import java.net.URL; -import java.util.Date; @Service @RequiredArgsConstructor diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/QuerydslRepositorySupporter.java b/backend/kirikiri/src/main/java/co/kirikiri/common/persistence/QuerydslRepositorySupporter.java similarity index 98% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/QuerydslRepositorySupporter.java rename to backend/kirikiri/src/main/java/co/kirikiri/common/persistence/QuerydslRepositorySupporter.java index bef0cb5b1..a5d6a7a3f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/QuerydslRepositorySupporter.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/persistence/QuerydslRepositorySupporter.java @@ -1,4 +1,4 @@ -package co.kirikiri.persistence; +package co.kirikiri.common.persistence; import co.kirikiri.common.exception.ServerException; import com.querydsl.core.types.EntityPath; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolver.java b/backend/kirikiri/src/main/java/co/kirikiri/common/resolver/RoadmapSaveArgumentResolver.java similarity index 81% rename from backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolver.java rename to backend/kirikiri/src/main/java/co/kirikiri/common/resolver/RoadmapSaveArgumentResolver.java index 670ad0e4f..7ad2b1c12 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolver.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/resolver/RoadmapSaveArgumentResolver.java @@ -1,6 +1,7 @@ -package co.kirikiri.roadmap.resolver; +package co.kirikiri.common.resolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver; public interface RoadmapSaveArgumentResolver extends HandlerMethodArgumentResolver { + } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/service/FileService.java b/backend/kirikiri/src/main/java/co/kirikiri/common/service/FileService.java index 8965f8f26..c381550c8 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/common/service/FileService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/service/FileService.java @@ -1,8 +1,8 @@ package co.kirikiri.common.service; -import co.kirikiri.service.dto.FileInformation; -import org.springframework.http.HttpMethod; +import co.kirikiri.common.service.dto.FileInformation; import java.net.URL; +import org.springframework.http.HttpMethod; public interface FileService { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/UUIDFilePathGenerator.java b/backend/kirikiri/src/main/java/co/kirikiri/common/service/UUIDFilePathGenerator.java similarity index 95% rename from backend/kirikiri/src/main/java/co/kirikiri/service/UUIDFilePathGenerator.java rename to backend/kirikiri/src/main/java/co/kirikiri/common/service/UUIDFilePathGenerator.java index c3258879d..0df4ecd3b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/UUIDFilePathGenerator.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/service/UUIDFilePathGenerator.java @@ -1,12 +1,11 @@ -package co.kirikiri.service; +package co.kirikiri.common.service; import co.kirikiri.common.exception.BadRequestException; -import co.kirikiri.common.service.FilePathGenerator; import co.kirikiri.common.type.ImageDirType; -import org.springframework.stereotype.Component; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.UUID; +import org.springframework.stereotype.Component; @Component public class UUIDFilePathGenerator implements FilePathGenerator { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/CustomScrollRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/CustomScrollRequest.java similarity index 83% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/CustomScrollRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/CustomScrollRequest.java index f76ab1dd8..de8c7baec 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/CustomScrollRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/CustomScrollRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto; +package co.kirikiri.common.service.dto; import jakarta.validation.constraints.NotNull; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/ErrorResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/ErrorResponse.java similarity index 92% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/ErrorResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/ErrorResponse.java index 789d0f211..42624d206 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/ErrorResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/ErrorResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto; +package co.kirikiri.common.service.dto; import java.util.Objects; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/FileInformation.java b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/FileInformation.java new file mode 100644 index 000000000..cb31804d3 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/common/service/dto/FileInformation.java @@ -0,0 +1,23 @@ +package co.kirikiri.common.service.dto; + +import co.kirikiri.common.exception.ServerException; +import java.io.IOException; +import java.io.InputStream; +import org.springframework.web.multipart.MultipartFile; + +public record FileInformation( + String originalFileName, + long size, + String contentType, + InputStream inputStream +) { + + public static FileInformation from(final MultipartFile multipartFile) { + try { + return new FileInformation(multipartFile.getOriginalFilename(), multipartFile.getSize(), + multipartFile.getContentType(), multipartFile.getInputStream()); + } catch (final IOException exception) { + throw new ServerException(exception.getMessage()); + } + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoom.java b/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoom.java deleted file mode 100644 index 08e586002..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoom.java +++ /dev/null @@ -1,285 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import co.kirikiri.common.entity.BaseUpdatedTimeEntity; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.member.domain.Member; -import co.kirikiri.roadmap.domain.RoadmapContent; -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GoalRoom extends BaseUpdatedTimeEntity { - - private static final int DATE_OFFSET = 1; - @Embedded - private final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(); - @Embedded - private final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(); - @Embedded - private final GoalRoomToDos goalRoomToDos = new GoalRoomToDos(); - @Embedded - private final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes(); - @Embedded - private GoalRoomName name; - @Embedded - private LimitedMemberCount limitedMemberCount; - @Enumerated(value = EnumType.STRING) - private GoalRoomStatus status = GoalRoomStatus.RECRUITING; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "roadmap_content_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - private RoadmapContent roadmapContent; - @Column(nullable = false) - private LocalDate startDate; - @Column(nullable = false) - private LocalDate endDate; - - public GoalRoom(final GoalRoomName name, final LimitedMemberCount limitedMemberCount, - final RoadmapContent roadmapContent, final Member member) { - this(null, name, limitedMemberCount, roadmapContent, member); - } - - public GoalRoom(final Long id, final GoalRoomName name, final LimitedMemberCount limitedMemberCount, - final RoadmapContent roadmapContent, final Member member) { - this.id = id; - this.name = name; - this.limitedMemberCount = limitedMemberCount; - this.roadmapContent = roadmapContent; - updateLeader(member); - } - - private void updateLeader(final Member member) { - final GoalRoomPendingMember leader = new GoalRoomPendingMember(GoalRoomRole.LEADER, member); - leader.initGoalRoom(this); - goalRoomPendingMembers.add(leader); - } - - public void join(final Member member) { - final GoalRoomPendingMember newMember = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, member); - newMember.initGoalRoom(this); - validateJoinGoalRoom(newMember); - goalRoomPendingMembers.add(newMember); - } - - private void validateJoinGoalRoom(final GoalRoomPendingMember member) { - validateMemberCount(); - validateStatus(); - validateAlreadyParticipated(member); - } - - private void validateMemberCount() { - if (getCurrentMemberCount() >= limitedMemberCount.getValue()) { - throw new GoalRoomException("제한 인원이 꽉 찬 골룸에는 참여할 수 없습니다."); - } - } - - private void validateStatus() { - if (status != GoalRoomStatus.RECRUITING) { - throw new GoalRoomException("모집 중이지 않은 골룸에는 참여할 수 없습니다."); - } - } - - private void validateAlreadyParticipated(final GoalRoomPendingMember member) { - if (goalRoomPendingMembers.containGoalRoomPendingMember(member)) { - throw new GoalRoomException("이미 참여한 골룸에는 참여할 수 없습니다."); - } - } - - public void start() { - this.status = GoalRoomStatus.RUNNING; - } - - public void complete() { - this.status = GoalRoomStatus.COMPLETED; - } - - public int calculateTotalPeriod() { - return (int) ChronoUnit.DAYS.between(startDate, endDate) + DATE_OFFSET; - } - - public int getAllCheckCount() { - return goalRoomRoadmapNodes.calculateAllCheckCount(); - } - - public boolean isRecruiting() { - return status == GoalRoomStatus.RECRUITING; - } - - public boolean isRunning() { - return status == GoalRoomStatus.RUNNING; - } - - public void addAllGoalRoomRoadmapNodes(final GoalRoomRoadmapNodes goalRoomRoadmapNodes) { - checkTotalSize(goalRoomRoadmapNodes.size() + this.goalRoomRoadmapNodes.size()); - this.goalRoomRoadmapNodes.addAll(goalRoomRoadmapNodes); - this.startDate = goalRoomRoadmapNodes.getGoalRoomStartDate(); - this.endDate = goalRoomRoadmapNodes.getGoalRoomEndDate(); - } - - private void checkTotalSize(final int totalSize) { - if (totalSize > roadmapContent.nodesSize()) { - throw new GoalRoomException("로드맵의 노드 수보다 골룸의 노드 수가 큽니다."); - } - } - - public void addGoalRoomTodo(final GoalRoomToDo goalRoomToDo) { - goalRoomToDos.add(goalRoomToDo); - } - - public Member findGoalRoomLeader() { - if (status == GoalRoomStatus.RECRUITING) { - return goalRoomPendingMembers.findGoalRoomLeader(); - } - return goalRoomMembers.findGoalRoomLeader(); - } - - public boolean isNotLeader(final Member member) { - if (status == GoalRoomStatus.RECRUITING) { - return goalRoomPendingMembers.isNotLeader(member); - } - return goalRoomMembers.isNotLeader(member); - } - - public boolean isCompleted() { - return this.status == GoalRoomStatus.COMPLETED; - } - - public GoalRoomToDo findLastGoalRoomTodo() { - return goalRoomToDos.findLast(); - } - - public Optional findNodeByDate(final LocalDate date) { - return goalRoomRoadmapNodes.getNodeByDate(date); - } - - public Integer getCurrentMemberCount() { - if (status == GoalRoomStatus.RECRUITING) { - return goalRoomPendingMembers.size(); - } - return goalRoomMembers.size(); - } - - public void addAllGoalRoomMembers(final List members) { - this.goalRoomMembers.addAll(new ArrayList<>(members)); - } - - public boolean isGoalRoomMember(final Member member) { - if (status == GoalRoomStatus.RECRUITING) { - return goalRoomPendingMembers.isMember(member); - } - return goalRoomMembers.isMember(member); - } - - public void leave(final Member member) { - if (status == GoalRoomStatus.RECRUITING) { - final GoalRoomPendingMember goalRoomPendingMember = findGoalRoomPendingMemberByMember(member); - changeRoleIfLeaderLeave(goalRoomPendingMembers, goalRoomPendingMember); - goalRoomPendingMembers.remove(goalRoomPendingMember); - return; - } - final GoalRoomMember goalRoomMember = findGoalRoomMemberByMember(member); - changeRoleIfLeaderLeave(goalRoomMembers, goalRoomMember); - goalRoomMembers.remove(goalRoomMember); - } - - public boolean cannotStart() { - return startDate.isAfter(LocalDate.now()); - } - - private GoalRoomPendingMember findGoalRoomPendingMemberByMember(final Member member) { - return goalRoomPendingMembers.findByMember(member) - .orElseThrow(() -> new GoalRoomException("골룸에 참여한 사용자가 아닙니다. memberId = " + member.getId())); - } - - private void changeRoleIfLeaderLeave(final GoalRoomPendingMembers goalRoomPendingMembers, - final GoalRoomPendingMember goalRoomPendingMember) { - if (goalRoomPendingMember.isLeader()) { - goalRoomPendingMembers.findNextLeader() - .ifPresent(GoalRoomPendingMember::becomeLeader); - } - } - - private GoalRoomMember findGoalRoomMemberByMember(final Member member) { - return goalRoomMembers.findByMember(member) - .orElseThrow(() -> new GoalRoomException("골룸에 참여한 사용자가 아닙니다. memberId = " + member.getId())); - } - - private void changeRoleIfLeaderLeave(final GoalRoomMembers goalRoomMembers, - final GoalRoomMember goalRoomMember) { - if (goalRoomMember.isLeader()) { - goalRoomMembers.findNextLeader() - .ifPresent(GoalRoomMember::becomeLeader); - } - } - - public boolean isEmptyGoalRoom() { - return goalRoomPendingMembers.isEmpty() && goalRoomMembers.isEmpty(); - } - - public Optional findGoalRoomTodoByTodoId(final Long todoId) { - return goalRoomToDos.findById(todoId); - } - - public void deleteAllPendingMembers() { - goalRoomPendingMembers.deleteAll(); - } - - public boolean isCompletedAfterMonths(final long numberOfMonth) { - final LocalDate currentDate = LocalDate.now(); - return currentDate.isAfter(endDate.plusMonths(numberOfMonth)); - } - - public GoalRoomName getName() { - return name; - } - - public LimitedMemberCount getLimitedMemberCount() { - return limitedMemberCount; - } - - public LocalDate getStartDate() { - return startDate; - } - - public LocalDate getEndDate() { - return endDate; - } - - public GoalRoomStatus getStatus() { - return status; - } - - public RoadmapContent getRoadmapContent() { - return roadmapContent; - } - - public GoalRoomRoadmapNodes getGoalRoomRoadmapNodes() { - return goalRoomRoadmapNodes; - } - - public GoalRoomToDos getGoalRoomToDos() { - return goalRoomToDos; - } - - public GoalRoomPendingMembers getGoalRoomPendingMembers() { - return goalRoomPendingMembers; - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMembers.java b/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMembers.java deleted file mode 100644 index 286badcd2..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMembers.java +++ /dev/null @@ -1,88 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import co.kirikiri.common.exception.domain.UnexpectedDomainException; -import co.kirikiri.member.domain.Member; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToMany; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.BatchSize; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -@Embeddable -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GoalRoomMembers { - - private static final int MIN_SIZE_TO_FIND_NEXT_LEADER = 1; - - @BatchSize(size = 20) - @OneToMany(fetch = FetchType.LAZY, - cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, - orphanRemoval = true, mappedBy = "goalRoom") - private final List values = new ArrayList<>(); - - public GoalRoomMembers(final List values) { - this.values.addAll(new ArrayList<>(values)); - } - - public void add(final GoalRoomMember goalRoomMember) { - this.values.add(goalRoomMember); - } - - public void addAll(final List goalRoomMembers) { - this.values.addAll(new ArrayList<>(goalRoomMembers)); - } - - public Optional findByMember(final Member member) { - return values.stream() - .filter(value -> value.isSameMember(member)) - .findFirst(); - } - - public Optional findNextLeader() { - if (size() <= MIN_SIZE_TO_FIND_NEXT_LEADER) { - return Optional.empty(); - } - values.sort(Comparator.comparing(GoalRoomMember::getJoinedAt)); - return Optional.of(values.get(1)); - } - - public boolean isMember(final Member member) { - return values.stream() - .anyMatch(value -> value.isSameMember(member)); - } - - public boolean isNotLeader(final Member member) { - final Member goalRoomLeader = findGoalRoomLeader(); - return !goalRoomLeader.equals(member); - } - - public Member findGoalRoomLeader() { - return values.stream() - .filter(GoalRoomMember::isLeader) - .findFirst() - .map(GoalRoomMember::getMember) - .orElseThrow(() -> new UnexpectedDomainException("골룸의 리더가 없습니다.")); - } - - public int size() { - return values.size(); - } - - public void remove(final GoalRoomMember goalRoomMember) { - values.remove(goalRoomMember); - } - - public boolean isEmpty() { - return values.isEmpty(); - } - - public List getValues() { - return values; - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMember.java b/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMember.java deleted file mode 100644 index 821dc3b2d..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMember.java +++ /dev/null @@ -1,36 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import co.kirikiri.member.domain.Member; -import jakarta.persistence.Entity; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import java.time.LocalDateTime; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GoalRoomPendingMember extends BaseGoalRoomMember { - - public GoalRoomPendingMember(final GoalRoomRole role, final Member member) { - super(role, null, null, member); - } - - public GoalRoomPendingMember(final GoalRoomRole role, final GoalRoom goalRoom, final Member member) { - super(role, null, goalRoom, member); - } - - public GoalRoomPendingMember(final GoalRoomRole role, final LocalDateTime joinedAt, - final GoalRoom goalRoom, final Member member) { - super(role, joinedAt, goalRoom, member); - } - - public GoalRoomPendingMember(final Long id, final GoalRoomRole role, final LocalDateTime joinedAt, - final GoalRoom goalRoom, final Member member) { - super(id, role, joinedAt, goalRoom, member); - } - - public void initGoalRoom(final GoalRoom goalRoom) { - if (this.goalRoom == null) { - this.goalRoom = goalRoom; - } - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembers.java b/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembers.java deleted file mode 100644 index fbd3b2028..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembers.java +++ /dev/null @@ -1,93 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import co.kirikiri.common.exception.domain.UnexpectedDomainException; -import co.kirikiri.member.domain.Member; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToMany; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.BatchSize; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -@Embeddable -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GoalRoomPendingMembers { - - private static final int MIN_SIZE_TO_FIND_NEXT_LEADER = 1; - - @BatchSize(size = 20) - @OneToMany(fetch = FetchType.LAZY, - cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, - orphanRemoval = true, mappedBy = "goalRoom") - private final List values = new ArrayList<>(); - - public GoalRoomPendingMembers(final List values) { - this.values.addAll(new ArrayList<>(values)); - } - - public void add(final GoalRoomPendingMember goalRoomPendingMember) { - values.add(goalRoomPendingMember); - } - - public boolean containGoalRoomPendingMember(final GoalRoomPendingMember goalRoomPendingMember) { - return values.stream() - .anyMatch(value -> value.isSameMember(goalRoomPendingMember.getMember())); - } - - public boolean isMember(final Member member) { - return values.stream() - .anyMatch(value -> value.isSameMember(member)); - } - - public int size() { - return values.size(); - } - - public Member findGoalRoomLeader() { - return values.stream() - .filter(GoalRoomPendingMember::isLeader) - .findFirst() - .map(GoalRoomPendingMember::getMember) - .orElseThrow(() -> new UnexpectedDomainException("골룸의 리더가 없습니다.")); - } - - public boolean isNotLeader(final Member member) { - final Member goalRoomLeader = findGoalRoomLeader(); - return !goalRoomLeader.equals(member); - } - - public Optional findByMember(final Member member) { - return values.stream() - .filter(value -> value.isSameMember(member)) - .findFirst(); - } - - public Optional findNextLeader() { - if (size() <= MIN_SIZE_TO_FIND_NEXT_LEADER) { - return Optional.empty(); - } - values.sort(Comparator.comparing(GoalRoomPendingMember::getJoinedAt)); - return Optional.of(values.get(1)); - } - - public void remove(final GoalRoomPendingMember goalRoomPendingMember) { - values.remove(goalRoomPendingMember); - } - - public boolean isEmpty() { - return values.isEmpty(); - } - - public void deleteAll() { - this.values.clear(); - } - - public List getValues() { - return new ArrayList<>(values); - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/controller/GoalRoomController.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/controller/GoalRoomController.java similarity index 59% rename from backend/kirikiri/src/main/java/co/kirikiri/controller/GoalRoomController.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/controller/GoalRoomController.java index 896caec93..b555ae1c5 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/controller/GoalRoomController.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/controller/GoalRoomController.java @@ -1,38 +1,31 @@ -package co.kirikiri.controller; +package co.kirikiri.goalroom.controller; import co.kirikiri.common.interceptor.Authenticated; import co.kirikiri.common.resolver.MemberIdentifier; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomStatusTypeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import co.kirikiri.service.goalroom.GoalRoomCreateService; -import co.kirikiri.service.goalroom.GoalRoomReadService; +import co.kirikiri.goalroom.service.GoalRoomCreateService; +import co.kirikiri.goalroom.service.GoalRoomReadService; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomStatusTypeRequest; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; import jakarta.validation.Valid; +import java.net.URI; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.net.URI; -import java.util.List; @RestController @RequestMapping("/goal-rooms") @@ -58,34 +51,6 @@ public ResponseEntity joinGoalRoom(@MemberIdentifier final String identifi return ResponseEntity.status(HttpStatus.OK).build(); } - @PostMapping("/{goalRoomId}/todos") - @Authenticated - public ResponseEntity addTodo(@RequestBody @Valid final GoalRoomTodoRequest goalRoomTodoRequest, - @PathVariable final Long goalRoomId, - @MemberIdentifier final String identifier) { - final Long id = goalRoomCreateService.addGoalRoomTodo(goalRoomId, identifier, goalRoomTodoRequest); - return ResponseEntity.created(URI.create("/api/goal-rooms/" + goalRoomId + "/todos/" + id)).build(); - } - - @PostMapping("/{goalRoomId}/todos/{todoId}") - @Authenticated - public ResponseEntity checkTodo(@PathVariable final Long goalRoomId, - @PathVariable final Long todoId, - @MemberIdentifier final String identifier) { - final GoalRoomToDoCheckResponse checkResponse = goalRoomCreateService.checkGoalRoomTodo(goalRoomId, todoId, - identifier); - return ResponseEntity.ok(checkResponse); - } - - @PostMapping(value = "/{goalRoomId}/checkFeeds", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) - @Authenticated - public ResponseEntity createCheckFeed(@MemberIdentifier final String identifier, - @PathVariable("goalRoomId") final Long goalRoomId, - @ModelAttribute final CheckFeedRequest checkFeedRequest) { - final String imageUrl = goalRoomCreateService.createCheckFeed(identifier, goalRoomId, checkFeedRequest); - return ResponseEntity.created(URI.create(imageUrl)).build(); - } - @PostMapping("/{goalRoomId}/leave") @Authenticated public ResponseEntity leave(@MemberIdentifier final String identifier, @@ -150,16 +115,6 @@ public ResponseEntity> findMemberGoalRoomsBy return ResponseEntity.ok(memberGoalRoomForListResponses); } - @GetMapping("/{goalRoomId}/todos") - @Authenticated - public ResponseEntity> findAllTodos( - @PathVariable final Long goalRoomId, - @MemberIdentifier final String identifier) { - final List todoResponses = goalRoomReadService.findAllGoalRoomTodo(goalRoomId, - identifier); - return ResponseEntity.ok(todoResponses); - } - @GetMapping("/{goalRoomId}/nodes") @Authenticated public ResponseEntity> findAllNodes( @@ -170,14 +125,4 @@ public ResponseEntity> findAllNodes( goalRoomId, identifier); return ResponseEntity.ok(nodeResponses); } - - @GetMapping("/{goalRoomId}/checkFeeds") - @Authenticated - public ResponseEntity> findGoalRoomCheckFeeds( - @MemberIdentifier final String identifier, - @PathVariable("goalRoomId") final Long goalRoomId) { - final List response = goalRoomReadService.findGoalRoomCheckFeeds(identifier, - goalRoomId); - return ResponseEntity.ok(response); - } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/BaseGoalRoomMember.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/BaseGoalRoomMember.java similarity index 77% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/BaseGoalRoomMember.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/BaseGoalRoomMember.java index aa79e2006..578c963ea 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/BaseGoalRoomMember.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/BaseGoalRoomMember.java @@ -1,8 +1,6 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import co.kirikiri.common.entity.BaseEntity; -import co.kirikiri.member.domain.Member; -import com.querydsl.core.annotations.QueryInit; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; @@ -34,26 +32,22 @@ public abstract class BaseGoalRoomMember extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "goal_room_id", nullable = false) @OnDelete(action = OnDeleteAction.CASCADE) - @QueryInit(value = {"roadmapContent.roadmap"}) protected GoalRoom goalRoom; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - @QueryInit(value = {"identifier"}) - protected Member member; + protected Long memberId; protected BaseGoalRoomMember(final GoalRoomRole role, final LocalDateTime joinedAt, - final GoalRoom goalRoom, final Member member) { - this(null, role, joinedAt, goalRoom, member); + final GoalRoom goalRoom, final Long memberId) { + this(null, role, joinedAt, goalRoom, memberId); } protected BaseGoalRoomMember(final Long id, final GoalRoomRole role, final LocalDateTime joinedAt, - final GoalRoom goalRoom, final Member member) { + final GoalRoom goalRoom, final Long memberId) { this.id = id; this.role = role; this.joinedAt = joinedAt; this.goalRoom = goalRoom; - this.member = member; + this.memberId = memberId; } @PrePersist @@ -67,8 +61,8 @@ public boolean isLeader() { return role == GoalRoomRole.LEADER; } - public boolean isSameMember(final Member member) { - return this.member.equals(member); + public boolean isSameMember(final Long memberId) { + return this.memberId.equals(memberId); } public void becomeLeader() { @@ -87,12 +81,13 @@ public boolean equals(final Object o) { return false; } final BaseGoalRoomMember that = (BaseGoalRoomMember) o; - return Objects.equals(goalRoom, that.goalRoom) && Objects.equals(member, that.member); + return role == that.role && Objects.equals(joinedAt, that.joinedAt) && Objects.equals(goalRoom, + that.goalRoom) && Objects.equals(memberId, that.memberId); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), goalRoom, member); + return Objects.hash(super.hashCode(), role, joinedAt, goalRoom, memberId); } public GoalRoomRole getRole() { @@ -107,7 +102,7 @@ public GoalRoom getGoalRoom() { return goalRoom; } - public Member getMember() { - return member; + public Long getMemberId() { + return memberId; } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoom.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoom.java new file mode 100644 index 000000000..439a6d913 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoom.java @@ -0,0 +1,131 @@ +package co.kirikiri.goalroom.domain; + +import co.kirikiri.common.entity.BaseUpdatedTimeEntity; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GoalRoom extends BaseUpdatedTimeEntity { + + private static final int DATE_OFFSET = 1; + + @Embedded + private GoalRoomName name; + + @Embedded + private LimitedMemberCount limitedMemberCount; + + @Enumerated(value = EnumType.STRING) + private GoalRoomStatus status = GoalRoomStatus.RECRUITING; + + private Long roadmapContentId; + + @Column(nullable = false) + private LocalDate startDate; + + @Column(nullable = false) + private LocalDate endDate; + + @Embedded + private final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes(); + + public GoalRoom(final GoalRoomName name, final LimitedMemberCount limitedMemberCount, + final Long roadmapContentId, final GoalRoomRoadmapNodes goalRoomRoadmapNodes) { + this(null, name, limitedMemberCount, roadmapContentId, goalRoomRoadmapNodes); + } + + public GoalRoom(final Long id, final GoalRoomName name, final LimitedMemberCount limitedMemberCount, + final Long roadmapContentId, final GoalRoomRoadmapNodes goalRoomRoadmapNodes) { + this.id = id; + this.name = name; + this.limitedMemberCount = limitedMemberCount; + this.roadmapContentId = roadmapContentId; + addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); + } + + private void addAllGoalRoomRoadmapNodes(final GoalRoomRoadmapNodes goalRoomRoadmapNodes) { + this.goalRoomRoadmapNodes.addAll(goalRoomRoadmapNodes); + this.startDate = goalRoomRoadmapNodes.getGoalRoomStartDate(); + this.endDate = goalRoomRoadmapNodes.getGoalRoomEndDate(); + } + + public void start() { + this.status = GoalRoomStatus.RUNNING; + } + + public void complete() { + this.status = GoalRoomStatus.COMPLETED; + } + + public int calculateTotalPeriod() { + return (int) ChronoUnit.DAYS.between(startDate, endDate) + DATE_OFFSET; + } + + public int getAllCheckCount() { + return goalRoomRoadmapNodes.calculateAllCheckCount(); + } + + public boolean isRecruiting() { + return status == GoalRoomStatus.RECRUITING; + } + + public boolean isRunning() { + return status == GoalRoomStatus.RUNNING; + } + + public boolean isCompleted() { + return this.status == GoalRoomStatus.COMPLETED; + } + + public Optional findNodeByDate(final LocalDate date) { + return goalRoomRoadmapNodes.getNodeByDate(date); + } + + public boolean cannotStart() { + return startDate.isAfter(LocalDate.now()); + } + + public boolean isCompletedAfterMonths(final long numberOfMonth) { + final LocalDate currentDate = LocalDate.now(); + return currentDate.isAfter(endDate.plusMonths(numberOfMonth)); + } + + public GoalRoomName getName() { + return name; + } + + public LimitedMemberCount getLimitedMemberCount() { + return limitedMemberCount; + } + + public LocalDate getStartDate() { + return startDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public GoalRoomStatus getStatus() { + return status; + } + + public Long getRoadmapContentId() { + return roadmapContentId; + } + + public GoalRoomRoadmapNodes getGoalRoomRoadmapNodes() { + return goalRoomRoadmapNodes; + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMember.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMember.java similarity index 61% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMember.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMember.java index 7c9d2c6e4..58cd1ceb8 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomMember.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMember.java @@ -1,11 +1,10 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; -import co.kirikiri.member.domain.Member; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -15,8 +14,13 @@ public class GoalRoomMember extends BaseGoalRoomMember { private Double accomplishmentRate = 0.0; public GoalRoomMember(final GoalRoomRole role, final LocalDateTime joinedAt, - final GoalRoom goalRoom, final Member member) { - super(role, joinedAt, goalRoom, member); + final GoalRoom goalRoom, final Long memberId) { + super(role, joinedAt, goalRoom, memberId); + } + + public GoalRoomMember(final Long id, final GoalRoomRole role, final LocalDateTime joinedAt, final GoalRoom goalRoom, + final Long memberId) { + super(id, role, joinedAt, goalRoom, memberId); } public void updateAccomplishmentRate(final Double rate) { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMembers.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMembers.java new file mode 100644 index 000000000..3d6f142bf --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomMembers.java @@ -0,0 +1,62 @@ +package co.kirikiri.goalroom.domain; + +import co.kirikiri.common.exception.domain.UnexpectedDomainException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GoalRoomMembers { + + private static final int MIN_SIZE_TO_FIND_NEXT_LEADER = 1; + + private final List values = new ArrayList<>(); + + public GoalRoomMembers(final List values) { + this.values.addAll(new ArrayList<>(values)); + } + + public boolean isNotLeader(final Long memberId) { + final Long goalRoomLeaderId = findGoalRoomLeaderId(); + return !goalRoomLeaderId.equals(memberId); + } + + public Long findGoalRoomLeaderId() { + return values.stream() + .filter(GoalRoomMember::isLeader) + .findFirst() + .map(GoalRoomMember::getMemberId) + .orElseThrow(() -> new UnexpectedDomainException("골룸의 리더가 없습니다.")); + } + + public Optional findByMemberId(final Long memberId) { + return values.stream() + .filter(value -> value.isSameMember(memberId)) + .findFirst(); + } + + public void changeLeaderIfLeaderLeave(final GoalRoomMember leaveGoalRoomMember) { + if (leaveGoalRoomMember.isLeader()) { + findNextLeader().ifPresent(GoalRoomMember::becomeLeader); + } + } + + public Optional findNextLeader() { + if (size() <= MIN_SIZE_TO_FIND_NEXT_LEADER) { + return Optional.empty(); + } + values.sort(Comparator.comparing(GoalRoomMember::getJoinedAt)); + return Optional.of(values.get(1)); + } + + public int size() { + return values.size(); + } + + public List getValues() { + return new ArrayList<>(values); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMember.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMember.java new file mode 100644 index 000000000..b199f26d0 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMember.java @@ -0,0 +1,20 @@ +package co.kirikiri.goalroom.domain; + +import jakarta.persistence.Entity; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GoalRoomPendingMember extends BaseGoalRoomMember { + + public GoalRoomPendingMember(final GoalRoomRole role, final GoalRoom goalRoom, final Long memberId) { + this(null, role, null, goalRoom, memberId); + } + + public GoalRoomPendingMember(final Long id, final GoalRoomRole role, final LocalDateTime joinedAt, + final GoalRoom goalRoom, final Long memberId) { + super(id, role, joinedAt, goalRoom, memberId); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembers.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembers.java new file mode 100644 index 000000000..f137495b8 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembers.java @@ -0,0 +1,67 @@ +package co.kirikiri.goalroom.domain; + +import co.kirikiri.common.exception.domain.UnexpectedDomainException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GoalRoomPendingMembers { + + private static final int MIN_SIZE_TO_FIND_NEXT_LEADER = 1; + + private final List values = new ArrayList<>(); + + public GoalRoomPendingMembers(final List values) { + this.values.addAll(new ArrayList<>(values)); + } + + public boolean containGoalRoomPendingMember(final Long memberId) { + return values.stream() + .anyMatch(value -> value.isSameMember(memberId)); + } + + public boolean isNotLeader(final Long memberId) { + final Long goalRoomLeaderId = findGoalRoomLeaderId(); + return !goalRoomLeaderId.equals(memberId); + } + + public Long findGoalRoomLeaderId() { + return values.stream() + .filter(GoalRoomPendingMember::isLeader) + .findFirst() + .map(GoalRoomPendingMember::getMemberId) + .orElseThrow(() -> new UnexpectedDomainException("골룸의 리더가 없습니다.")); + } + + public Optional findByMemberId(final Long memberId) { + return values.stream() + .filter(value -> value.isSameMember(memberId)) + .findFirst(); + } + + public void changeLeaderIfLeaderLeave(final GoalRoomPendingMember leaveGoalRoomPendingMember) { + if (leaveGoalRoomPendingMember.isLeader()) { + findNextLeader().ifPresent(GoalRoomPendingMember::becomeLeader); + } + } + + public Optional findNextLeader() { + if (size() <= MIN_SIZE_TO_FIND_NEXT_LEADER) { + return Optional.empty(); + } + values.sort(Comparator.comparing(GoalRoomPendingMember::getJoinedAt)); + return Optional.of(values.get(1)); + } + + public int size() { + return values.size(); + } + + public List getValues() { + return new ArrayList<>(values); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNode.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNode.java similarity index 69% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNode.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNode.java index 1f60cc2b0..9f0c426b7 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNode.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNode.java @@ -1,19 +1,13 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import co.kirikiri.common.entity.BaseEntity; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.vo.Period; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import java.time.LocalDate; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import java.time.LocalDate; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -26,22 +20,19 @@ public class GoalRoomRoadmapNode extends BaseEntity { private Integer checkCount; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "roadmap_node_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - private RoadmapNode roadmapNode; + private Long roadmapNodeId; - public GoalRoomRoadmapNode(final Period period, final Integer checkCount, final RoadmapNode roadmapNode) { - this(null, period, checkCount, roadmapNode); + public GoalRoomRoadmapNode(final Period period, final Integer checkCount, final Long roadmapNodeId) { + this(null, period, checkCount, roadmapNodeId); } public GoalRoomRoadmapNode(final Long id, final Period period, final Integer checkCount, - final RoadmapNode roadmapNode) { + final Long roadmapNodeId) { validate(period, checkCount); this.id = id; this.period = period; this.checkCount = checkCount; - this.roadmapNode = roadmapNode; + this.roadmapNodeId = roadmapNodeId; } private void validate(final Period period, final Integer checkCount) { @@ -77,8 +68,8 @@ public LocalDate getEndDate() { return period.getEndDate(); } - public RoadmapNode getRoadmapNode() { - return roadmapNode; + public Long getRoadmapNodeId() { + return roadmapNodeId; } public int getCheckCount() { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodes.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodes.java similarity index 97% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodes.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodes.java index 3a70b4d72..ba254eeb3 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodes.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodes.java @@ -1,6 +1,6 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.FetchType; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRole.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRole.java similarity index 57% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRole.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRole.java index 1fec95ac2..e7027da50 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomRole.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomRole.java @@ -1,4 +1,4 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; public enum GoalRoomRole { LEADER, FOLLOWER diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomStatus.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomStatus.java similarity index 64% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomStatus.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomStatus.java index 91e0916ef..374b7ccc6 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomStatus.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/GoalRoomStatus.java @@ -1,4 +1,4 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; public enum GoalRoomStatus { RECRUITING, RUNNING, COMPLETED diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/exception/GoalRoomException.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/exception/GoalRoomException.java similarity index 81% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/exception/GoalRoomException.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/exception/GoalRoomException.java index 407c25426..1a3212a75 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/exception/GoalRoomException.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/exception/GoalRoomException.java @@ -1,4 +1,4 @@ -package co.kirikiri.domain.goalroom.exception; +package co.kirikiri.goalroom.domain.exception; import co.kirikiri.common.exception.domain.DomainException; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomName.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/GoalRoomName.java similarity index 88% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomName.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/GoalRoomName.java index 8893b12de..d9746488e 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomName.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/GoalRoomName.java @@ -1,6 +1,6 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.goalroom.domain.vo; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import lombok.AccessLevel; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCount.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCount.java similarity index 87% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCount.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCount.java index 9160fb278..2f2ffe00b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCount.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCount.java @@ -1,6 +1,6 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.goalroom.domain.vo; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import lombok.AccessLevel; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/Period.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/Period.java similarity index 94% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/Period.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/Period.java index a9b8f6d37..8426a34e2 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/Period.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/domain/vo/Period.java @@ -1,6 +1,6 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.goalroom.domain.vo; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import lombok.AccessLevel; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepository.java similarity index 60% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepository.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepository.java index 9a7b2e8b5..af3b6f3b6 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepository.java @@ -1,6 +1,6 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import co.kirikiri.domain.goalroom.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomMember; import java.util.List; public interface GoalRoomMemberJdbcRepository { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepositoryImpl.java similarity index 87% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepositoryImpl.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepositoryImpl.java index bd16f8969..a54fafa34 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberJdbcRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberJdbcRepositoryImpl.java @@ -1,9 +1,9 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import co.kirikiri.domain.goalroom.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; -import java.util.List; @Repository public class GoalRoomMemberJdbcRepositoryImpl implements GoalRoomMemberJdbcRepository { @@ -21,7 +21,7 @@ public void saveAllInBatch(final List goalRoomMembers) { + "VALUES (?, ?, ?, ?, ?)"; jdbcTemplate.batchUpdate(sql, goalRoomMembers, goalRoomMembers.size(), ((ps, goalRoomMember) -> { ps.setLong(1, goalRoomMember.getGoalRoom().getId()); - ps.setLong(2, goalRoomMember.getMember().getId()); + ps.setLong(2, goalRoomMember.getMemberId()); ps.setString(3, goalRoomMember.getRole().name()); ps.setObject(4, goalRoomMember.getJoinedAt()); ps.setDouble(5, goalRoomMember.getAccomplishmentRate()); diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepository.java new file mode 100644 index 000000000..d41686713 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepository.java @@ -0,0 +1,19 @@ +package co.kirikiri.goalroom.persistence; + +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; +import java.util.List; +import java.util.Optional; + +public interface GoalRoomMemberQueryRepository { + + Optional findByRoadmapIdAndMemberIdAndGoalRoomStatus(final Long roadmapId, + final Long memberId, + final GoalRoomStatus status); + + List findByGoalRoomIdOrderedBySortType(final Long goalRoomId, + final GoalRoomMemberSortType sortType); + + Optional findByGoalRoomIdAndMemberId(final Long goalRoomId, final Long memberId); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepositoryImpl.java similarity index 52% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepositoryImpl.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepositoryImpl.java index 0edace5f8..446dbec8a 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberQueryRepositoryImpl.java @@ -1,18 +1,14 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import static co.kirikiri.domain.goalroom.QGoalRoom.goalRoom; -import static co.kirikiri.domain.goalroom.QGoalRoomMember.goalRoomMember; -import static co.kirikiri.member.domain.QMember.member; -import static co.kirikiri.member.domain.QMemberImage.memberImage; -import static co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType.ACCOMPLISHMENT_RATE; -import static co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType.JOINED_ASC; -import static co.kirikiri.roadmap.domain.QRoadmapContent.roadmapContent; +import static co.kirikiri.goalroom.domain.QGoalRoom.goalRoom; +import static co.kirikiri.goalroom.domain.QGoalRoomMember.goalRoomMember; +import static co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType.ACCOMPLISHMENT_RATE; +import static co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType.JOINED_ASC; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.persistence.QuerydslRepositorySupporter; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; import java.util.List; @@ -26,19 +22,14 @@ public GoalRoomMemberQueryRepositoryImpl() { } @Override - public Optional findByRoadmapIdAndMemberIdentifierAndGoalRoomStatus(final Long roadmapId, - final Identifier identifier, - final GoalRoomStatus status) { + public Optional findByRoadmapIdAndMemberIdAndGoalRoomStatus(final Long roadmapId, + final Long memberId, + final GoalRoomStatus status) { return Optional.ofNullable(selectFrom(goalRoomMember) .innerJoin(goalRoomMember.goalRoom, goalRoom) .fetchJoin() - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .fetchJoin() - .innerJoin(goalRoomMember.member, member) - .fetchJoin() .where( - goalRoom.roadmapContent.roadmapId.eq(roadmapId), - member.identifier.eq(identifier), + memberIdCond(memberId), goalRoom.status.eq(status)) .fetchOne()); } @@ -47,15 +38,22 @@ public Optional findByRoadmapIdAndMemberIdentifierAndGoalRoomSta public List findByGoalRoomIdOrderedBySortType(final Long goalRoomId, final GoalRoomMemberSortType sortType) { return selectFrom(goalRoomMember) - .innerJoin(goalRoomMember.member, member) - .fetchJoin() - .innerJoin(member.image, memberImage) - .fetchJoin() .where(goalRoomMember.goalRoom.id.eq(goalRoomId)) .orderBy(sortCond(sortType)) .fetch(); } + @Override + public Optional findByGoalRoomIdAndMemberId(final Long goalRoomId, final Long memberId) { + return Optional.ofNullable(selectFrom(goalRoomMember) + .innerJoin(goalRoomMember.goalRoom, goalRoom) + .fetchJoin() + .where( + goalRoomIdCond(goalRoomId), + memberIdCond(memberId)) + .fetchFirst()); + } + private OrderSpecifier sortCond(final GoalRoomMemberSortType sortType) { if (sortType == null || sortType == ACCOMPLISHMENT_RATE) { return goalRoomMember.accomplishmentRate.desc(); @@ -66,22 +64,11 @@ private OrderSpecifier sortCond(final GoalRoomMemberSortType sortType) { return goalRoomMember.joinedAt.desc(); } - @Override - public Optional findGoalRoomMember(final Long goalRoomId, final Identifier memberIdentifier) { - return Optional.ofNullable(selectFrom(goalRoomMember) - .innerJoin(goalRoomMember.goalRoom, goalRoom) - .where( - goalRoomIdCond(goalRoomId), - memberIdentifierCond(memberIdentifier)) - .fetchJoin() - .fetchFirst()); - } - private BooleanExpression goalRoomIdCond(final Long goalRoomId) { return goalRoom.id.eq(goalRoomId); } - private BooleanExpression memberIdentifierCond(final Identifier memberIdentifier) { - return goalRoomMember.member.identifier.eq(memberIdentifier); + private BooleanExpression memberIdCond(final Long memberId) { + return goalRoomMember.memberId.eq(memberId); } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepository.java similarity index 52% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepository.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepository.java index 1ff1ed50d..704e091a0 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepository.java @@ -1,29 +1,31 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; public interface GoalRoomMemberRepository extends JpaRepository, GoalRoomMemberQueryRepository, GoalRoomMemberJdbcRepository { @Query("select gm from GoalRoomMember gm " + "inner join fetch gm.goalRoom g " - + "inner join fetch gm.member m " + "where g=:goalRoom " - + "and m.identifier =:identifier") - Optional findByGoalRoomAndMemberIdentifier( - @Param("goalRoom") final GoalRoom goalRoom, @Param("identifier") final Identifier identifier); + + "and gm.memberId =:memberId") + Optional findByGoalRoomAndMemberId( + @Param("goalRoom") final GoalRoom goalRoom, + @Param("memberId") final Long memberId); + + List findByGoalRoom(final GoalRoom goalRoom); @Query("select gm from GoalRoomMember gm " + "join fetch gm.goalRoom g " - + "join fetch gm.member m " - + "where g=:goalRoom " - + "and gm.member = m") + + "where g=:goalRoom ") List findAllByGoalRoom(@Param("goalRoom") final GoalRoom goalRoom); + + Optional findLeaderByGoalRoomAndRole(final GoalRoom goalRoom, final GoalRoomRole goalRoomRole); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepository.java similarity index 52% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepository.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepository.java index ab5d88527..0528e928e 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepository.java @@ -1,11 +1,13 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; import java.util.List; public interface GoalRoomPendingMemberQueryRepository { List findByGoalRoomIdOrderedBySortType(final Long goalRoomId, final GoalRoomMemberSortType sortType); + + List findByGoalRoomIdWithPessimisticLock(final Long goalRoomId); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepositoryImpl.java similarity index 59% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepositoryImpl.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepositoryImpl.java index f9a52c6f0..8209c622b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberQueryRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberQueryRepositoryImpl.java @@ -1,14 +1,13 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import static co.kirikiri.domain.goalroom.QGoalRoomPendingMember.goalRoomPendingMember; -import static co.kirikiri.member.domain.QMember.member; -import static co.kirikiri.member.domain.QMemberImage.memberImage; -import static co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType.JOINED_DESC; +import static co.kirikiri.goalroom.domain.QGoalRoomPendingMember.goalRoomPendingMember; +import static co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType.JOINED_DESC; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.persistence.QuerydslRepositorySupporter; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; import com.querydsl.core.types.OrderSpecifier; +import jakarta.persistence.LockModeType; import java.util.List; public class GoalRoomPendingMemberQueryRepositoryImpl extends QuerydslRepositorySupporter @@ -22,15 +21,19 @@ public GoalRoomPendingMemberQueryRepositoryImpl() { public List findByGoalRoomIdOrderedBySortType(final Long goalRoomId, final GoalRoomMemberSortType sortType) { return selectFrom(goalRoomPendingMember) - .innerJoin(goalRoomPendingMember.member, member) - .fetchJoin() - .innerJoin(member.image, memberImage) - .fetchJoin() .where(goalRoomPendingMember.goalRoom.id.eq(goalRoomId)) .orderBy(sortCond(sortType)) .fetch(); } + @Override + public List findByGoalRoomIdWithPessimisticLock(final Long goalRoomId) { + return selectFrom(goalRoomPendingMember) + .where(goalRoomPendingMember.goalRoom.id.eq(goalRoomId)) + .setLockMode(LockModeType.PESSIMISTIC_WRITE) + .fetch(); + } + private OrderSpecifier sortCond(final GoalRoomMemberSortType sortType) { if (sortType == JOINED_DESC) { return goalRoomPendingMember.joinedAt.desc(); diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepository.java similarity index 62% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepository.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepository.java index 880d1af38..bb9ef5c6f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepository.java @@ -1,36 +1,36 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; public interface GoalRoomPendingMemberRepository extends JpaRepository, GoalRoomPendingMemberQueryRepository { @Query("select gp from GoalRoomPendingMember gp " + "inner join fetch gp.goalRoom g " - + "inner join fetch gp.member m " - + "where g=:goalRoom " - + "and m.identifier =:identifier") - Optional findByGoalRoomAndMemberIdentifier( - @Param("goalRoom") final GoalRoom goalRoom, @Param("identifier") final Identifier identifier); + + "where g = :goalRoom " + + "and gp.memberId = :memberId") + Optional findByGoalRoomAndMemberId( + @Param("goalRoom") final GoalRoom goalRoom, @Param("memberId") final Long memberId); List findByGoalRoom(final GoalRoom goalRoom); @Query("select gp from GoalRoomPendingMember gp " + "join fetch gp.goalRoom g " - + "join fetch gp.member m " - + "where g=:goalRoom " - + "and gp.member = m") + + "where g = :goalRoom ") List findAllByGoalRoom(@Param("goalRoom") final GoalRoom goalRoom); @Modifying @Query("DELETE FROM GoalRoomPendingMember gp WHERE gp.id IN :ids") void deleteAllByIdIn(@Param("ids") final List ids); + + Optional findLeaderByGoalRoomAndRole(final GoalRoom goalRoom, + final GoalRoomRole goalRoomRole); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepository.java new file mode 100644 index 000000000..122ba3bc3 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepository.java @@ -0,0 +1,19 @@ +package co.kirikiri.goalroom.persistence; + +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public interface GoalRoomQueryRepository { + + List findGoalRoomsByRoadmapContentIdAndCond(final Long roadmapContentId, + final RoadmapGoalRoomsOrderType filterType, + final Long lastId, + final int pageSize); + + Optional findByIdWithNodes(final Long goalRoomId); + + List findAllRecruitingGoalRoomsByStartDateEarlierThan(final LocalDate startDate); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepositoryImpl.java new file mode 100644 index 000000000..2c8991679 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomQueryRepositoryImpl.java @@ -0,0 +1,102 @@ +package co.kirikiri.goalroom.persistence; + +import static co.kirikiri.goalroom.domain.QGoalRoom.goalRoom; +import static co.kirikiri.goalroom.domain.QGoalRoomRoadmapNode.goalRoomRoadmapNode; + +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public class GoalRoomQueryRepositoryImpl extends QuerydslRepositorySupporter implements GoalRoomQueryRepository { + + private static final int LIMIT_OFFSET = 1; + + public GoalRoomQueryRepositoryImpl() { + super(GoalRoom.class); + } + + @Override + public List findGoalRoomsByRoadmapContentIdAndCond(final Long roadmapContentId, + final RoadmapGoalRoomsOrderType orderType, + final Long lastId, + final int pageSize) { + return selectFrom(goalRoom) + .where( + statusCond(orderType), + lessThanLastId(lastId, orderType), + roadmapContentIdCond(roadmapContentId)) + .limit(pageSize + LIMIT_OFFSET) + .orderBy(sortCond(orderType)) + .fetch(); + } + + @Override + public Optional findByIdWithNodes(final Long goalRoomId) { + return Optional.ofNullable(selectFrom(goalRoom) + .innerJoin(goalRoom.goalRoomRoadmapNodes.values, goalRoomRoadmapNode) + .fetchJoin() + .where(goalRoomIdCond(goalRoomId)) + .fetchOne()); + } + + @Override + public List findAllRecruitingGoalRoomsByStartDateEarlierThan(final LocalDate date) { + return selectFrom(goalRoom) + .where(statusCond(GoalRoomStatus.RECRUITING)) + .where(equalOrEarlierStartDateThan(date)) + .fetch(); + } + + private BooleanExpression goalRoomIdCond(final Long goalRoomId) { + return goalRoom.id.eq(goalRoomId); + } + + private BooleanExpression statusCond(final GoalRoomStatus status) { + return goalRoom.status.eq(status); + } + + private BooleanExpression statusCond(final RoadmapGoalRoomsOrderType orderType) { + if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { + return statusCond(GoalRoomStatus.RECRUITING); + } + return null; + } + + private OrderSpecifier sortCond(final RoadmapGoalRoomsOrderType orderType) { + if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { + return goalRoom.startDate.asc(); + } + return goalRoom.createdAt.desc(); + } + + private BooleanExpression lessThanLastId(final Long lastId, final RoadmapGoalRoomsOrderType orderType) { + if (lastId == null) { + return null; + } + if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { + return select(goalRoom.startDate) + .from(goalRoom) + .where(goalRoom.id.eq(lastId)) + .lt(goalRoom.startDate); + } + return goalRoom.createdAt.lt( + select(goalRoom.createdAt) + .from(goalRoom) + .where(goalRoom.id.eq(lastId)) + ); + } + + private BooleanExpression roadmapContentIdCond(final Long roadmapContentId) { + return goalRoom.roadmapContentId.eq(roadmapContentId); + } + + private BooleanExpression equalOrEarlierStartDateThan(final LocalDate date) { + return goalRoom.startDate.loe(date); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomRepository.java new file mode 100644 index 000000000..551571a61 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/GoalRoomRepository.java @@ -0,0 +1,32 @@ +package co.kirikiri.goalroom.persistence; + +import co.kirikiri.goalroom.domain.GoalRoom; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface GoalRoomRepository extends JpaRepository, GoalRoomQueryRepository { + + @Override + Optional findById(final Long goalRoomId); + + List findAllByEndDate(final LocalDate endDate); + + List findByRoadmapContentId(final Long roadmapContentId); + + @Query(value = "SELECT g.* FROM goal_room as g " + + "LEFT JOIN goal_room_pending_member as gpm ON gpm.goal_room_id = g.id " + + "LEFT JOIN goal_room_member as gm ON gm.goal_room_id = g.id " + + "WHERE gpm.member_id = :memberId OR gm.member_id = :memberId ", nativeQuery = true) + List findByMemberId(final Long memberId); + + @Query(value = "SELECT g.* FROM goal_room as g " + + "LEFT JOIN goal_room_pending_member as gpm ON gpm.goal_room_id = g.id " + + "LEFT JOIN goal_room_member as gm ON gm.goal_room_id = g.id " + + "WHERE (gpm.member_id = :memberId OR gm.member_id = :memberId) " + + "AND g.status = :goalRoomStatus ", nativeQuery = true) + List findByMemberAndStatus(final Long memberId, final String goalRoomStatus); + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomFilterType.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomFilterType.java similarity index 59% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomFilterType.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomFilterType.java index a102fa9eb..e4e413143 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomFilterType.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomFilterType.java @@ -1,4 +1,4 @@ -package co.kirikiri.persistence.goalroom.dto; +package co.kirikiri.goalroom.persistence.dto; public enum GoalRoomFilterType { LATEST, PARTICIPATION_RATE diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomMemberSortType.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomMemberSortType.java similarity index 87% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomMemberSortType.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomMemberSortType.java index 417a74f29..14f935c26 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/GoalRoomMemberSortType.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/GoalRoomMemberSortType.java @@ -1,4 +1,4 @@ -package co.kirikiri.persistence.goalroom.dto; +package co.kirikiri.goalroom.persistence.dto; public enum GoalRoomMemberSortType { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/RoadmapGoalRoomsOrderType.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/RoadmapGoalRoomsOrderType.java similarity index 61% rename from backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/RoadmapGoalRoomsOrderType.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/RoadmapGoalRoomsOrderType.java index 17a5256d3..746c9453f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/dto/RoadmapGoalRoomsOrderType.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/persistence/dto/RoadmapGoalRoomsOrderType.java @@ -1,4 +1,4 @@ -package co.kirikiri.persistence.goalroom.dto; +package co.kirikiri.goalroom.persistence.dto; public enum RoadmapGoalRoomsOrderType { LATEST, CLOSE_TO_DEADLINE diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardCheckFeedService.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardCheckFeedService.java new file mode 100644 index 000000000..1522a5838 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardCheckFeedService.java @@ -0,0 +1,10 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import java.util.List; + +public interface DashBoardCheckFeedService { + + List findCheckFeedsByNodeAndGoalRoomStatus(final GoalRoom goalRoom); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardToDoService.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardToDoService.java new file mode 100644 index 000000000..7811f228d --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/DashBoardToDoService.java @@ -0,0 +1,10 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import java.util.List; + +public interface DashBoardToDoService { + + List findMemberCheckedGoalRoomToDoIds(final GoalRoom goalRoom, final Long memberId); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListener.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListener.java new file mode 100644 index 000000000..49af35db0 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListener.java @@ -0,0 +1,49 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.event.GoalRoomCreateEvent; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.persistence.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Service +@RequiredArgsConstructor +@ExceptionConvert +public class GoalRoomCreateEventListener { + + private final GoalRoomRepository goalRoomRepository; + private final MemberRepository memberRepository; + private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + @Transactional + public void handleGoalRoomLeaderUpdate(final GoalRoomCreateEvent goalRoomCreateEvent) { + final Member leader = findMemberByIdentifier(goalRoomCreateEvent.leaderIdentifier()); + final GoalRoom goalRoom = findGoalRoomById(goalRoomCreateEvent); + + final GoalRoomPendingMember goalRoomLeader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, + leader.getId()); + goalRoomPendingMemberRepository.save(goalRoomLeader); + } + + private Member findMemberByIdentifier(final String memberIdentifier) { + return memberRepository.findByIdentifier(new Identifier(memberIdentifier)) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); + } + + private GoalRoom findGoalRoomById(final GoalRoomCreateEvent goalRoomCreateEvent) { + return goalRoomRepository.findById(goalRoomCreateEvent.goalRoomId()) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다.")); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateService.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateService.java new file mode 100644 index 000000000..a4e9d84ed --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomCreateService.java @@ -0,0 +1,269 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomMembers; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomPendingMembers; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.dto.GoalRoomCreateDto; +import co.kirikiri.goalroom.service.dto.GoalRoomRoadmapNodeDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.event.GoalRoomCreateEvent; +import co.kirikiri.goalroom.service.event.GoalRoomLeaveEvent; +import co.kirikiri.goalroom.service.mapper.GoalRoomMapper; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.persistence.RoadmapContentRepository; +import co.kirikiri.roadmap.persistence.RoadmapRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +@ExceptionConvert +public class GoalRoomCreateService { + + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final RoadmapContentRepository roadmapContentRepository; + private final RoadmapRepository roadmapRepository; + private final MemberRepository memberRepository; + private final ApplicationEventPublisher applicationEventPublisher; + + public Long create(final GoalRoomCreateRequest goalRoomCreateRequest, final String memberIdentifier) { + final GoalRoomCreateDto goalRoomCreateDto = GoalRoomMapper.convertToGoalRoomCreateDto(goalRoomCreateRequest); + final RoadmapContent roadmapContent = findRoadmapContentById(goalRoomCreateDto.roadmapContentId()); + validateDeletedRoadmap(roadmapContent); + validateNodeSizeEqual(roadmapContent.nodesSize(), goalRoomCreateDto.goalRoomRoadmapNodeDtosSize()); + final GoalRoom goalRoom = createGoalRoom(goalRoomCreateDto, roadmapContent); + final GoalRoom savedGoalRoom = goalRoomRepository.save(goalRoom); + + applicationEventPublisher.publishEvent(new GoalRoomCreateEvent(savedGoalRoom.getId(), memberIdentifier)); + + return savedGoalRoom.getId(); + } + + private RoadmapContent findRoadmapContentById(final Long roadmapContentId) { + return roadmapContentRepository.findById(roadmapContentId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 로드맵 본문입니다.")); + } + + private void validateDeletedRoadmap(final RoadmapContent roadmapContent) { + final Roadmap roadmap = findRoadmapById(roadmapContent.getRoadmapId()); + if (roadmap.isDeleted()) { + throw new BadRequestException("삭제된 로드맵에 대해 골룸을 생성할 수 없습니다."); + } + } + + private Roadmap findRoadmapById(final Long roadmapId) { + return roadmapRepository.findById(roadmapId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 로드맵입니다.")); + } + + private void validateNodeSizeEqual(final int roadmapNodesSize, final int goalRoomRoadmapNodeDtosSize) { + if (roadmapNodesSize != goalRoomRoadmapNodeDtosSize) { + throw new BadRequestException("모든 노드에 대해 기간이 설정돼야 합니다."); + } + } + + private GoalRoom createGoalRoom(final GoalRoomCreateDto goalRoomCreateDto, final RoadmapContent roadmapContent) { + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = makeGoalRoomRoadmapNodes( + goalRoomCreateDto.goalRoomRoadmapNodeDtos(), roadmapContent); + validateGoalRoomRoadmapNodeSize(goalRoomRoadmapNodes, roadmapContent); + return new GoalRoom(goalRoomCreateDto.goalRoomName(), goalRoomCreateDto.limitedMemberCount(), + roadmapContent.getId(), goalRoomRoadmapNodes); + } + + private static GoalRoomRoadmapNodes makeGoalRoomRoadmapNodes( + final List goalRoomRoadmapNodeDtos, + final RoadmapContent roadmapContent) { + final List goalRoomRoadmapNodes = goalRoomRoadmapNodeDtos.stream() + .map(it -> makeGoalRoomRoadmapNode(roadmapContent, it)) + .toList(); + return new GoalRoomRoadmapNodes(goalRoomRoadmapNodes); + } + + private static GoalRoomRoadmapNode makeGoalRoomRoadmapNode(final RoadmapContent roadmapContent, + final GoalRoomRoadmapNodeDto it) { + return new GoalRoomRoadmapNode(new Period(it.startDate(), it.endDate()), it.checkCount(), + findRoadmapNode(roadmapContent, it.roadmapNodeId()).getId()); + } + + private static RoadmapNode findRoadmapNode(final RoadmapContent roadmapContent, final Long roadmapNodeId) { + return roadmapContent.findRoadmapNodeById(roadmapNodeId) + .orElseThrow(() -> new NotFoundException("로드맵에 존재하지 않는 노드입니다.")); + } + + private void validateGoalRoomRoadmapNodeSize(final GoalRoomRoadmapNodes goalRoomRoadmapNodes, + final RoadmapContent roadmapContent) { + final int goalRoomRoadmapNodesSize = goalRoomRoadmapNodes.size(); + if (goalRoomRoadmapNodesSize > roadmapContent.nodesSize()) { + throw new BadRequestException("로드맵의 노드 수보다 골룸의 노드 수가 큽니다."); + } + } + + public void join(final String identifier, final Long goalRoomId) { + final Member member = findMemberByIdentifier(identifier); + final GoalRoom goalRoom = findGoalRoomById(goalRoomId); + validateJoinGoalRoom(goalRoom, member); + final GoalRoomPendingMember newMember = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, + member.getId()); + goalRoomPendingMemberRepository.save(newMember); + } + + private Member findMemberByIdentifier(final String memberIdentifier) { + return memberRepository.findByIdentifier(new Identifier(memberIdentifier)) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); + } + + private GoalRoom findGoalRoomById(final Long goalRoomId) { + return goalRoomRepository.findById(goalRoomId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); + } + + private void validateJoinGoalRoom(final GoalRoom goalRoom, final Member member) { + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + findGoalRoomPendingMembersWithPessimisticLock(goalRoom)); + validateMemberCount(goalRoomPendingMembers, goalRoom); + validateRecruiting(goalRoom); + validateAlreadyParticipated(goalRoomPendingMembers, member); + } + + private List findGoalRoomPendingMembersWithPessimisticLock(final GoalRoom goalRoom) { + return goalRoomPendingMemberRepository.findByGoalRoomIdWithPessimisticLock(goalRoom.getId()); + } + + private void validateMemberCount(final GoalRoomPendingMembers goalRoomPendingMembers, final GoalRoom goalRoom) { + if (goalRoomPendingMembers.size() >= goalRoom.getLimitedMemberCount().getValue()) { + throw new BadRequestException("제한 인원이 꽉 찬 골룸에는 참여할 수 없습니다."); + } + } + + private void validateRecruiting(final GoalRoom goalRoom) { + if (!goalRoom.isRecruiting()) { + throw new BadRequestException("모집 중이지 않은 골룸에는 참여할 수 없습니다."); + } + } + + private void validateAlreadyParticipated(final GoalRoomPendingMembers goalRoomPendingMembers, final Member member) { + if (goalRoomPendingMembers.containGoalRoomPendingMember(member.getId())) { + throw new BadRequestException("이미 참여한 골룸에는 참여할 수 없습니다."); + } + } + + public void leave(final String identifier, final Long goalRoomId) { + final Member member = findMemberByIdentifier(identifier); + final GoalRoom goalRoom = findGoalRoomById(goalRoomId); + validateRunning(goalRoom); + + if (goalRoom.isRecruiting()) { + leaveRecruitingGoalRoom(member, goalRoom); + } + if (goalRoom.isCompleted()) { + leaveCompletedGoalRoom(member, goalRoom); + } + + applicationEventPublisher.publishEvent(new GoalRoomLeaveEvent(goalRoomId)); + } + + private void validateRunning(final GoalRoom goalRoom) { + if (goalRoom.isRunning()) { + throw new BadRequestException("진행중인 골룸에서는 나갈 수 없습니다."); + } + } + + private void leaveRecruitingGoalRoom(final Member member, final GoalRoom goalRoom) { + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + findGoalRoomPendingMembers(goalRoom)); + final GoalRoomPendingMember goalRoomPendingMember = goalRoomPendingMembers.findByMemberId(member.getId()) + .orElseThrow(() -> new BadRequestException("골룸에 참여한 사용자가 아닙니다. memberId = " + member.getId())); + goalRoomPendingMembers.changeLeaderIfLeaderLeave(goalRoomPendingMember); + goalRoomPendingMemberRepository.delete(goalRoomPendingMember); + } + + private List findGoalRoomPendingMembers(final GoalRoom goalRoom) { + return goalRoomPendingMemberRepository.findByGoalRoom(goalRoom); + } + + private void leaveCompletedGoalRoom(final Member member, final GoalRoom goalRoom) { + final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(findGoalRoomMembers(goalRoom)); + final GoalRoomMember goalRoomMember = goalRoomMembers.findByMemberId(member.getId()) + .orElseThrow(() -> new BadRequestException("골룸에 참여한 사용자가 아닙니다. memberId = " + member.getId())); + goalRoomMembers.changeLeaderIfLeaderLeave(goalRoomMember); + goalRoomMemberRepository.delete(goalRoomMember); + } + + private List findGoalRoomMembers(final GoalRoom goalRoom) { + return goalRoomMemberRepository.findAllByGoalRoom(goalRoom); + } + + public void startGoalRoom(final String memberIdentifier, final Long goalRoomId) { + final Member member = findMemberByIdentifier(memberIdentifier); + + final GoalRoom goalRoom = findGoalRoomById(goalRoomId); + checkGoalRoomStartDate(goalRoom); + + final List findGoalRoomPendingMembers = findGoalRoomPendingMembers(goalRoom); + checkAlreadyStarted(findGoalRoomPendingMembers); + + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(findGoalRoomPendingMembers); + checkGoalRoomLeader(member.getId(), goalRoomPendingMembers); + + saveGoalRoomMemberFromPendingMembers(goalRoomPendingMembers); + goalRoom.start(); + } + + private void checkGoalRoomStartDate(final GoalRoom goalRoom) { + if (goalRoom.cannotStart()) { + throw new BadRequestException("골룸의 시작 날짜가 되지 않았습니다."); + } + } + + private void checkAlreadyStarted(final List goalRoomPendingMembers) { + if (goalRoomPendingMembers.isEmpty()) { + throw new BadRequestException("이미 시작된 골룸입니다."); + } + } + + private void checkGoalRoomLeader(final Long memberId, final GoalRoomPendingMembers goalRoomPendingMembers) { + if (goalRoomPendingMembers.isNotLeader(memberId)) { + throw new BadRequestException("골룸의 리더만 골룸을 시작할 수 있습니다."); + } + } + + private void saveGoalRoomMemberFromPendingMembers(final GoalRoomPendingMembers goalRoomPendingMembers) { + final List goalRoomMembers = makeGoalRoomMembers(goalRoomPendingMembers); + goalRoomMemberRepository.saveAllInBatch(goalRoomMembers); + goalRoomPendingMemberRepository.deleteAllInBatch(goalRoomPendingMembers.getValues()); + } + + private List makeGoalRoomMembers(final GoalRoomPendingMembers goalRoomPendingMembers) { + return goalRoomPendingMembers.getValues().stream() + .map(this::makeGoalRoomMember) + .toList(); + } + + private GoalRoomMember makeGoalRoomMember(final GoalRoomPendingMember goalRoomPendingMember) { + return new GoalRoomMember(goalRoomPendingMember.getRole(), goalRoomPendingMember.getJoinedAt(), + goalRoomPendingMember.getGoalRoom(), goalRoomPendingMember.getMemberId()); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListener.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListener.java new file mode 100644 index 000000000..18fa34160 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListener.java @@ -0,0 +1,46 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.event.GoalRoomLeaveEvent; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; + +@Service +@RequiredArgsConstructor +@ExceptionConvert +public class GoalRoomLeaveEventListener { + + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + + @Async + @TransactionalEventListener + @Transactional + public void handleDeleteEmptyGoalRoom(final GoalRoomLeaveEvent goalRoomLeaveEvent) { + final GoalRoom goalRoom = findGoalRoomById(goalRoomLeaveEvent.goalRoomId()); + final List goalRoomMembers = goalRoomMemberRepository.findByGoalRoom(goalRoom); + final List goalRoomPendingMembers = goalRoomPendingMemberRepository.findByGoalRoom( + goalRoom); + + if (goalRoomMembers.isEmpty() && goalRoomPendingMembers.isEmpty()) { + goalRoomRepository.delete(goalRoom); + } + } + + private GoalRoom findGoalRoomById(final Long goalRoomId) { + return goalRoomRepository.findById(goalRoomId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다.")); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomReadService.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomReadService.java similarity index 54% rename from backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomReadService.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomReadService.java index becc9da3e..24be54849 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomReadService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/GoalRoomReadService.java @@ -1,54 +1,48 @@ -package co.kirikiri.service.goalroom; +package co.kirikiri.goalroom.service; import co.kirikiri.common.aop.ExceptionConvert; import co.kirikiri.common.exception.ForbiddenException; import co.kirikiri.common.exception.NotFoundException; import co.kirikiri.common.service.FileService; -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.GoalRoomToDos; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberDto; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto; +import co.kirikiri.goalroom.service.dto.GoalRoomRoadmapNodeDetailDto; +import co.kirikiri.goalroom.service.dto.MemberDto; +import co.kirikiri.goalroom.service.dto.MemberGoalRoomForListDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomStatusTypeRequest; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; +import co.kirikiri.goalroom.service.mapper.GoalRoomMapper; import co.kirikiri.member.domain.Member; import co.kirikiri.member.domain.vo.Identifier; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.member.service.dto.MemberDto; -import co.kirikiri.persistence.goalroom.CheckFeedRepository; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.goalroom.GoalRoomToDoCheckRepository; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; +import co.kirikiri.roadmap.domain.RoadmapContent; import co.kirikiri.roadmap.domain.RoadmapNode; -import co.kirikiri.service.dto.goalroom.CheckFeedDto; -import co.kirikiri.service.dto.goalroom.GoalRoomCheckFeedDto; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberDto; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto; -import co.kirikiri.service.dto.goalroom.GoalRoomRoadmapNodeDetailDto; -import co.kirikiri.service.dto.goalroom.MemberGoalRoomForListDto; -import co.kirikiri.service.dto.goalroom.request.GoalRoomStatusTypeRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import co.kirikiri.service.mapper.GoalRoomMapper; +import co.kirikiri.roadmap.persistence.RoadmapContentRepository; +import co.kirikiri.roadmap.persistence.RoadmapNodeRepository; +import java.net.URL; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.net.URL; -import java.time.LocalDate; -import java.util.Collections; -import java.util.List; -import java.util.Optional; @Service @Transactional(readOnly = true) @@ -56,35 +50,58 @@ @ExceptionConvert public class GoalRoomReadService { - private final MemberRepository memberRepository; private final GoalRoomRepository goalRoomRepository; private final GoalRoomMemberRepository goalRoomMemberRepository; - private final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; - private final CheckFeedRepository checkFeedRepository; + private final DashBoardToDoService dashBoardToDoService; + private final DashBoardCheckFeedService dashBoardCheckFeedService; + private final RoadmapContentRepository roadmapContentRepository; + private final RoadmapNodeRepository roadmapNodeRepository; + private final MemberRepository memberRepository; private final FileService fileService; - public GoalRoomResponse findGoalRoom(final Long goalRoomId) { + public GoalRoomCertifiedResponse findGoalRoom(final String identifier, final Long goalRoomId) { final GoalRoom goalRoom = findGoalRoomWithRoadmapContentById(goalRoomId); - return GoalRoomMapper.convertGoalRoomResponse(goalRoom); + final RoadmapContent roadmapContent = findRoadmapContentByIdWithRoadmapNodes(goalRoom.getRoadmapContentId()); + + final boolean isJoined = isMemberGoalRoomJoin(new Identifier(identifier), goalRoom); + return GoalRoomMapper.convertGoalRoomCertifiedResponse(goalRoom, getCurrentMemberCount(goalRoom), + roadmapContent.getNodes(), isJoined); } private GoalRoom findGoalRoomWithRoadmapContentById(final Long goalRoomId) { - return goalRoomRepository.findByIdWithRoadmapContent(goalRoomId) + return goalRoomRepository.findById(goalRoomId) .orElseThrow(() -> new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = " + goalRoomId)); } - public GoalRoomCertifiedResponse findGoalRoom(final String identifier, final Long goalRoomId) { - final GoalRoom goalRoom = findGoalRoomWithRoadmapContentById(goalRoomId); - final boolean isJoined = isMemberGoalRoomJoin(new Identifier(identifier), goalRoom); - return GoalRoomMapper.convertGoalRoomCertifiedResponse(goalRoom, isJoined); + private RoadmapContent findRoadmapContentByIdWithRoadmapNodes(final Long roadmapContentId) { + return roadmapContentRepository.findByIdWithRoadmapNodes(roadmapContentId) + .orElseThrow(() -> new NotFoundException( + "존재하지 않는 로드맵 컨텐츠 아이디입니다. roadmapContentId = " + roadmapContentId)); } private boolean isMemberGoalRoomJoin(final Identifier identifier, final GoalRoom goalRoom) { + final Member member = findMemberByIdentifier(identifier); + if (goalRoom.isRecruiting()) { + return goalRoomPendingMemberRepository.findByGoalRoomAndMemberId(goalRoom, member.getId()).isPresent(); + } + return goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, member.getId()).isPresent(); + } + + private int getCurrentMemberCount(final GoalRoom goalRoom) { if (goalRoom.isRecruiting()) { - return goalRoomPendingMemberRepository.findByGoalRoomAndMemberIdentifier(goalRoom, identifier).isPresent(); + return goalRoomPendingMemberRepository.findByGoalRoom(goalRoom) + .size(); } - return goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(goalRoom, identifier).isPresent(); + return goalRoomMemberRepository.findByGoalRoom(goalRoom) + .size(); + } + + public GoalRoomResponse findGoalRoom(final Long goalRoomId) { + final GoalRoom goalRoom = findGoalRoomWithRoadmapContentById(goalRoomId); + final RoadmapContent roadmapContent = findRoadmapContentByIdWithRoadmapNodes(goalRoom.getRoadmapContentId()); + return GoalRoomMapper.convertGoalRoomResponse(goalRoom, getCurrentMemberCount(goalRoom), + roadmapContent.getNodes()); } public List findGoalRoomMembers(final Long goalRoomId, @@ -118,12 +135,18 @@ private List makeGoalRoomMemberDtosWithAccomplishmentRateZero private GoalRoomMemberDto makeGoalRoomMemberDtoWithAccomplishmentRateZero( final GoalRoomPendingMember goalRoomPendingMember) { - final Member member = goalRoomPendingMember.getMember(); + final Long memberId = goalRoomPendingMember.getMemberId(); + final Member member = findMemberWithImageById(memberId); final URL memberImageUrl = fileService.generateUrl(member.getImage().getServerFilePath(), HttpMethod.GET); return new GoalRoomMemberDto(member.getId(), member.getNickname().getValue(), memberImageUrl.toExternalForm(), 0D); } + private Member findMemberWithImageById(final Long memberId) { + return memberRepository.findWithMemberImageById(memberId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸 멤버입니다. memberId = " + memberId)); + } + private List makeGoalRoomMemberDtos( final List goalRoomPendingMembers) { return goalRoomPendingMembers.stream() @@ -132,62 +155,29 @@ private List makeGoalRoomMemberDtos( } private GoalRoomMemberDto makeGoalRoomMemberDto(final GoalRoomMember goalRoomMember) { - final Member member = goalRoomMember.getMember(); + final Long memberId = goalRoomMember.getMemberId(); + final Member member = findMemberWithImageById(memberId); final URL memberImageUrl = fileService.generateUrl(member.getImage().getServerFilePath(), HttpMethod.GET); return new GoalRoomMemberDto(member.getId(), member.getNickname().getValue(), memberImageUrl.toExternalForm(), goalRoomMember.getAccomplishmentRate()); } - public List findAllGoalRoomTodo(final Long goalRoomId, final String identifier) { - final GoalRoomToDos goalRoomToDos = findGoalRoomTodosByGoalRoomId(goalRoomId); - validateGoalRoomMember(goalRoomId, identifier); - final List checkedTodos = findMemberCheckedGoalRoomToDoIds(goalRoomId, identifier); - return GoalRoomMapper.convertGoalRoomTodoResponses(goalRoomToDos, checkedTodos); - } - - private void validateGoalRoomMember(final Long goalRoomId, final String identifier) { - if (goalRoomMemberRepository.findGoalRoomMember(goalRoomId, new Identifier(identifier)).isEmpty()) { - throw new ForbiddenException( - "골룸에 참여하지 않은 사용자입니다. goalRoomId = " + goalRoomId + " memberIdentifier = " + identifier); - } - } - - private GoalRoomToDos findGoalRoomTodosByGoalRoomId(final Long goalRoomId) { - return goalRoomRepository.findByIdWithTodos(goalRoomId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)) - .getGoalRoomToDos(); - } - - private List findMemberCheckedGoalRoomToDoIds(final Long goalRoomId, final String identifier) { - return goalRoomToDoCheckRepository.findByGoalRoomIdAndMemberIdentifier(goalRoomId, new Identifier(identifier)); - } - public MemberGoalRoomResponse findMemberGoalRoom(final String identifier, final Long goalRoomId) { - final GoalRoom goalRoom = findMemberGoalRoomById(goalRoomId); + final GoalRoom goalRoom = findGoalRoomWithNodesById(goalRoomId); final Member member = findMemberByIdentifier(new Identifier(identifier)); - validateMemberInGoalRoom(goalRoom, member); - - final Optional currentGoalRoomRoadmapNode = findCurrentGoalRoomNode(goalRoom); - final List checkFeeds = findCheckFeedsByNodeAndGoalRoomStatus(goalRoom, currentGoalRoomRoadmapNode); - final List checkedTodos = findMemberCheckedGoalRoomToDoIds(goalRoomId, identifier); - final List checkFeedDtos = makeCheckFeedDtos(checkFeeds); - return GoalRoomMapper.convertToMemberGoalRoomResponse(goalRoom, checkFeedDtos, checkedTodos); - } - - private List makeCheckFeedDtos(final List checkFeeds) { - return checkFeeds.stream() - .map(this::makeCheckFeedDto) - .toList(); - } + validateMemberInGoalRoom(goalRoom, member.getId()); - private CheckFeedDto makeCheckFeedDto(final CheckFeed checkFeed) { - final URL checkFeedImageUrl = fileService.generateUrl(checkFeed.getServerFilePath(), HttpMethod.GET); - return new CheckFeedDto(checkFeed.getId(), checkFeedImageUrl.toExternalForm(), - checkFeed.getDescription(), checkFeed.getCreatedAt()); + final RoadmapContent roadmapContent = findRoadmapContentByIdWithRoadmapNodes(goalRoom.getRoadmapContentId()); + final List checkFeeds = dashBoardCheckFeedService.findCheckFeedsByNodeAndGoalRoomStatus( + goalRoom); + final List checkedTodos = dashBoardToDoService.findMemberCheckedGoalRoomToDoIds( + goalRoom, member.getId()); + return GoalRoomMapper.convertToMemberGoalRoomResponse(goalRoom, findGoalRoomLeaderId(goalRoom), + getCurrentMemberCount(goalRoom), roadmapContent.getNodes(), checkFeeds, checkedTodos); } - private GoalRoom findMemberGoalRoomById(final Long goalRoomId) { - return goalRoomRepository.findByIdWithContentAndTodos(goalRoomId) + private GoalRoom findGoalRoomWithNodesById(final Long goalRoomId) { + return goalRoomRepository.findByIdWithNodes(goalRoomId) .orElseThrow(() -> new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = " + goalRoomId)); } @@ -196,30 +186,30 @@ private Member findMemberByIdentifier(final Identifier identifier) { .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); } - private void validateMemberInGoalRoom(final GoalRoom goalRoom, final Member member) { - if (!goalRoom.isGoalRoomMember(member)) { - throw new ForbiddenException("해당 골룸에 참여하지 않은 사용자입니다."); + private void validateMemberInGoalRoom(final GoalRoom goalRoom, final Long memberId) { + if (goalRoom.isRecruiting()) { + goalRoomPendingMemberRepository.findByGoalRoomAndMemberId(goalRoom, memberId) + .orElseThrow(() -> new ForbiddenException("해당 골룸에 참여하지 않은 사용자입니다.")); + return; } + goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, memberId) + .orElseThrow(() -> new ForbiddenException("해당 골룸에 참여하지 않은 사용자입니다.")); } - private Optional findCurrentGoalRoomNode(final GoalRoom goalRoom) { - return goalRoom.findNodeByDate(LocalDate.now()); - } - - private List findCheckFeedsByNodeAndGoalRoomStatus(final GoalRoom goalRoom, - final Optional currentGoalRoomRoadmapNode) { - if (goalRoom.isCompleted()) { - return checkFeedRepository.findByGoalRoom(goalRoom); - } - if (goalRoom.isRunning() && currentGoalRoomRoadmapNode.isPresent()) { - return checkFeedRepository.findByRunningGoalRoomRoadmapNode(currentGoalRoomRoadmapNode.get()); + private Long findGoalRoomLeaderId(final GoalRoom goalRoom) { + if (goalRoom.isRecruiting()) { + return goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom, GoalRoomRole.LEADER) + .orElseThrow(() -> new NotFoundException("골룸의 리더가 존재하지 않습니다.")) + .getMemberId(); } - return Collections.emptyList(); + return goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom, GoalRoomRole.LEADER) + .orElseThrow(() -> new NotFoundException("골룸의 리더가 존재하지 않습니다.")) + .getMemberId(); } public List findMemberGoalRooms(final String identifier) { final Member member = findMemberByIdentifier(new Identifier(identifier)); - final List memberGoalRooms = goalRoomRepository.findByMember(member); + final List memberGoalRooms = goalRoomRepository.findByMemberId(member.getId()); final List memberGoalRoomForListDtos = makeMemberGoalRoomForListDto( memberGoalRooms); return GoalRoomMapper.convertToMemberGoalRoomForListResponses(memberGoalRoomForListDtos); @@ -232,20 +222,27 @@ private List makeMemberGoalRoomForListDto(final List new NotFoundException("존재하지 않는 회원입니다. memberId = " + goalRoomLeaderId)); + } + public List findMemberGoalRoomsByStatusType(final String identifier, final GoalRoomStatusTypeRequest goalRoomStatusTypeRequest) { final Member member = findMemberByIdentifier(new Identifier(identifier)); final GoalRoomStatus goalRoomStatus = GoalRoomMapper.convertToGoalRoomStatus(goalRoomStatusTypeRequest); - final List memberGoalRooms = goalRoomRepository.findByMemberAndStatus(member, goalRoomStatus); + final List memberGoalRooms = goalRoomRepository.findByMemberAndStatus(member.getId(), + goalRoomStatus.name()); final List memberGoalRoomForListDtos = makeMemberGoalRoomForListDto( memberGoalRooms); return GoalRoomMapper.convertToMemberGoalRoomForListResponses(memberGoalRoomForListDtos); @@ -267,7 +264,15 @@ private GoalRoom findGoalRoomWithNodesByGoalRoomId(final Long goalRoomId) { .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); } - public List makeGoalRoomNodeDetailDtos( + private void validateGoalRoomMember(final Long goalRoomId, final String identifier) { + final Member member = findMemberByIdentifier(new Identifier(identifier)); + if (goalRoomMemberRepository.findByGoalRoomIdAndMemberId(goalRoomId, member.getId()).isEmpty()) { + throw new ForbiddenException( + "골룸에 참여하지 않은 사용자입니다. goalRoomId = " + goalRoomId + " memberIdentifier = " + identifier); + } + } + + private List makeGoalRoomNodeDetailDtos( final GoalRoomRoadmapNodes nodes) { return nodes.getValues().stream() .map(this::makeGoalRoomNodeDetailResponse) @@ -275,11 +280,17 @@ public List makeGoalRoomNodeDetailDtos( } private GoalRoomRoadmapNodeDetailDto makeGoalRoomNodeDetailResponse(final GoalRoomRoadmapNode node) { - final RoadmapNode roadmapNode = node.getRoadmapNode(); + final Long roadmapNodeId = node.getRoadmapNodeId(); + final RoadmapNode roadmapNode = findRoadmapNodeById(roadmapNodeId); return new GoalRoomRoadmapNodeDetailDto(node.getId(), roadmapNode.getTitle(), roadmapNode.getContent(), makeRoadmapNodeImageUrls(roadmapNode), node.getStartDate(), node.getEndDate(), node.getCheckCount()); } + private RoadmapNode findRoadmapNodeById(final Long roadmapNodeId) { + return roadmapNodeRepository.findByIdWithRoadmapNodeImages(roadmapNodeId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 로드맵 노드입니다. roadmapNodeId = " + roadmapNodeId)); + } + private List makeRoadmapNodeImageUrls(final RoadmapNode roadmapNode) { return roadmapNode.getRoadmapNodeImages() .getValues() @@ -287,55 +298,4 @@ private List makeRoadmapNodeImageUrls(final RoadmapNode roadmapNode) { .map(it -> fileService.generateUrl(it.getServerFilePath(), HttpMethod.GET).toExternalForm()) .toList(); } - - public List findGoalRoomCheckFeeds(final String identifier, final Long goalRoomId) { - final GoalRoom goalRoom = findGoalRoomWithNodesById(goalRoomId); - validateJoinedMemberInRunningGoalRoom(goalRoom, identifier); - final Optional currentGoalRoomRoadmapNode = findCurrentGoalRoomNode(goalRoom); - final List checkFeeds = findCheckFeedsByNodeAndGoalRoomStatusWithMember(goalRoom, - currentGoalRoomRoadmapNode); - final List goalRoomCheckFeedDtos = makeGoalRoomCheckFeedDtos(checkFeeds); - return GoalRoomMapper.convertToGoalRoomCheckFeedResponses(goalRoomCheckFeedDtos); - } - - private GoalRoom findGoalRoomWithNodesById(final Long goalRoomId) { - return goalRoomRepository.findByIdWithNodes(goalRoomId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); - } - - private void validateJoinedMemberInRunningGoalRoom(final GoalRoom goalRoom, final String identifier) { - if (goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(goalRoom, new Identifier(identifier)) - .isEmpty()) { - throw new ForbiddenException("골룸에 참여하지 않은 회원입니다."); - } - } - - private List findCheckFeedsByNodeAndGoalRoomStatusWithMember(final GoalRoom goalRoom, - final Optional currentGoalRoomRoadmapNode) { - if (goalRoom.isCompleted()) { - return checkFeedRepository.findByGoalRoomWithMemberAndMemberImage(goalRoom); - } - if (goalRoom.isRunning() && currentGoalRoomRoadmapNode.isPresent()) { - return checkFeedRepository.findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage( - currentGoalRoomRoadmapNode.get()); - } - return Collections.emptyList(); - } - - public List makeGoalRoomCheckFeedDtos( - final List checkFeeds) { - return checkFeeds.stream() - .map(this::makeGoalRoomCheckFeedDto) - .toList(); - } - - private GoalRoomCheckFeedDto makeGoalRoomCheckFeedDto(final CheckFeed checkFeed) { - final GoalRoomMember goalRoomMember = checkFeed.getGoalRoomMember(); - final Member member = goalRoomMember.getMember(); - - final URL memberImageUrl = fileService.generateUrl(member.getImage().getServerFilePath(), HttpMethod.GET); - - return new GoalRoomCheckFeedDto(new MemberDto(member.getId(), member.getNickname().getValue(), - memberImageUrl.toExternalForm()), makeCheckFeedDto(checkFeed)); - } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/RoadmapGoalRoomServiceImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/RoadmapGoalRoomServiceImpl.java new file mode 100644 index 000000000..9d969e439 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/RoadmapGoalRoomServiceImpl.java @@ -0,0 +1,157 @@ +package co.kirikiri.goalroom.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.mapper.ScrollResponseMapper; +import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.CustomScrollRequest; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; +import co.kirikiri.goalroom.service.dto.MemberDto; +import co.kirikiri.goalroom.service.dto.RoadmapGoalRoomDto; +import co.kirikiri.goalroom.service.dto.RoadmapGoalRoomScrollDto; +import co.kirikiri.goalroom.service.mapper.GoalRoomMapper; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.persistence.RoadmapContentRepository; +import co.kirikiri.roadmap.service.RoadmapGoalRoomService; +import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomNumberDto; +import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; +import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; +import java.net.URL; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +@ExceptionConvert +public class RoadmapGoalRoomServiceImpl implements RoadmapGoalRoomService { + + private static final int DELETE_AFTER_MONTH = 3; + + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + private final RoadmapContentRepository roadmapContentRepository; + private final MemberRepository memberRepository; + private final FileService fileService; + + @Override + public Long findCompletedGoalRoomMemberId(final Long roadmapId, final String identifier) { + final Member member = findMemberByIdentifier(identifier); + return goalRoomMemberRepository.findByRoadmapIdAndMemberIdAndGoalRoomStatus(roadmapId, member.getId(), + GoalRoomStatus.COMPLETED) + .orElseThrow(() -> new BadRequestException( + "로드맵에 대해서 완료된 골룸이 존재하지 않습니다. roadmapId = " + roadmapId + " memberIdentifier = " + identifier)) + .getMemberId(); + } + + private Member findMemberByIdentifier(final String identifier) { + return memberRepository.findByIdentifier(new Identifier(identifier)) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); + } + + @Override + public boolean hasGoalRooms(final Long roadmapId) { + + return !findGoalRoomsByRoadmapId(roadmapId).isEmpty(); + } + + private List findGoalRoomsByRoadmapId(final Long roadmapId) { + final RoadmapContent roadmapContent = findRoadmapContentByRoadmapId(roadmapId); + return goalRoomRepository.findByRoadmapContentId(roadmapContent.getId()); + } + + private RoadmapContent findRoadmapContentByRoadmapId(final Long roadmapId) { + return roadmapContentRepository.findFirstByRoadmapIdOrderByCreatedAtDesc(roadmapId) + .orElseThrow(() -> new NotFoundException("로드맵 본문이 존재하지 않습니다.")); + } + + @Override + public RoadmapGoalRoomNumberDto findRoadmapGoalRoomsByRoadmap(final Roadmap roadmap) { + return GoalRoomMapper.convertRoadmapGoalRoomDto(findGoalRoomsByRoadmapId(roadmap.getId())); + } + + @Override + public RoadmapGoalRoomResponses makeRoadmapGoalRoomResponsesByOrderType(final Long roadmapId, + final RoadmapGoalRoomsOrderTypeDto orderTypeDto, + final CustomScrollRequest scrollRequest) { + final RoadmapGoalRoomsOrderType orderType = GoalRoomMapper.convertToGoalRoomOrderType(orderTypeDto); + final RoadmapContent roadmapContent = findRoadmapContentByRoadmapId(roadmapId); + final List roadmapGoalRoomDtos = goalRoomRepository.findGoalRoomsByRoadmapContentIdAndCond( + roadmapContent.getId(), orderType, scrollRequest.lastId(), scrollRequest.size()) + .stream() + .map(this::makeGoalRoomDto) + .toList(); + final List subDtos = ScrollResponseMapper.getSubResponses(roadmapGoalRoomDtos, + scrollRequest.size()); + final boolean hasNext = ScrollResponseMapper.hasNext(roadmapGoalRoomDtos.size(), scrollRequest.size()); + + return GoalRoomMapper.convertToRoadmapGoalRoomResponses(new RoadmapGoalRoomScrollDto(subDtos, hasNext)); + } + + private RoadmapGoalRoomDto makeGoalRoomDto(final GoalRoom goalRoom) { + final Long goalRoomLeaderId = findGoalRoomLeaderId(goalRoom); + return new RoadmapGoalRoomDto(goalRoom.getId(), goalRoom.getName().getValue(), goalRoom.getStatus(), + getCurrentMemberCount(goalRoom), goalRoom.getLimitedMemberCount().getValue(), + goalRoom.getCreatedAt(), goalRoom.getStartDate(), + goalRoom.getEndDate(), makeMemberDto(goalRoomLeaderId)); + } + + private Long findGoalRoomLeaderId(final GoalRoom goalRoom) { + if (goalRoom.isRecruiting()) { + return goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom, GoalRoomRole.LEADER) + .orElseThrow(() -> new NotFoundException("골룸의 리더가 존재하지 않습니다.")) + .getMemberId(); + } + return goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom, GoalRoomRole.LEADER) + .orElseThrow(() -> new NotFoundException("골룸의 리더가 존재하지 않습니다.")) + .getMemberId(); + } + + private int getCurrentMemberCount(final GoalRoom goalRoom) { + if (goalRoom.isRecruiting()) { + return goalRoomPendingMemberRepository.findByGoalRoom(goalRoom) + .size(); + } + return goalRoomMemberRepository.findAllByGoalRoom(goalRoom) + .size(); + } + + private MemberDto makeMemberDto(final Long creatorId) { + final Member creator = findMemberById(creatorId); + final URL url = fileService.generateUrl(creator.getImage().getServerFilePath(), HttpMethod.GET); + return new MemberDto(creator.getId(), creator.getNickname().getValue(), url.toExternalForm()); + } + + private Member findMemberById(final Long creatorId) { + return memberRepository.findById(creatorId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다. memberId = " + creatorId)); + } + + @Override + @Transactional + public boolean canDeleteGoalRoomsInRoadmap(final Long roadmapId) { + final RoadmapContent roadmapContent = findRoadmapContentByRoadmapId(roadmapId); + final List goalRooms = goalRoomRepository.findByRoadmapContentId(roadmapContent.getId()); + return canDeleteRoadmapBasedOnGoalRooms(goalRooms); + } + + private boolean canDeleteRoadmapBasedOnGoalRooms(final List goalRooms) { + return goalRooms.stream() + .allMatch(goalRoom -> goalRoom.isCompleted() && goalRoom.isCompletedAfterMonths(DELETE_AFTER_MONTH)); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCreateDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomCreateDto.java similarity index 69% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCreateDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomCreateDto.java index 8d7482748..3f895bad3 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCreateDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomCreateDto.java @@ -1,7 +1,7 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; import java.util.List; public record GoalRoomCreateDto( diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomFilterTypeDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomFilterTypeDto.java similarity index 84% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomFilterTypeDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomFilterTypeDto.java index 596ecde51..0936d1954 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomFilterTypeDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomFilterTypeDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; public enum GoalRoomFilterTypeDto { @@ -10,4 +10,4 @@ public enum GoalRoomFilterTypeDto { GoalRoomFilterTypeDto(final String description) { this.description = description; } -} \ No newline at end of file +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberDto.java similarity index 78% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberDto.java index 01363bc1e..ad8211fd5 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; public record GoalRoomMemberDto( Long memberId, diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberSortTypeDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberSortTypeDto.java similarity index 88% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberSortTypeDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberSortTypeDto.java index a7f816eea..2d77278ef 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomMemberSortTypeDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomMemberSortTypeDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; public enum GoalRoomMemberSortTypeDto { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDetailDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDetailDto.java similarity index 87% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDetailDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDetailDto.java index d1808167f..d9dd9771d 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDetailDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDetailDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; import java.time.LocalDate; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDto.java similarity index 81% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDto.java index a456a23d4..b0c874e1c 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomRoadmapNodeDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomRoadmapNodeDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; import java.time.LocalDate; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomStatusTypeDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomStatusTypeDto.java similarity index 86% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomStatusTypeDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomStatusTypeDto.java index 4f59aced8..af024d202 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomStatusTypeDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/GoalRoomStatusTypeDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; public enum GoalRoomStatusTypeDto { RECRUITING("모집 중"), diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberDto.java new file mode 100644 index 000000000..4a709b505 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberDto.java @@ -0,0 +1,9 @@ +package co.kirikiri.goalroom.service.dto; + +public record MemberDto( + long id, + String name, + String imageUrl +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/MemberGoalRoomForListDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberGoalRoomForListDto.java similarity index 80% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/MemberGoalRoomForListDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberGoalRoomForListDto.java index 0e4ce4e81..684a2abb8 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/MemberGoalRoomForListDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/MemberGoalRoomForListDto.java @@ -1,6 +1,5 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; -import co.kirikiri.member.service.dto.MemberDto; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomDto.java similarity index 72% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomDto.java index 969d5fc6a..b73ad3f1a 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomDto.java @@ -1,7 +1,6 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.service.dto.MemberDto; +import co.kirikiri.goalroom.domain.GoalRoomStatus; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomScrollDto.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomScrollDto.java similarity index 78% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomScrollDto.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomScrollDto.java index 59937fe0e..5975d255a 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/RoadmapGoalRoomScrollDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/RoadmapGoalRoomScrollDto.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom; +package co.kirikiri.goalroom.service.dto; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomCreateRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomCreateRequest.java similarity index 92% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomCreateRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomCreateRequest.java index 546273c53..8bbe645e3 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomCreateRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomCreateRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.request; +package co.kirikiri.goalroom.service.dto.request; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomRoadmapNodeRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomRoadmapNodeRequest.java similarity index 93% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomRoadmapNodeRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomRoadmapNodeRequest.java index 91582039e..e14087735 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomRoadmapNodeRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomRoadmapNodeRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.request; +package co.kirikiri.goalroom.service.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotNull; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomStatusTypeRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomStatusTypeRequest.java similarity index 84% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomStatusTypeRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomStatusTypeRequest.java index 53fd85154..f7c615841 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomStatusTypeRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/request/GoalRoomStatusTypeRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.request; +package co.kirikiri.goalroom.service.dto.request; public enum GoalRoomStatusTypeRequest { RECRUITING("모집 중"), diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardCheckFeedResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardCheckFeedResponse.java new file mode 100644 index 000000000..52b25ce65 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardCheckFeedResponse.java @@ -0,0 +1,12 @@ +package co.kirikiri.goalroom.service.dto.response; + +import java.time.LocalDate; + +public record DashBoardCheckFeedResponse( + Long id, + String imageUrl, + String description, + LocalDate createdAt +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoCheckResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoCheckResponse.java new file mode 100644 index 000000000..84fcd0f9a --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoCheckResponse.java @@ -0,0 +1,7 @@ +package co.kirikiri.goalroom.service.dto.response; + +public record DashBoardToDoCheckResponse( + boolean isChecked +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoResponse.java new file mode 100644 index 000000000..d8b47403b --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/DashBoardToDoResponse.java @@ -0,0 +1,13 @@ +package co.kirikiri.goalroom.service.dto.response; + +import java.time.LocalDate; + +public record DashBoardToDoResponse( + Long id, + String content, + LocalDate startDate, + LocalDate endDate, + DashBoardToDoCheckResponse check +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCertifiedResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomCertifiedResponse.java similarity index 84% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCertifiedResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomCertifiedResponse.java index 253bb6b21..217b1d69b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCertifiedResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomCertifiedResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomMemberResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomMemberResponse.java similarity index 75% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomMemberResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomMemberResponse.java index 11ef4b04f..5cbe00a1d 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomMemberResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomMemberResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; public record GoalRoomMemberResponse( Long memberId, diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomResponse.java similarity index 82% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomResponse.java index 15f9df8be..0ba8e1a34 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeDetailResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeDetailResponse.java similarity index 85% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeDetailResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeDetailResponse.java index 62a1422bc..e92257c99 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeDetailResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeDetailResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.time.LocalDate; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeResponse.java similarity index 79% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeResponse.java index c89b8b1a5..eb419e69f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodeResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodeResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.time.LocalDate; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodesResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodesResponse.java similarity index 78% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodesResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodesResponse.java index 000dad727..e26c083a5 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomRoadmapNodesResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/GoalRoomRoadmapNodesResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.util.List; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomForListResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomForListResponse.java similarity index 77% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomForListResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomForListResponse.java index f01e093a6..b108b10b9 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomForListResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomForListResponse.java @@ -1,6 +1,5 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; -import co.kirikiri.member.service.dto.response.MemberResponse; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomResponse.java similarity index 71% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomResponse.java index 093b45cbb..6423e72f0 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/MemberGoalRoomResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberGoalRoomResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.goalroom.service.dto.response; import java.time.LocalDate; import java.util.List; @@ -13,8 +13,8 @@ public record MemberGoalRoomResponse( LocalDate endDate, Long roadmapContentId, GoalRoomRoadmapNodesResponse goalRoomRoadmapNodes, - List goalRoomTodos, - List checkFeeds + List goalRoomTodos, + List checkFeeds ) { } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberResponse.java new file mode 100644 index 000000000..a95dda910 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/dto/response/MemberResponse.java @@ -0,0 +1,9 @@ +package co.kirikiri.goalroom.service.dto.response; + +public record MemberResponse( + long id, + String name, + String imageUrl +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomCreateEvent.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomCreateEvent.java new file mode 100644 index 000000000..afcad2a70 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomCreateEvent.java @@ -0,0 +1,8 @@ +package co.kirikiri.goalroom.service.event; + +public record GoalRoomCreateEvent( + Long goalRoomId, + String leaderIdentifier +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomLeaveEvent.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomLeaveEvent.java new file mode 100644 index 000000000..b5dacaaa6 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/event/GoalRoomLeaveEvent.java @@ -0,0 +1,7 @@ +package co.kirikiri.goalroom.service.event; + +public record GoalRoomLeaveEvent( + Long goalRoomId +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/mapper/GoalRoomMapper.java b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/mapper/GoalRoomMapper.java similarity index 52% rename from backend/kirikiri/src/main/java/co/kirikiri/service/mapper/GoalRoomMapper.java rename to backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/mapper/GoalRoomMapper.java index bbe37f2c6..50e6ef0c7 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/mapper/GoalRoomMapper.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/goalroom/service/mapper/GoalRoomMapper.java @@ -1,66 +1,55 @@ -package co.kirikiri.service.mapper; - -import co.kirikiri.common.exception.ServerException; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.GoalRoomToDos; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.member.service.dto.MemberDto; -import co.kirikiri.member.service.dto.response.MemberResponse; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; +package co.kirikiri.goalroom.service.mapper; + +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; +import co.kirikiri.goalroom.service.dto.GoalRoomCreateDto; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberDto; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto; +import co.kirikiri.goalroom.service.dto.GoalRoomRoadmapNodeDetailDto; +import co.kirikiri.goalroom.service.dto.GoalRoomRoadmapNodeDto; +import co.kirikiri.goalroom.service.dto.MemberDto; +import co.kirikiri.goalroom.service.dto.MemberGoalRoomForListDto; +import co.kirikiri.goalroom.service.dto.RoadmapGoalRoomDto; +import co.kirikiri.goalroom.service.dto.RoadmapGoalRoomScrollDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomStatusTypeRequest; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodesResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.MemberResponse; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodes; import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomNumberDto; import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; +import co.kirikiri.roadmap.service.dto.response.MemberRoadmapGoalRoomResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; -import co.kirikiri.service.dto.FileInformation; -import co.kirikiri.service.dto.goalroom.CheckFeedDto; -import co.kirikiri.service.dto.goalroom.GoalRoomCheckFeedDto; -import co.kirikiri.service.dto.goalroom.GoalRoomCreateDto; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberDto; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto; -import co.kirikiri.service.dto.goalroom.GoalRoomRoadmapNodeDetailDto; -import co.kirikiri.service.dto.goalroom.GoalRoomRoadmapNodeDto; -import co.kirikiri.service.dto.goalroom.MemberGoalRoomForListDto; -import co.kirikiri.service.dto.goalroom.RoadmapGoalRoomDto; -import co.kirikiri.service.dto.goalroom.RoadmapGoalRoomScrollDto; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomStatusTypeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.CheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodesResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.time.LocalDate; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class GoalRoomMapper { - private static final int MAX_MEMBER_GOAL_ROOM_TODO_NUMBER = 3; private static final int MAX_MEMBER_GOAL_ROOM_CHECK_FEED_NUMBER = 4; public static GoalRoomCreateDto convertToGoalRoomCreateDto(final GoalRoomCreateRequest goalRoomCreateRequest) { @@ -75,11 +64,6 @@ public static GoalRoomCreateDto convertToGoalRoomCreateDto(final GoalRoomCreateR goalRoomRoadmapNodeDtos); } - public static GoalRoomToDo convertToGoalRoomTodo(final GoalRoomTodoRequest goalRoomTodoRequest) { - return new GoalRoomToDo(new GoalRoomTodoContent(goalRoomTodoRequest.content()), - new Period(goalRoomTodoRequest.startDate(), goalRoomTodoRequest.endDate())); - } - private static List makeGoalRoomRoadmapNodeDtos( final List goalRoomRoadmapNodeRequests) { return goalRoomRoadmapNodeRequests @@ -89,22 +73,30 @@ private static List makeGoalRoomRoadmapNodeDtos( .toList(); } - public static GoalRoomResponse convertGoalRoomResponse(final GoalRoom goalRoom) { + public static GoalRoomResponse convertGoalRoomResponse(final GoalRoom goalRoom, final int currentMemberCount, + final RoadmapNodes roadmapNodes) { final GoalRoomRoadmapNodes nodes = goalRoom.getGoalRoomRoadmapNodes(); - final List roadmapNodeResponses = convertGoalRoomNodeResponses(nodes); + final List roadmapNodeResponses = convertGoalRoomNodeResponses(nodes, + roadmapNodes); final int period = goalRoom.calculateTotalPeriod(); - return new GoalRoomResponse(goalRoom.getName().getValue(), goalRoom.getCurrentMemberCount(), + return new GoalRoomResponse(goalRoom.getName().getValue(), currentMemberCount, goalRoom.getLimitedMemberCount().getValue(), roadmapNodeResponses, period); } - public static List convertGoalRoomNodeResponses(final GoalRoomRoadmapNodes nodes) { - return nodes.getValues().stream() - .map(GoalRoomMapper::convertGoalRoomNodeResponse) + public static List convertGoalRoomNodeResponses( + final GoalRoomRoadmapNodes goalRoomRoadmapNodes, + final RoadmapNodes roadmapNodes) { + return goalRoomRoadmapNodes.getValues().stream() + .map(goalRoomRoadmapNode -> convertGoalRoomNodeResponse(goalRoomRoadmapNode, roadmapNodes)) .toList(); } - private static GoalRoomRoadmapNodeResponse convertGoalRoomNodeResponse(final GoalRoomRoadmapNode node) { - return new GoalRoomRoadmapNodeResponse(node.getId(), node.getRoadmapNode().getTitle(), node.getStartDate(), + private static GoalRoomRoadmapNodeResponse convertGoalRoomNodeResponse(final GoalRoomRoadmapNode node, + final RoadmapNodes roadmapNodes) { + final RoadmapNode roadmapNode = roadmapNodes.findById(node.getRoadmapNodeId()) + .orElseThrow(() -> new NotFoundException( + "존재하지 않는 로드맵 노드 아이디입니다. roadmapNodeId = " + node.getRoadmapNodeId())); + return new GoalRoomRoadmapNodeResponse(node.getId(), roadmapNode.getTitle(), node.getStartDate(), node.getEndDate(), node.getCheckCount()); } @@ -124,11 +116,14 @@ private static GoalRoomRoadmapNodeDetailResponse convertGoalRoomNodeDetailRespon } public static GoalRoomCertifiedResponse convertGoalRoomCertifiedResponse(final GoalRoom goalRoom, + final int currentMemberCount, + final RoadmapNodes roadmapNodes, final boolean isJoined) { final GoalRoomRoadmapNodes nodes = goalRoom.getGoalRoomRoadmapNodes(); - final List roadmapNodeResponses = convertGoalRoomNodeResponses(nodes); + final List roadmapNodeResponses = convertGoalRoomNodeResponses(nodes, + roadmapNodes); final int period = goalRoom.calculateTotalPeriod(); - return new GoalRoomCertifiedResponse(goalRoom.getName().getValue(), goalRoom.getCurrentMemberCount(), + return new GoalRoomCertifiedResponse(goalRoom.getName().getValue(), currentMemberCount, goalRoom.getLimitedMemberCount().getValue(), roadmapNodeResponses, period, isJoined); } @@ -158,8 +153,8 @@ private static RoadmapGoalRoomResponse convertToRoadmapGoalRoomResponse( roadmapGoalRoomDto.endDate(), convertToMemberResponse(roadmapGoalRoomDto.goalRoomLeader())); } - private static MemberResponse convertToMemberResponse(final MemberDto memberDto) { - return new MemberResponse(memberDto.id(), memberDto.name(), memberDto.imageUrl()); + private static MemberRoadmapGoalRoomResponse convertToMemberResponse(final MemberDto memberDto) { + return new MemberRoadmapGoalRoomResponse(memberDto.id(), memberDto.name(), memberDto.imageUrl()); } public static GoalRoomMemberSortType convertGoalRoomMemberSortType(final GoalRoomMemberSortTypeDto sortType) { @@ -181,47 +176,24 @@ private static GoalRoomMemberResponse convertToGoalRoomMemberResponse(final Goal goalRoomMemberDto.imagePath(), goalRoomMemberDto.accomplishmentRate()); } - public static List convertGoalRoomTodoResponses(final GoalRoomToDos goalRoomToDos, - final List checkedTodos) { - return goalRoomToDos.getValues().stream() - .map(goalRoomToDo -> convertGoalRoomTodoResponse(checkedTodos, goalRoomToDo)) - .toList(); - } - - private static GoalRoomTodoResponse convertGoalRoomTodoResponse(final List checkedTodos, - final GoalRoomToDo goalRoomToDo) { - final GoalRoomToDoCheckResponse checkResponse = new GoalRoomToDoCheckResponse( - isCheckedTodo(goalRoomToDo.getId(), checkedTodos)); - return new GoalRoomTodoResponse(goalRoomToDo.getId(), - goalRoomToDo.getContent(), - goalRoomToDo.getStartDate(), goalRoomToDo.getEndDate(), - checkResponse); - } - - private static boolean isCheckedTodo(final Long targetTodoId, final List checkedTodos) { - final List checkTodoIds = checkedTodos.stream() - .map(goalRoomToDoCheck -> goalRoomToDoCheck.getGoalRoomToDo().getId()) - .toList(); - return checkTodoIds.contains(targetTodoId); - } - public static MemberGoalRoomResponse convertToMemberGoalRoomResponse(final GoalRoom goalRoom, - final List checkFeedDtos, - final List checkedTodos) { + final Long goalRoomLeaderId, + final int currentMemberCount, + final RoadmapNodes roadmapNodes, + final List allCheckFeedResponses, + final List todoResponses) { final GoalRoomRoadmapNodesResponse nodeResponses = convertToGoalRoomRoadmapNodesResponse( - goalRoom.getGoalRoomRoadmapNodes()); - final List todoResponses = convertGoalRoomTodoResponsesLimit(goalRoom.getGoalRoomToDos(), - checkedTodos); - final List checkFeedResponses = convertToCheckFeedResponses(checkFeedDtos); + goalRoom.getGoalRoomRoadmapNodes(), roadmapNodes); + final List checkFeedResponses = makeCheckFeedToLimit(allCheckFeedResponses); return new MemberGoalRoomResponse(goalRoom.getName().getValue(), goalRoom.getStatus().name(), - goalRoom.findGoalRoomLeader().getId(), goalRoom.getCurrentMemberCount(), - goalRoom.getLimitedMemberCount().getValue(), goalRoom.getStartDate(), goalRoom.getEndDate(), - goalRoom.getRoadmapContent().getId(), nodeResponses, todoResponses, checkFeedResponses); + goalRoomLeaderId, currentMemberCount, goalRoom.getLimitedMemberCount().getValue(), + goalRoom.getStartDate(), goalRoom.getEndDate(), goalRoom.getRoadmapContentId(), nodeResponses, + todoResponses, checkFeedResponses); } private static GoalRoomRoadmapNodesResponse convertToGoalRoomRoadmapNodesResponse( - final GoalRoomRoadmapNodes nodes) { + final GoalRoomRoadmapNodes nodes, final RoadmapNodes roadmapNodes) { final GoalRoomRoadmapNode currentNode = nodes.getNodeByDate(LocalDate.now()) .orElse(nodes.getNodeByDate(nodes.getGoalRoomStartDate()).get()); @@ -229,43 +201,19 @@ private static GoalRoomRoadmapNodesResponse convertToGoalRoomRoadmapNodesRespons return new GoalRoomRoadmapNodesResponse( nodes.hasFrontNode(currentNode), nodes.hasBackNode(currentNode), - List.of(convertMemberGoalRoomNodeResponse(currentNode)) + List.of(convertGoalRoomNodeResponse(currentNode, roadmapNodes)) ); } final GoalRoomRoadmapNode nextNode = nodes.nextNode(currentNode).get(); return new GoalRoomRoadmapNodesResponse(nodes.hasFrontNode(currentNode), nodes.hasBackNode(nextNode), - List.of(convertMemberGoalRoomNodeResponse(currentNode), convertMemberGoalRoomNodeResponse(nextNode))); - } - - private static GoalRoomRoadmapNodeResponse convertMemberGoalRoomNodeResponse(final GoalRoomRoadmapNode node) { - return new GoalRoomRoadmapNodeResponse(node.getId(), node.getRoadmapNode().getTitle(), node.getStartDate(), - node.getEndDate(), node.getCheckCount()); - } - - private static List convertGoalRoomTodoResponsesLimit(final GoalRoomToDos goalRoomToDos, - final List checkedTodos) { - return goalRoomToDos.getValues() - .stream() - .map(goalRoomToDo -> convertMemberGoalRoomTodoResponse(checkedTodos, goalRoomToDo)) - .limit(MAX_MEMBER_GOAL_ROOM_TODO_NUMBER) - .toList(); + List.of(convertGoalRoomNodeResponse(currentNode, roadmapNodes), convertGoalRoomNodeResponse(nextNode, + roadmapNodes))); } - private static GoalRoomTodoResponse convertMemberGoalRoomTodoResponse(final List checkedTodos, - final GoalRoomToDo goalRoomToDo) { - final GoalRoomToDoCheckResponse checkResponse = new GoalRoomToDoCheckResponse( - isCheckedTodo(goalRoomToDo.getId(), checkedTodos)); - return new GoalRoomTodoResponse(goalRoomToDo.getId(), - goalRoomToDo.getContent(), - goalRoomToDo.getStartDate(), goalRoomToDo.getEndDate(), - checkResponse); - } - - private static List convertToCheckFeedResponses(final List checkFeedDtos) { + private static List makeCheckFeedToLimit( + final List checkFeedDtos) { return checkFeedDtos.stream() - .map(checkFeed -> new CheckFeedResponse(checkFeed.id(), checkFeed.imageUrl(), - checkFeed.description(), checkFeed.createdAt().toLocalDate())) .limit(MAX_MEMBER_GOAL_ROOM_CHECK_FEED_NUMBER) .toList(); } @@ -289,28 +237,7 @@ private static MemberGoalRoomForListResponse convertToMemberGoalRoomForListRespo memberGoalRoomForListDto.limitedMemberCount(), memberGoalRoomForListDto.createdAt(), memberGoalRoomForListDto.startDate(), memberGoalRoomForListDto.endDate(), - new MemberResponse(memberDto.id(), memberDto.name(), - memberDto.imageUrl())); - } - - public static List convertToGoalRoomCheckFeedResponses( - final List checkFeeds) { - return checkFeeds.stream() - .map(GoalRoomMapper::convertToGoalRoomCheckFeedResponse) - .toList(); - } - - private static GoalRoomCheckFeedResponse convertToGoalRoomCheckFeedResponse( - final GoalRoomCheckFeedDto goalRoomCheckFeedDto) { - final MemberDto memberDto = goalRoomCheckFeedDto.memberDto(); - final MemberResponse memberResponse = new MemberResponse(memberDto.id(), memberDto.name(), - memberDto.imageUrl()); - - final CheckFeedDto checkFeedDto = goalRoomCheckFeedDto.checkFeedDto(); - final CheckFeedResponse checkFeedResponse = new CheckFeedResponse(checkFeedDto.id(), checkFeedDto.imageUrl(), - checkFeedDto.description(), checkFeedDto.createdAt().toLocalDate()); - - return new GoalRoomCheckFeedResponse(memberResponse, checkFeedResponse); + new MemberResponse(memberDto.id(), memberDto.name(), memberDto.imageUrl())); } public static RoadmapGoalRoomNumberDto convertRoadmapGoalRoomDto(final List goalRooms) { @@ -322,13 +249,4 @@ public static RoadmapGoalRoomNumberDto convertRoadmapGoalRoomDto(final List goalRoomsToStart = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan( LocalDate.now()); for (final GoalRoom goalRoom : goalRoomsToStart) { - final List goalRoomPendingMembers = goalRoom.getGoalRoomPendingMembers().getValues(); + final List goalRoomPendingMembers = goalRoomPendingMemberRepository.findByGoalRoom( + goalRoom); saveGoalRoomMemberFromPendingMembers(goalRoomPendingMembers); goalRoom.start(); } @@ -39,8 +39,7 @@ public void startGoalRooms() { private void saveGoalRoomMemberFromPendingMembers(final List goalRoomPendingMembers) { final List goalRoomMembers = makeGoalRoomMembers(goalRoomPendingMembers); goalRoomMemberRepository.saveAllInBatch(goalRoomMembers); - final List ids = makeGoalRoomPendingMemberIds(goalRoomPendingMembers); - goalRoomPendingMemberRepository.deleteAllByIdIn(ids); + goalRoomPendingMemberRepository.deleteAllInBatch(goalRoomPendingMembers); } private List makeGoalRoomMembers(final List goalRoomPendingMembers) { @@ -51,13 +50,7 @@ private List makeGoalRoomMembers(final List makeGoalRoomPendingMemberIds(final List goalRoomPendingMembers) { - return goalRoomPendingMembers.stream() - .map(BaseEntity::getId) - .toList(); + goalRoomPendingMember.getGoalRoom(), goalRoomPendingMember.getMemberId()); } @Scheduled(cron = "0 0 4 * * *") diff --git a/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepository.java index 51129c9b0..8f35c60f3 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepository.java @@ -8,4 +8,6 @@ public interface MemberQueryRepository { Optional findWithMemberProfileAndImageByIdentifier(final String identifier); Optional findWithMemberProfileAndImageById(final Long memberId); + + Optional findWithMemberImageById(final Long memberId); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepositoryImpl.java index 2b2247f18..4e81c87b9 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/member/persistence/MemberQueryRepositoryImpl.java @@ -4,8 +4,8 @@ import static co.kirikiri.member.domain.QMemberImage.memberImage; import static co.kirikiri.member.domain.QMemberProfile.memberProfile; +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; import co.kirikiri.member.domain.Member; -import co.kirikiri.persistence.QuerydslRepositorySupporter; import java.util.Optional; public class MemberQueryRepositoryImpl extends QuerydslRepositorySupporter implements MemberQueryRepository { @@ -35,4 +35,13 @@ public Optional findWithMemberProfileAndImageById(final Long memberId) { .where(member.id.eq(memberId)) .fetchOne()); } + + @Override + public Optional findWithMemberImageById(final Long memberId) { + return Optional.ofNullable(selectFrom(member) + .innerJoin(member.image, memberImage) + .fetchJoin() + .where(member.id.eq(memberId)) + .fetchOne()); + } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/RandomNumberGenerator.java b/backend/kirikiri/src/main/java/co/kirikiri/member/service/RandomNumberGenerator.java similarity index 92% rename from backend/kirikiri/src/main/java/co/kirikiri/service/RandomNumberGenerator.java rename to backend/kirikiri/src/main/java/co/kirikiri/member/service/RandomNumberGenerator.java index 7b9e5372f..17065b063 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/RandomNumberGenerator.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/member/service/RandomNumberGenerator.java @@ -1,8 +1,8 @@ -package co.kirikiri.service; +package co.kirikiri.member.service; import co.kirikiri.common.service.NumberGenerator; -import org.springframework.stereotype.Component; import java.util.Random; +import org.springframework.stereotype.Component; @Component public class RandomNumberGenerator implements NumberGenerator { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepository.java deleted file mode 100644 index 5cb828965..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import java.util.List; - -public interface CheckFeedQueryRepository { - - List findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage(final GoalRoomRoadmapNode goalRoomRoadmapNode); - - List findByRunningGoalRoomRoadmapNode(final GoalRoomRoadmapNode currentGoalRoomRoadmapNode); - - List findByGoalRoomWithMemberAndMemberImage(final GoalRoom goalRoom); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepositoryImpl.java deleted file mode 100644 index 71adf913f..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedQueryRepositoryImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import static co.kirikiri.domain.goalroom.QCheckFeed.checkFeed; -import static co.kirikiri.domain.goalroom.QGoalRoomMember.goalRoomMember; -import static co.kirikiri.member.domain.QMember.member; -import static co.kirikiri.member.domain.QMemberImage.memberImage; - -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.persistence.QuerydslRepositorySupporter; -import com.querydsl.core.types.dsl.BooleanExpression; -import java.util.List; - -public class CheckFeedQueryRepositoryImpl extends QuerydslRepositorySupporter implements CheckFeedQueryRepository { - - public CheckFeedQueryRepositoryImpl() { - super(CheckFeed.class); - } - - @Override - public List findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage( - final GoalRoomRoadmapNode goalRoomRoadmapNode) { - return selectFrom(checkFeed) - .innerJoin(checkFeed.goalRoomMember, goalRoomMember) - .fetchJoin() - .innerJoin(goalRoomMember.member, member) - .fetchJoin() - .innerJoin(member.image, memberImage) - .fetchJoin() - .where(nodeCond(goalRoomRoadmapNode)) - .orderBy(checkFeed.createdAt.desc()) - .fetch(); - } - - @Override - public List findByRunningGoalRoomRoadmapNode( - final GoalRoomRoadmapNode currentGoalRoomRoadmapNode) { - return selectFrom(checkFeed) - .innerJoin(checkFeed.goalRoomMember, goalRoomMember) - .fetchJoin() - .innerJoin(goalRoomMember.member, member) - .fetchJoin() - .where(nodeCond(currentGoalRoomRoadmapNode)) - .orderBy(checkFeed.createdAt.desc()) - .fetch(); - } - - @Override - public List findByGoalRoomWithMemberAndMemberImage(final GoalRoom goalRoom) { - return selectFrom(checkFeed) - .innerJoin(checkFeed.goalRoomMember, goalRoomMember) - .fetchJoin() - .innerJoin(goalRoomMember.member, member) - .fetchJoin() - .innerJoin(member.image, memberImage) - .fetchJoin() - .where(goalRoomCond(goalRoom)) - .orderBy(checkFeed.createdAt.desc()) - .fetch(); - } - - private BooleanExpression nodeCond(final GoalRoomRoadmapNode node) { - return checkFeed.goalRoomRoadmapNode.eq(node); - } - - private BooleanExpression goalRoomCond(final GoalRoom goalRoom) { - return goalRoomMember.goalRoom.eq(goalRoom); - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedRepository.java deleted file mode 100644 index 25f009dcd..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/CheckFeedRepository.java +++ /dev/null @@ -1,42 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -public interface CheckFeedRepository extends JpaRepository, CheckFeedQueryRepository { - - @Query("SELECT cf" - + " FROM CheckFeed cf" - + " WHERE cf.goalRoomMember = :goalRoomMember" - + " AND cf.createdAt >= :start" - + " AND cf.createdAt < :end") - Optional findByGoalRoomMemberAndDateTime(final GoalRoomMember goalRoomMember, final LocalDateTime start, - final LocalDateTime end); - - @Query("SELECT COUNT(cf)" - + " FROM CheckFeed cf" - + " WHERE cf.goalRoomMember = :goalRoomMember") - int countByGoalRoomMember(final GoalRoomMember goalRoomMember); - - @Query("SELECT COUNT(cf)" - + " FROM CheckFeed cf" - + " WHERE cf.goalRoomMember = :goalRoomMember" - + " AND cf.goalRoomRoadmapNode = :goalRoomRoadmapNode") - int countByGoalRoomMemberAndGoalRoomRoadmapNode(final GoalRoomMember goalRoomMember, - final GoalRoomRoadmapNode goalRoomRoadmapNode); - - @Query("SELECT cf" - + " FROM CheckFeed cf" - + " WHERE cf.goalRoomMember.goalRoom =:goalRoom" - + " ORDER BY cf.createdAt DESC") - List findByGoalRoom(final GoalRoom goalRoom); - - List findByGoalRoomRoadmapNode(final GoalRoomRoadmapNode goalRoomRoadmapNode); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepository.java deleted file mode 100644 index 5bbae175f..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomMemberQueryRepository.java +++ /dev/null @@ -1,22 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; -import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; - -public interface GoalRoomMemberQueryRepository { - - Optional findByRoadmapIdAndMemberIdentifierAndGoalRoomStatus( - @Param("roadmapId") final Long roadmapId, - @Param("identifier") final Identifier identifier, - @Param("status") final GoalRoomStatus status); - - List findByGoalRoomIdOrderedBySortType(final Long goalRoomId, - final GoalRoomMemberSortType sortType); - - Optional findGoalRoomMember(final Long goalRoomId, final Identifier memberIdentifier); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepository.java deleted file mode 100644 index 0cd2f99a2..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepository.java +++ /dev/null @@ -1,35 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.domain.Member; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - -public interface GoalRoomQueryRepository { - - Optional findGoalRoomByIdWithPessimisticLock(Long goalRoomId); - - Optional findByIdWithRoadmapContent(final Long goalRoomId); - - Optional findByIdWithContentAndTodos(final Long goalRoomId); - - List findGoalRoomsByRoadmapIdAndCond(final Long roadmapId, - final RoadmapGoalRoomsOrderType filterType, - final Long lastId, - final int pageSize); - - Optional findByIdWithTodos(final Long goalRoomId); - - List findByMember(final Member member); - - List findByMemberAndStatus(final Member member, final GoalRoomStatus goalRoomStatus); - - Optional findByIdWithNodes(final Long goalRoomId); - - List findByRoadmapId(final Long roadmapId); - - List findAllRecruitingGoalRoomsByStartDateEarlierThan(final LocalDate startDate); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepositoryImpl.java deleted file mode 100644 index e63da13d5..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomQueryRepositoryImpl.java +++ /dev/null @@ -1,177 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import static co.kirikiri.domain.goalroom.QGoalRoom.goalRoom; -import static co.kirikiri.domain.goalroom.QGoalRoomMember.goalRoomMember; -import static co.kirikiri.domain.goalroom.QGoalRoomPendingMember.goalRoomPendingMember; -import static co.kirikiri.domain.goalroom.QGoalRoomRoadmapNode.goalRoomRoadmapNode; -import static co.kirikiri.domain.goalroom.QGoalRoomToDo.goalRoomToDo; -import static co.kirikiri.roadmap.domain.QRoadmapContent.roadmapContent; - -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.domain.Member; -import co.kirikiri.persistence.QuerydslRepositorySupporter; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.BooleanExpression; -import jakarta.persistence.LockModeType; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - -public class GoalRoomQueryRepositoryImpl extends QuerydslRepositorySupporter implements GoalRoomQueryRepository { - - private static final int LIMIT_OFFSET = 1; - - public GoalRoomQueryRepositoryImpl() { - super(GoalRoom.class); - } - - @Override - public Optional findGoalRoomByIdWithPessimisticLock(final Long goalRoomId) { - return Optional.ofNullable(selectFrom(goalRoom) - .innerJoin(goalRoom.goalRoomPendingMembers.values, goalRoomPendingMember) - .fetchJoin() - .where(goalRoom.id.eq(goalRoomId)) - .setLockMode(LockModeType.PESSIMISTIC_WRITE) - .fetchOne()); - } - - @Override - public Optional findByIdWithRoadmapContent(final Long goalRoomId) { - return Optional.ofNullable(selectFrom(goalRoom) - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .fetchJoin() - .where(goalRoomIdCond(goalRoomId)) - .fetchFirst()); - } - - @Override - public Optional findByIdWithContentAndTodos(final Long goalRoomId) { - return Optional.ofNullable(selectFrom(goalRoom) - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .fetchJoin() - .leftJoin(goalRoom.goalRoomToDos.values, goalRoomToDo) - .fetchJoin() - .where(goalRoomIdCond(goalRoomId)) - .fetchOne()); - } - - @Override - public List findGoalRoomsByRoadmapIdAndCond(final Long roadmapId, - final RoadmapGoalRoomsOrderType orderType, - final Long lastId, - final int pageSize) { - return selectFrom(goalRoom) - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .on(roadmapContent.roadmapId.eq(roadmapId)) - .where( - statusCond(orderType), - lessThanLastId(lastId, orderType)) - .limit(pageSize + LIMIT_OFFSET) - .orderBy(sortCond(orderType)) - .fetch(); - } - - @Override - public Optional findByIdWithTodos(final Long goalRoomId) { - return Optional.ofNullable(selectFrom(goalRoom) - .leftJoin(goalRoom.goalRoomToDos.values, goalRoomToDo) - .fetchJoin() - .where(goalRoomIdCond(goalRoomId)) - .fetchFirst()); - } - - @Override - public List findByMember(final Member member) { - return selectFrom(goalRoom) - .leftJoin(goalRoom.goalRoomPendingMembers.values, goalRoomPendingMember) - .leftJoin(goalRoom.goalRoomMembers.values, goalRoomMember) - .where(goalRoomPendingMember.member.eq(member) - .or(goalRoomMember.member.eq(member))) - .fetch(); - } - - @Override - public List findByMemberAndStatus(final Member member, final GoalRoomStatus goalRoomStatus) { - return selectFrom(goalRoom) - .leftJoin(goalRoom.goalRoomPendingMembers.values, goalRoomPendingMember) - .leftJoin(goalRoom.goalRoomMembers.values, goalRoomMember) - .where(goalRoomPendingMember.member.eq(member) - .or(goalRoomMember.member.eq(member))) - .where(statusCond(goalRoomStatus)) - .fetch(); - } - - @Override - public Optional findByIdWithNodes(final Long goalRoomId) { - return Optional.ofNullable(selectFrom(goalRoom) - .innerJoin(goalRoom.goalRoomRoadmapNodes.values, goalRoomRoadmapNode) - .fetchJoin() - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .fetchJoin() - .where(goalRoomIdCond(goalRoomId)) - .fetchOne()); - } - - @Override - public List findByRoadmapId(final Long roadmapId) { - return selectFrom(goalRoom) - .innerJoin(goalRoom.roadmapContent, roadmapContent) - .where(roadmapContent.roadmapId.eq(roadmapId)) - .fetch(); - } - - @Override - public List findAllRecruitingGoalRoomsByStartDateEarlierThan(final LocalDate date) { - return selectFrom(goalRoom) - .innerJoin(goalRoom.goalRoomPendingMembers.values, goalRoomPendingMember) - .fetchJoin() - .where(statusCond(GoalRoomStatus.RECRUITING)) - .where(equalOrEarlierStartDateThan(date)) - .fetch(); - } - - private BooleanExpression goalRoomIdCond(final Long goalRoomId) { - return goalRoom.id.eq(goalRoomId); - } - - private BooleanExpression statusCond(final GoalRoomStatus status) { - return goalRoom.status.eq(status); - } - - private BooleanExpression statusCond(final RoadmapGoalRoomsOrderType orderType) { - if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { - return statusCond(GoalRoomStatus.RECRUITING); - } - return null; - } - - private OrderSpecifier sortCond(final RoadmapGoalRoomsOrderType orderType) { - if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { - return goalRoom.startDate.asc(); - } - return goalRoom.createdAt.desc(); - } - - private BooleanExpression lessThanLastId(final Long lastId, final RoadmapGoalRoomsOrderType orderType) { - if (lastId == null) { - return null; - } - if (orderType == RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE) { - return select(goalRoom.startDate) - .from(goalRoom) - .where(goalRoom.id.eq(lastId)) - .lt(goalRoom.startDate); - } - return goalRoom.createdAt.lt( - select(goalRoom.createdAt) - .from(goalRoom) - .where(goalRoom.id.eq(lastId)) - ); - } - - private BooleanExpression equalOrEarlierStartDateThan(final LocalDate date) { - return goalRoom.startDate.loe(date); - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomRepository.java deleted file mode 100644 index c3262251f..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.GoalRoom; -import org.springframework.data.jpa.repository.JpaRepository; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - -public interface GoalRoomRepository extends JpaRepository, GoalRoomQueryRepository { - - Optional findById(final Long goalRoomId); - - List findAllByEndDate(final LocalDate endDate); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepository.java deleted file mode 100644 index 0fc94aa76..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package co.kirikiri.persistence.goalroom; - -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.member.domain.vo.Identifier; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; - -public interface GoalRoomToDoCheckRepository extends JpaRepository { - - @Query("select gc from GoalRoomToDoCheck gc " - + "inner join fetch gc.goalRoomMember gcm " - + "inner join fetch gcm.member m " - + "inner join fetch gcm.goalRoom g " - + "where m.identifier = :identifier " - + "and gc.goalRoomToDo = :goalRoomTodo " - + "and g.id = :goalRoomId") - Optional findByGoalRoomIdAndTodoAndMemberIdentifier( - @Param("goalRoomId") final Long goalRoomId, - @Param("goalRoomTodo") final GoalRoomToDo goalRoomToDo, - @Param("identifier") final Identifier identifier); - - @Query("select gc from GoalRoomToDoCheck gc " - + "inner join fetch gc.goalRoomMember gcm " - + "inner join fetch gcm.member m " - + "inner join fetch gcm.goalRoom g " - + "where m.identifier = :identifier " - + "and g.id = :goalRoomId ") - List findByGoalRoomIdAndMemberIdentifier( - @Param("goalRoomId") final Long goalRoomId, - @Param("identifier") final Identifier identifier); - - @Modifying - @Query("delete from GoalRoomToDoCheck gc " - + "where gc.goalRoomMember = :goalRoomMember " - + "and gc.goalRoomToDo.id = :todoId") - void deleteByGoalRoomMemberAndToDoId(@Param("goalRoomMember") final GoalRoomMember goalRoomMember, - @Param("todoId") final Long todoId); -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/controller/RoadmapController.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/controller/RoadmapController.java index fb5f855c3..a7750d410 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/controller/RoadmapController.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/controller/RoadmapController.java @@ -2,6 +2,7 @@ import co.kirikiri.common.interceptor.Authenticated; import co.kirikiri.common.resolver.MemberIdentifier; +import co.kirikiri.common.service.dto.CustomScrollRequest; import co.kirikiri.roadmap.service.RoadmapCreateService; import co.kirikiri.roadmap.service.RoadmapReadService; import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; @@ -16,8 +17,9 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; -import co.kirikiri.service.dto.CustomScrollRequest; import jakarta.validation.Valid; +import java.net.URI; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -31,8 +33,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.net.URI; -import java.util.List; @RestController @RequestMapping("/roadmaps") diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepository.java new file mode 100644 index 000000000..a743d8e9b --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepository.java @@ -0,0 +1,9 @@ +package co.kirikiri.roadmap.persistence; + +import co.kirikiri.roadmap.domain.RoadmapContent; +import java.util.Optional; + +public interface RoadmapContentQueryRepository { + + Optional findByIdWithRoadmapNodes(final Long id); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepositoryImpl.java new file mode 100644 index 000000000..afd29d68d --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentQueryRepositoryImpl.java @@ -0,0 +1,25 @@ +package co.kirikiri.roadmap.persistence; + +import static co.kirikiri.roadmap.domain.QRoadmapContent.roadmapContent; +import static co.kirikiri.roadmap.domain.QRoadmapNode.roadmapNode; + +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; +import co.kirikiri.roadmap.domain.RoadmapContent; +import java.util.Optional; + +public class RoadmapContentQueryRepositoryImpl extends QuerydslRepositorySupporter implements + RoadmapContentQueryRepository { + + public RoadmapContentQueryRepositoryImpl() { + super(RoadmapContent.class); + } + + @Override + public Optional findByIdWithRoadmapNodes(final Long id) { + return Optional.ofNullable(selectFrom(roadmapContent) + .innerJoin(roadmapContent.nodes.values, roadmapNode) + .fetchJoin() + .where(roadmapContent.id.eq(id)) + .fetchOne()); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentRepository.java index 54396bac9..2ce268041 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentRepository.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapContentRepository.java @@ -1,11 +1,11 @@ package co.kirikiri.roadmap.persistence; import co.kirikiri.roadmap.domain.RoadmapContent; -import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; -public interface RoadmapContentRepository extends JpaRepository { +public interface RoadmapContentRepository extends JpaRepository, RoadmapContentQueryRepository { Optional findFirstByRoadmapIdOrderByCreatedAtDesc(final Long roadmapId); diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepository.java new file mode 100644 index 000000000..f787caff8 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepository.java @@ -0,0 +1,9 @@ +package co.kirikiri.roadmap.persistence; + +import co.kirikiri.roadmap.domain.RoadmapNode; +import java.util.Optional; + +public interface RoadmapNodeQueryRepository { + + Optional findByIdWithRoadmapNodeImages(final Long id); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepositoryImpl.java new file mode 100644 index 000000000..79b1895f3 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeQueryRepositoryImpl.java @@ -0,0 +1,25 @@ +package co.kirikiri.roadmap.persistence; + +import static co.kirikiri.roadmap.domain.QRoadmapNode.roadmapNode; +import static co.kirikiri.roadmap.domain.QRoadmapNodeImage.roadmapNodeImage; + +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; +import co.kirikiri.roadmap.domain.RoadmapNode; +import java.util.Optional; + +public class RoadmapNodeQueryRepositoryImpl extends QuerydslRepositorySupporter implements + RoadmapNodeQueryRepository { + + public RoadmapNodeQueryRepositoryImpl() { + super(RoadmapNode.class); + } + + @Override + public Optional findByIdWithRoadmapNodeImages(final Long id) { + return Optional.ofNullable(selectFrom(roadmapNode) + .innerJoin(roadmapNode.roadmapNodeImages.values, roadmapNodeImage) + .fetchJoin() + .where(roadmapNode.id.eq(id)) + .fetchOne()); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeRepository.java new file mode 100644 index 000000000..37013e7f0 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapNodeRepository.java @@ -0,0 +1,8 @@ +package co.kirikiri.roadmap.persistence; + +import co.kirikiri.roadmap.domain.RoadmapNode; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RoadmapNodeRepository extends JpaRepository, RoadmapNodeQueryRepository { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapQueryRepositoryImpl.java index ba7f7f3b9..92a04755d 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapQueryRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapQueryRepositoryImpl.java @@ -1,15 +1,16 @@ package co.kirikiri.roadmap.persistence; -import static co.kirikiri.domain.goalroom.QGoalRoom.goalRoom; -import static co.kirikiri.domain.goalroom.QGoalRoomMember.goalRoomMember; +import static co.kirikiri.goalroom.domain.QGoalRoom.goalRoom; +import static co.kirikiri.goalroom.domain.QGoalRoomMember.goalRoomMember; import static co.kirikiri.member.domain.QMember.member; import static co.kirikiri.roadmap.domain.QRoadmap.roadmap; import static co.kirikiri.roadmap.domain.QRoadmapCategory.roadmapCategory; +import static co.kirikiri.roadmap.domain.QRoadmapContent.roadmapContent; import static co.kirikiri.roadmap.domain.QRoadmapReview.roadmapReview; import static co.kirikiri.roadmap.domain.QRoadmapTag.roadmapTag; +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.persistence.QuerydslRepositorySupporter; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapStatus; @@ -160,13 +161,15 @@ private OrderSpecifier sortCond(final RoadmapOrderType orderType) { if (orderType == RoadmapOrderType.GOAL_ROOM_COUNT) { return new OrderSpecifier<>( Order.DESC, - goalRoomCountCond(goalRoom.roadmapContent.roadmapId.eq(roadmap.id)) + goalRoomCountCond(goalRoom.roadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(roadmap.id))) ); } if (orderType == RoadmapOrderType.PARTICIPANT_COUNT) { return new OrderSpecifier<>( Order.DESC, - participantCountCond(goalRoomMember.goalRoom.roadmapContent.roadmapId.eq(roadmap.id)) + participantCountCond( + goalRoomMember.goalRoom.roadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(roadmap.id)) + ) ); } if (orderType == RoadmapOrderType.REVIEW_RATE) { @@ -196,19 +199,35 @@ private JPAQuery reviewRateCond(final BooleanExpression isSatisfiedRoadm .where(isSatisfiedRoadmap); } + private JPAQuery getLatestRoadmapContentIdByRoadmap(final NumberPath id) { + return select(roadmapContent.id) + .from(roadmapContent) + .where(roadmapContent.roadmapId.eq(id)) + .orderBy(roadmapContent.createdAt.desc()); + } + + private JPAQuery getLatestRoadmapContentIdByRoadmap(final Long id) { + return select(roadmapContent.id) + .from(roadmapContent) + .where(roadmapContent.roadmapId.eq(id)) + .orderBy(roadmapContent.createdAt.desc()); + } + private BooleanExpression lessThanLastId(final Long lastId, final RoadmapOrderType orderType) { if (lastId == null) { return null; } if (orderType == RoadmapOrderType.GOAL_ROOM_COUNT) { - final NumberPath goalRoomRoadmapId = goalRoom.roadmapContent.roadmapId; - return goalRoomCountCond(goalRoomRoadmapId.eq(roadmap.id)) - .lt(goalRoomCountCond(goalRoomRoadmapId.eq(lastId))); + final NumberPath goalRoomRoadmapContentId = goalRoom.roadmapContentId; + return goalRoomCountCond(goalRoomRoadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(roadmap.id))) + .lt(goalRoomCountCond(goalRoomRoadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(lastId)))); } if (orderType == RoadmapOrderType.PARTICIPANT_COUNT) { - final NumberPath goalRoomMemberRoadmapId = goalRoomMember.goalRoom.roadmapContent.roadmapId; - return participantCountCond(goalRoomMemberRoadmapId.eq(roadmap.id)) - .lt(participantCountCond(goalRoomMemberRoadmapId.eq(lastId))); + final NumberPath goalRoomMemberRoadmapContentId = goalRoomMember.goalRoom.roadmapContentId; + return participantCountCond( + goalRoomMemberRoadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(roadmap.id))) + .lt(participantCountCond( + goalRoomMemberRoadmapContentId.eq(getLatestRoadmapContentIdByRoadmap(lastId)))); } if (orderType == RoadmapOrderType.REVIEW_RATE) { final NumberPath roadmapReviewRoadmapId = roadmapReview.roadmapId; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapReviewQueryRepositoryImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapReviewQueryRepositoryImpl.java index 161d12969..c39898a9a 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapReviewQueryRepositoryImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/persistence/RoadmapReviewQueryRepositoryImpl.java @@ -2,7 +2,7 @@ import static co.kirikiri.roadmap.domain.QRoadmapReview.roadmapReview; -import co.kirikiri.persistence.QuerydslRepositorySupporter; +import co.kirikiri.common.persistence.QuerydslRepositorySupporter; import co.kirikiri.roadmap.domain.RoadmapReview; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolverImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolverImpl.java index 7c8e2642b..5b53544c8 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolverImpl.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/resolver/RoadmapSaveArgumentResolverImpl.java @@ -1,11 +1,13 @@ package co.kirikiri.roadmap.resolver; import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.resolver.RoadmapSaveArgumentResolver; import co.kirikiri.roadmap.service.dto.request.RoadmapNodeSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; @@ -19,13 +21,11 @@ import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.support.StandardServletMultipartResolver; -import java.util.List; @Component @RequiredArgsConstructor public class RoadmapSaveArgumentResolverImpl implements RoadmapSaveArgumentResolver { - private final ObjectMapper objectMapper; private final Validator validator; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateEventListener.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateEventListener.java index 5a4fee44f..e9d69380f 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateEventListener.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateEventListener.java @@ -5,6 +5,7 @@ import co.kirikiri.common.exception.ServerException; import co.kirikiri.common.service.FilePathGenerator; import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.FileInformation; import co.kirikiri.common.type.ImageContentType; import co.kirikiri.common.type.ImageDirType; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -14,14 +15,13 @@ import co.kirikiri.roadmap.persistence.RoadmapContentRepository; import co.kirikiri.roadmap.service.dto.RoadmapNodeSaveDto; import co.kirikiri.roadmap.service.event.RoadmapCreateEvent; -import co.kirikiri.service.dto.FileInformation; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; -import java.util.List; @Service @RequiredArgsConstructor diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateService.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateService.java index 56e3defa2..479c2cd80 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapCreateService.java @@ -33,12 +33,12 @@ import co.kirikiri.roadmap.service.event.RoadmapCreateEvent; import co.kirikiri.roadmap.service.event.RoadmapDeleteEvent; import co.kirikiri.roadmap.service.mapper.RoadmapMapper; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; @Service @Transactional @@ -54,6 +54,7 @@ public class RoadmapCreateService { private final RoadmapGoalRoomService roadmapGoalRoomService; private final ApplicationEventPublisher applicationEventPublisher; + @CacheEvict(value = "roadmapList", allEntries = true) public Long create(final RoadmapSaveRequest request, final String identifier) { final Member member = findMemberByIdentifier(identifier); final RoadmapCategory roadmapCategory = findRoadmapCategoryById(request.categoryId()); @@ -84,7 +85,8 @@ private Roadmap makeRoadmap(final Long memberId, final RoadmapSaveDto roadmapSav final RoadmapCategory roadmapCategory) { final RoadmapTags roadmapTags = makeRoadmapTags(roadmapSaveDto.tags()); return new Roadmap(roadmapSaveDto.title(), roadmapSaveDto.introduction(), - roadmapSaveDto.requiredPeriod(), RoadmapDifficulty.valueOf(roadmapSaveDto.difficulty().name()), memberId, + roadmapSaveDto.requiredPeriod(), RoadmapDifficulty.valueOf(roadmapSaveDto.difficulty().name()), + memberId, roadmapCategory, roadmapTags); } @@ -111,13 +113,13 @@ private RoadmapNodes makeRoadmapNodes(final List roadmapNode public void createReview(final Long roadmapId, final String identifier, final RoadmapReviewSaveRequest request) { final Roadmap roadmap = findRoadmapById(roadmapId); - final Long memberId = roadmapGoalRoomService.findCompletedGoalRoomMember(roadmapId, identifier) - .getId(); + final Long memberId = roadmapGoalRoomService.findCompletedGoalRoomMemberId(roadmapId, identifier); validateReviewQualification(roadmap, memberId); validateReviewCount(roadmapId, memberId); final RoadmapReviewDto roadmapReviewDto = RoadmapMapper.convertRoadmapReviewDto(request, memberId); - final RoadmapReview roadmapReview = new RoadmapReview(roadmapReviewDto.content(), roadmapReviewDto.rate(), memberId, roadmapId); + final RoadmapReview roadmapReview = new RoadmapReview(roadmapReviewDto.content(), roadmapReviewDto.rate(), + memberId, roadmapId); roadmapReviewRepository.save(roadmapReview); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapGoalRoomService.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapGoalRoomService.java index 0dbc56f73..2e3f9b142 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapGoalRoomService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapGoalRoomService.java @@ -1,21 +1,22 @@ package co.kirikiri.roadmap.service; -import co.kirikiri.member.domain.Member; +import co.kirikiri.common.service.dto.CustomScrollRequest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomNumberDto; import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; -import co.kirikiri.service.dto.CustomScrollRequest; public interface RoadmapGoalRoomService { - Member findCompletedGoalRoomMember(final Long roadmapId, final String identifier); + Long findCompletedGoalRoomMemberId(final Long roadmapId, final String identifier); boolean hasGoalRooms(final Long roadmapId); RoadmapGoalRoomNumberDto findRoadmapGoalRoomsByRoadmap(final Roadmap roadmap); - RoadmapGoalRoomResponses makeRoadmapGoalRoomResponsesByOrderType(final Long roadmapId, final RoadmapGoalRoomsOrderTypeDto orderTypeDto, final CustomScrollRequest scrollRequest); + RoadmapGoalRoomResponses makeRoadmapGoalRoomResponsesByOrderType(final Long roadmapId, + final RoadmapGoalRoomsOrderTypeDto orderTypeDto, + final CustomScrollRequest scrollRequest); boolean canDeleteGoalRoomsInRoadmap(final Long roadmapId); } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapReadService.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapReadService.java index 945d37cc5..de1320744 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapReadService.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/RoadmapReadService.java @@ -4,6 +4,7 @@ import co.kirikiri.common.exception.NotFoundException; import co.kirikiri.common.mapper.ScrollResponseMapper; import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.CustomScrollRequest; import co.kirikiri.member.domain.Member; import co.kirikiri.member.domain.vo.Identifier; import co.kirikiri.member.persistence.MemberRepository; @@ -40,14 +41,13 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; import co.kirikiri.roadmap.service.mapper.RoadmapMapper; -import co.kirikiri.service.dto.CustomScrollRequest; +import java.net.URL; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.net.URL; -import java.util.List; @Service @Transactional(readOnly = true) @@ -67,7 +67,8 @@ public class RoadmapReadService { public RoadmapResponse findRoadmap(final Long roadmapId) { final Roadmap roadmap = findRoadmapById(roadmapId); final RoadmapContent recentRoadmapContent = findRecentContent(roadmapId); - final RoadmapGoalRoomNumberDto roadmapGoalRoomNumberDto = roadmapGoalRoomService.findRoadmapGoalRoomsByRoadmap(roadmap); + final RoadmapGoalRoomNumberDto roadmapGoalRoomNumberDto = roadmapGoalRoomService.findRoadmapGoalRoomsByRoadmap( + roadmap); final RoadmapDto roadmapDto = makeRoadmapDto(roadmap, recentRoadmapContent); return RoadmapMapper.convertToRoadmapResponse(roadmapDto, roadmapGoalRoomNumberDto); } @@ -224,7 +225,8 @@ public RoadmapGoalRoomResponses findRoadmapGoalRoomsByOrderType(final Long roadm final RoadmapGoalRoomsOrderTypeDto orderTypeDto, final CustomScrollRequest scrollRequest) { final Roadmap roadmap = findRoadmapById(roadmapId); - return roadmapGoalRoomService.makeRoadmapGoalRoomResponsesByOrderType(roadmap.getId(), orderTypeDto, scrollRequest); + return roadmapGoalRoomService.makeRoadmapGoalRoomResponsesByOrderType(roadmap.getId(), orderTypeDto, + scrollRequest); } public List findRoadmapReviews(final Long roadmapId, diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/RoadmapNodeSaveDto.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/RoadmapNodeSaveDto.java index 83342d7c1..ee49fc6d4 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/RoadmapNodeSaveDto.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/RoadmapNodeSaveDto.java @@ -1,6 +1,6 @@ package co.kirikiri.roadmap.service.dto; -import co.kirikiri.service.dto.FileInformation; +import co.kirikiri.common.service.dto.FileInformation; import java.util.List; public record RoadmapNodeSaveDto( diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/MemberRoadmapGoalRoomResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/MemberRoadmapGoalRoomResponse.java new file mode 100644 index 000000000..c124e4fc5 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/MemberRoadmapGoalRoomResponse.java @@ -0,0 +1,9 @@ +package co.kirikiri.roadmap.service.dto.response; + +public record MemberRoadmapGoalRoomResponse( + long id, + String name, + String imageUrl +) { + +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/RoadmapGoalRoomResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/RoadmapGoalRoomResponse.java index 379cd6bea..6e6edaae1 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/RoadmapGoalRoomResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/dto/response/RoadmapGoalRoomResponse.java @@ -1,6 +1,5 @@ package co.kirikiri.roadmap.service.dto.response; -import co.kirikiri.member.service.dto.response.MemberResponse; import java.time.LocalDate; import java.time.LocalDateTime; @@ -13,7 +12,7 @@ public record RoadmapGoalRoomResponse( LocalDateTime createdAt, LocalDate startDate, LocalDate endDate, - MemberResponse goalRoomLeader + MemberRoadmapGoalRoomResponse goalRoomLeader ) { } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/mapper/RoadmapMapper.java b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/mapper/RoadmapMapper.java index 8aa57008b..e2598ff07 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/mapper/RoadmapMapper.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/roadmap/service/mapper/RoadmapMapper.java @@ -2,6 +2,7 @@ import co.kirikiri.common.exception.ServerException; import co.kirikiri.common.mapper.ScrollResponseMapper; +import co.kirikiri.common.service.dto.FileInformation; import co.kirikiri.member.service.dto.MemberDto; import co.kirikiri.member.service.dto.response.MemberResponse; import co.kirikiri.roadmap.domain.Roadmap; @@ -36,13 +37,12 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapTagResponse; -import co.kirikiri.service.dto.FileInformation; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.Collections; import java.util.List; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class RoadmapMapper { diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/FileInformation.java b/backend/kirikiri/src/main/java/co/kirikiri/service/dto/FileInformation.java deleted file mode 100644 index 0c1fc17b7..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/FileInformation.java +++ /dev/null @@ -1,12 +0,0 @@ -package co.kirikiri.service.dto; - -import java.io.InputStream; - -public record FileInformation( - String originalFileName, - long size, - String contentType, - InputStream inputStream -) { - -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCheckFeedDto.java b/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCheckFeedDto.java deleted file mode 100644 index 12db3e0bc..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/GoalRoomCheckFeedDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package co.kirikiri.service.dto.goalroom; - -import co.kirikiri.member.service.dto.MemberDto; - -public record GoalRoomCheckFeedDto( - MemberDto memberDto, - CheckFeedDto checkFeedDto -) { - -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCheckFeedResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCheckFeedResponse.java deleted file mode 100644 index 73c50c3bc..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomCheckFeedResponse.java +++ /dev/null @@ -1,10 +0,0 @@ -package co.kirikiri.service.dto.goalroom.response; - -import co.kirikiri.member.service.dto.response.MemberResponse; - -public record GoalRoomCheckFeedResponse( - MemberResponse member, - CheckFeedResponse checkFeed -) { - -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomCreateService.java b/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomCreateService.java deleted file mode 100644 index 550c07408..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/GoalRoomCreateService.java +++ /dev/null @@ -1,333 +0,0 @@ -package co.kirikiri.service.goalroom; - -import co.kirikiri.common.aop.ExceptionConvert; -import co.kirikiri.common.exception.AuthenticationException; -import co.kirikiri.common.exception.BadRequestException; -import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.common.service.FilePathGenerator; -import co.kirikiri.common.service.FileService; -import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.common.type.ImageDirType; -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.CheckFeedRepository; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.goalroom.GoalRoomToDoCheckRepository; -import co.kirikiri.roadmap.domain.Roadmap; -import co.kirikiri.roadmap.domain.RoadmapContent; -import co.kirikiri.roadmap.domain.RoadmapNode; -import co.kirikiri.roadmap.persistence.RoadmapContentRepository; -import co.kirikiri.roadmap.persistence.RoadmapRepository; -import co.kirikiri.service.dto.FileInformation; -import co.kirikiri.service.dto.goalroom.GoalRoomCreateDto; -import co.kirikiri.service.dto.goalroom.GoalRoomRoadmapNodeDto; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.mapper.GoalRoomMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; - -@Service -@Transactional -@RequiredArgsConstructor -@ExceptionConvert -public class GoalRoomCreateService { - - private final FileService fileService; - private final FilePathGenerator filePathGenerator; - private final MemberRepository memberRepository; - private final GoalRoomRepository goalRoomRepository; - private final RoadmapRepository roadmapRepository; - private final RoadmapContentRepository roadmapContentRepository; - private final GoalRoomMemberRepository goalRoomMemberRepository; - private final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; - private final CheckFeedRepository checkFeedRepository; - - public Long create(final GoalRoomCreateRequest goalRoomCreateRequest, final String memberIdentifier) { - final GoalRoomCreateDto goalRoomCreateDto = GoalRoomMapper.convertToGoalRoomCreateDto(goalRoomCreateRequest); - final RoadmapContent roadmapContent = findRoadmapContentById(goalRoomCreateDto.roadmapContentId()); - validateDeletedRoadmap(roadmapContent); - validateNodeSizeEqual(roadmapContent.nodesSize(), goalRoomCreateDto.goalRoomRoadmapNodeDtosSize()); - final GoalRoomRoadmapNodes goalRoomRoadmapNodes = makeGoalRoomRoadmapNodes( - goalRoomCreateDto.goalRoomRoadmapNodeDtos(), roadmapContent); - final Member leader = findMemberByIdentifier(memberIdentifier); - - final GoalRoom goalRoom = new GoalRoom(goalRoomCreateDto.goalRoomName(), goalRoomCreateDto.limitedMemberCount(), - roadmapContent, leader); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoomRepository.save(goalRoom).getId(); - } - - private RoadmapContent findRoadmapContentById(final Long roadmapContentId) { - return roadmapContentRepository.findById(roadmapContentId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 로드맵 컨텐츠입니다.")); - } - - private void validateDeletedRoadmap(final RoadmapContent roadmapContent) { - final Roadmap roadmap = findRoadmapById(roadmapContent.getRoadmapId()); - if (roadmap.isDeleted()) { - throw new BadRequestException("삭제된 로드맵에 대해 골룸을 생성할 수 없습니다."); - } - } - - private Roadmap findRoadmapById(final Long roadmapId) { - return roadmapRepository.findById(roadmapId) - .orElseThrow(() -> new AuthenticationException("존재하지 않는 로드맵입니다.")); - } - - private void validateNodeSizeEqual(final int roadmapNodesSize, final int goalRoomRoadmapNodeDtosSize) { - if (roadmapNodesSize != goalRoomRoadmapNodeDtosSize) { - throw new BadRequestException("모든 노드에 대해 기간이 설정돼야 합니다."); - } - } - - private GoalRoomRoadmapNodes makeGoalRoomRoadmapNodes(final List goalRoomRoadmapNodeDtos, - final RoadmapContent roadmapContent) { - final List goalRoomRoadmapNodes = goalRoomRoadmapNodeDtos.stream() - .map(it -> makeGoalRoomRoadmapNode(roadmapContent, it)) - .toList(); - return new GoalRoomRoadmapNodes(goalRoomRoadmapNodes); - } - - private GoalRoomRoadmapNode makeGoalRoomRoadmapNode(final RoadmapContent roadmapContent, - final GoalRoomRoadmapNodeDto it) { - return new GoalRoomRoadmapNode(new Period(it.startDate(), it.endDate()), it.checkCount(), - findRoadmapNode(roadmapContent, it.roadmapNodeId())); - } - - private RoadmapNode findRoadmapNode(final RoadmapContent roadmapContent, final Long roadmapNodeId) { - return roadmapContent.findRoadmapNodeById(roadmapNodeId) - .orElseThrow(() -> new NotFoundException("로드맵에 존재하지 않는 노드입니다.")); - } - - private Member findMemberByIdentifier(final String memberIdentifier) { - return memberRepository.findByIdentifier(new Identifier(memberIdentifier)) - .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); - } - - public void join(final String identifier, final Long goalRoomId) { - final Member member = findMemberByIdentifier(identifier); - final GoalRoom goalRoom = findGoalRoomByIdWithPessimisticLock(goalRoomId); - goalRoom.join(member); - } - - private GoalRoom findGoalRoomByIdWithPessimisticLock(final Long goalRoomId) { - return goalRoomRepository.findGoalRoomByIdWithPessimisticLock(goalRoomId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); - } - - public Long addGoalRoomTodo(final Long goalRoomId, final String identifier, - final GoalRoomTodoRequest goalRoomTodoRequest) { - final Member member = findMemberByIdentifier(identifier); - final GoalRoom goalRoom = findGoalRoomById(goalRoomId); - checkGoalRoomCompleted(goalRoom); - checkGoalRoomLeader(member, goalRoom, "골룸의 리더만 투두리스트를 추가할 수 있습니다."); - final GoalRoomToDo goalRoomToDo = GoalRoomMapper.convertToGoalRoomTodo(goalRoomTodoRequest); - goalRoom.addGoalRoomTodo(goalRoomToDo); - goalRoomRepository.save(goalRoom); - return goalRoom.findLastGoalRoomTodo().getId(); - } - - private GoalRoom findGoalRoomById(final Long goalRoomId) { - return goalRoomRepository.findById(goalRoomId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); - } - - private void checkGoalRoomCompleted(final GoalRoom goalRoom) { - if (goalRoom.isCompleted()) { - throw new BadRequestException("이미 종료된 골룸입니다."); - } - } - - private void checkGoalRoomLeader(final Member member, final GoalRoom goalRoom, final String errorMessage) { - if (goalRoom.isNotLeader(member)) { - throw new BadRequestException(errorMessage); - } - } - - public GoalRoomToDoCheckResponse checkGoalRoomTodo(final Long goalRoomId, final Long todoId, - final String identifier) { - final Identifier memberIdentifier = new Identifier(identifier); - final GoalRoom goalRoom = findGoalRoomWithTodos(goalRoomId); - final GoalRoomToDo goalRoomToDo = findGoalRoomTodoById(todoId, goalRoom); - final GoalRoomMember goalRoomMember = findGoalRoomMember(memberIdentifier, goalRoom); - - final boolean isAlreadyChecked = goalRoomToDoCheckRepository.findByGoalRoomIdAndTodoAndMemberIdentifier( - goalRoomId, goalRoomToDo, memberIdentifier).isPresent(); - if (isAlreadyChecked) { - goalRoomToDoCheckRepository.deleteByGoalRoomMemberAndToDoId(goalRoomMember, todoId); - return new GoalRoomToDoCheckResponse(false); - } - final GoalRoomToDoCheck goalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, goalRoomToDo); - goalRoomToDoCheckRepository.save(goalRoomToDoCheck); - return new GoalRoomToDoCheckResponse(true); - } - - private GoalRoom findGoalRoomWithTodos(final Long goalRoomId) { - return goalRoomRepository.findByIdWithTodos(goalRoomId) - .orElseThrow(() -> new NotFoundException("골룸이 존재하지 않습니다. goalRoomId = " + goalRoomId)); - } - - private GoalRoomToDo findGoalRoomTodoById(final Long todoId, final GoalRoom goalRoom) { - return goalRoom.findGoalRoomTodoByTodoId(todoId) - .orElseThrow(() -> new NotFoundException("존재하지 않는 투두입니다. todoId = " + todoId)); - } - - private GoalRoomMember findGoalRoomMember(final Identifier memberIdentifier, final GoalRoom goalRoom) { - return goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(goalRoom, memberIdentifier) - .orElseThrow(() -> new NotFoundException( - "골룸에 사용자가 존재하지 않습니다. goalRoomId = " + goalRoom.getId() + " memberIdentifier = " - + memberIdentifier.getValue())); - } - - public String createCheckFeed(final String identifier, final Long goalRoomId, - final CheckFeedRequest checkFeedRequest) { - final MultipartFile checkFeedImage = checkFeedRequest.image(); - validateEmptyImage(checkFeedImage); - final FileInformation fileInformation = GoalRoomMapper.convertToFileInformation(checkFeedImage); - - final GoalRoom goalRoom = findGoalRoomById(goalRoomId); - final GoalRoomMember goalRoomMember = findGoalRoomMemberByGoalRoomAndIdentifier(goalRoom, identifier); - final GoalRoomRoadmapNode currentNode = getNodeByDate(goalRoom); - final int currentMemberCheckCount = checkFeedRepository.countByGoalRoomMemberAndGoalRoomRoadmapNode( - goalRoomMember, currentNode); - validateCheckCount(currentMemberCheckCount, goalRoomMember, currentNode); - updateAccomplishmentRate(goalRoom, goalRoomMember, currentMemberCheckCount); - - final String path = filePathGenerator.makeFilePath(ImageDirType.CHECK_FEED, - fileInformation.originalFileName()); - saveCheckFeed(checkFeedRequest, checkFeedImage, goalRoomMember, currentNode, path); - fileService.save(path, fileInformation); - return fileService.generateUrl(path, HttpMethod.GET).toExternalForm(); - } - - private void validateEmptyImage(final MultipartFile image) { - if (image.isEmpty()) { - throw new BadRequestException("인증 피드 등록 시 이미지가 반드시 포함되어야 합니다."); - } - - if (image.getOriginalFilename() == null) { - throw new BadRequestException("파일 이름은 반드시 포함되어야 합니다."); - } - } - - private GoalRoomMember findGoalRoomMemberByGoalRoomAndIdentifier(final GoalRoom goalRoom, final String identifier) { - return goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(goalRoom, new Identifier(identifier)) - .orElseThrow(() -> new NotFoundException("골룸에 해당 사용자가 존재하지 않습니다. 사용자 아이디 = " + identifier)); - } - - private GoalRoomRoadmapNode getNodeByDate(final GoalRoom goalRoom) { - return goalRoom.findNodeByDate(LocalDate.now()) - .orElseThrow(() -> new BadRequestException("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")); - } - - private void validateCheckCount(final int memberCheckCount, final GoalRoomMember member, - final GoalRoomRoadmapNode goalRoomRoadmapNode) { - validateNodeCheckCount(memberCheckCount, goalRoomRoadmapNode); - validateTodayCheckCount(member); - } - - private void validateNodeCheckCount(final int memberCheckCount, - final GoalRoomRoadmapNode goalRoomRoadmapNode) { - if (memberCheckCount >= goalRoomRoadmapNode.getCheckCount()) { - throw new BadRequestException( - "이번 노드에는 최대 " + goalRoomRoadmapNode.getCheckCount() + "번만 인증 피드를 등록할 수 있습니다."); - } - } - - private void validateTodayCheckCount(final GoalRoomMember member) { - final LocalDate today = LocalDate.now(); - final LocalDateTime todayStart = today.atStartOfDay(); - final LocalDateTime todayEnd = today.plusDays(1).atStartOfDay(); - if (checkFeedRepository.findByGoalRoomMemberAndDateTime(member, todayStart, todayEnd).isPresent()) { - throw new BadRequestException("이미 오늘 인증 피드를 등록하였습니다."); - } - } - - private void updateAccomplishmentRate(final GoalRoom goalRoom, final GoalRoomMember goalRoomMember, - final int pastCheckCount) { - final int wholeCheckCount = goalRoom.getAllCheckCount(); - final int memberCheckCount = pastCheckCount + 1; - final Double accomplishmentRate = 100 * memberCheckCount / (double) wholeCheckCount; - goalRoomMember.updateAccomplishmentRate(accomplishmentRate); - } - - private void saveCheckFeed(final CheckFeedRequest checkFeedRequest, final MultipartFile checkFeedImage, - final GoalRoomMember goalRoomMember, final GoalRoomRoadmapNode currentNode, - final String path) { - checkFeedRepository.save( - new CheckFeed(path, ImageContentType.findImageContentType(checkFeedImage.getContentType()), - checkFeedImage.getOriginalFilename(), - checkFeedRequest.description(), currentNode, goalRoomMember)); - } - - public void startGoalRoom(final String memberIdentifier, final Long goalRoomId) { - final Member member = findMemberByIdentifier(memberIdentifier); - final GoalRoom goalRoom = findGoalRoomById(goalRoomId); - checkGoalRoomLeader(member, goalRoom, "골룸의 리더만 골룸을 시작할 수 있습니다."); - validateGoalRoomStart(goalRoom); - final List goalRoomPendingMembers = goalRoom.getGoalRoomPendingMembers().getValues(); - saveGoalRoomMemberFromPendingMembers(goalRoomPendingMembers, goalRoom); - goalRoom.start(); - } - - private void validateGoalRoomStart(final GoalRoom goalRoom) { - if (goalRoom.cannotStart()) { - throw new BadRequestException("골룸의 시작 날짜가 되지 않았습니다."); - } - } - - private void saveGoalRoomMemberFromPendingMembers(final List goalRoomPendingMembers, - final GoalRoom goalRoom) { - final List goalRoomMembers = makeGoalRoomMembers(goalRoomPendingMembers); - goalRoom.addAllGoalRoomMembers(goalRoomMembers); - goalRoom.deleteAllPendingMembers(); - } - - private List makeGoalRoomMembers(final List goalRoomPendingMembers) { - return goalRoomPendingMembers.stream() - .map(this::makeGoalRoomMember) - .toList(); - } - - private GoalRoomMember makeGoalRoomMember(final GoalRoomPendingMember goalRoomPendingMember) { - return new GoalRoomMember(goalRoomPendingMember.getRole(), - goalRoomPendingMember.getJoinedAt(), goalRoomPendingMember.getGoalRoom(), - goalRoomPendingMember.getMember()); - } - - public void leave(final String identifier, final Long goalRoomId) { - final Member member = findMemberByIdentifier(identifier); - final GoalRoom goalRoom = findGoalRoomById(goalRoomId); - validateStatus(goalRoom); - goalRoom.leave(member); - if (goalRoom.isEmptyGoalRoom()) { - goalRoomRepository.delete(goalRoom); - } - } - - private void validateStatus(final GoalRoom goalRoom) { - if (goalRoom.isRunning()) { - throw new BadRequestException("진행중인 골룸에서는 나갈 수 없습니다."); - } - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/RoadmapGoalRoomServiceImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/RoadmapGoalRoomServiceImpl.java deleted file mode 100644 index 90a126e66..000000000 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/goalroom/RoadmapGoalRoomServiceImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package co.kirikiri.service.goalroom; - -import co.kirikiri.common.aop.ExceptionConvert; -import co.kirikiri.common.exception.BadRequestException; -import co.kirikiri.common.mapper.ScrollResponseMapper; -import co.kirikiri.common.service.FileService; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.service.dto.MemberDto; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; -import co.kirikiri.roadmap.domain.Roadmap; -import co.kirikiri.roadmap.service.RoadmapGoalRoomService; -import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomNumberDto; -import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; -import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; -import co.kirikiri.service.dto.CustomScrollRequest; -import co.kirikiri.service.dto.goalroom.RoadmapGoalRoomDto; -import co.kirikiri.service.dto.goalroom.RoadmapGoalRoomScrollDto; -import co.kirikiri.service.mapper.GoalRoomMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.net.URL; -import java.util.List; - -@Service -@Transactional(readOnly = true) -@RequiredArgsConstructor -@ExceptionConvert -public class RoadmapGoalRoomServiceImpl implements RoadmapGoalRoomService { - - private static final int DELETE_AFTER_MONTH = 3; - - private final GoalRoomRepository goalRoomRepository; - private final GoalRoomMemberRepository goalRoomMemberRepository; - private final FileService fileService; - - @Override - public Member findCompletedGoalRoomMember(final Long roadmapId, final String identifier) { - return goalRoomMemberRepository.findByRoadmapIdAndMemberIdentifierAndGoalRoomStatus(roadmapId, - new Identifier(identifier), GoalRoomStatus.COMPLETED) - .orElseThrow(() -> new BadRequestException( - "로드맵에 대해서 완료된 골룸이 존재하지 않습니다. roadmapId = " + roadmapId + " memberIdentifier = " + identifier)) - .getMember(); - } - - @Override - public boolean hasGoalRooms(final Long roadmapId) { - return !findGoalRoomsByRoadmapId(roadmapId).isEmpty(); - } - - private List findGoalRoomsByRoadmapId(final Long roadmapId) { - return goalRoomRepository.findByRoadmapId(roadmapId); - } - - @Override - public RoadmapGoalRoomNumberDto findRoadmapGoalRoomsByRoadmap(final Roadmap roadmap) { - return GoalRoomMapper.convertRoadmapGoalRoomDto(findGoalRoomsByRoadmapId(roadmap.getId())); - } - - @Override - public RoadmapGoalRoomResponses makeRoadmapGoalRoomResponsesByOrderType(final Long roadmapId, final RoadmapGoalRoomsOrderTypeDto orderTypeDto, final CustomScrollRequest scrollRequest) { - final RoadmapGoalRoomsOrderType orderType = GoalRoomMapper.convertToGoalRoomOrderType(orderTypeDto); - final List roadmapGoalRoomDtos = goalRoomRepository.findGoalRoomsByRoadmapIdAndCond(roadmapId, orderType, scrollRequest.lastId(), scrollRequest.size()) - .stream() - .map(this::makeGoalRoomDto) - .toList(); - final List subDtos = ScrollResponseMapper.getSubResponses(roadmapGoalRoomDtos, scrollRequest.size()); - final boolean hasNext = ScrollResponseMapper.hasNext(roadmapGoalRoomDtos.size(), scrollRequest.size()); - - return GoalRoomMapper.convertToRoadmapGoalRoomResponses(new RoadmapGoalRoomScrollDto(subDtos, hasNext)); - } - - private RoadmapGoalRoomDto makeGoalRoomDto(final GoalRoom goalRoom) { - final Member goalRoomLeader = goalRoom.findGoalRoomLeader(); - return new RoadmapGoalRoomDto(goalRoom.getId(), goalRoom.getName().getValue(), goalRoom.getStatus(), - goalRoom.getCurrentMemberCount(), goalRoom.getLimitedMemberCount().getValue(), - goalRoom.getCreatedAt(), goalRoom.getStartDate(), - goalRoom.getEndDate(), makeMemberDto(goalRoomLeader)); - } - - private MemberDto makeMemberDto(final Member creator) { - final URL url = fileService.generateUrl(creator.getImage().getServerFilePath(), HttpMethod.GET); - return new MemberDto(creator.getId(), creator.getNickname().getValue(), url.toExternalForm()); - } - - @Override - @Transactional - public boolean canDeleteGoalRoomsInRoadmap(final Long roadmapId) { - final List goalRooms = goalRoomRepository.findByRoadmapId(roadmapId); - return canDeleteRoadmapBasedOnGoalRooms(goalRooms); - } - - private boolean canDeleteRoadmapBasedOnGoalRooms(final List goalRooms) { - return goalRooms.stream() - .allMatch(goalRoom -> goalRoom.isCompleted() && goalRoom.isCompletedAfterMonths(DELETE_AFTER_MONTH)); - } -} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/controller/GoalRoomToDoController.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/controller/GoalRoomToDoController.java new file mode 100644 index 000000000..6164b3b7d --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/controller/GoalRoomToDoController.java @@ -0,0 +1,56 @@ +package co.kirikiri.todo.controller; + +import co.kirikiri.common.interceptor.Authenticated; +import co.kirikiri.common.resolver.MemberIdentifier; +import co.kirikiri.todo.service.GoalRoomToDoService; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import jakarta.validation.Valid; +import java.net.URI; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/goal-rooms/{goalRoomId}/todos") +@RequiredArgsConstructor +public class GoalRoomToDoController { + + private final GoalRoomToDoService goalRoomToDoService; + + @PostMapping + @Authenticated + public ResponseEntity addTodo(@RequestBody @Valid final GoalRoomTodoRequest goalRoomTodoRequest, + @PathVariable final Long goalRoomId, + @MemberIdentifier final String identifier) { + final Long id = goalRoomToDoService.addGoalRoomTodo(goalRoomId, identifier, goalRoomTodoRequest); + return ResponseEntity.created(URI.create("/api/goal-rooms/" + goalRoomId + "/todos/" + id)).build(); + } + + @PostMapping("/{todoId}") + @Authenticated + public ResponseEntity checkTodo(@PathVariable final Long goalRoomId, + @PathVariable final Long todoId, + @MemberIdentifier final String identifier) { + final GoalRoomToDoCheckResponse checkResponse = goalRoomToDoService.checkGoalRoomTodo(goalRoomId, todoId, + identifier); + return ResponseEntity.ok(checkResponse); + } + + @GetMapping + @Authenticated + public ResponseEntity> findAllTodos( + @PathVariable final Long goalRoomId, + @MemberIdentifier final String identifier) { + final List todoResponses = goalRoomToDoService.findAllGoalRoomTodo(goalRoomId, + identifier); + return ResponseEntity.ok(todoResponses); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDo.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDo.java similarity index 60% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDo.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDo.java index 3cea621b9..bd4df8f8d 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDo.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDo.java @@ -1,30 +1,34 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.todo.domain; import co.kirikiri.common.entity.BaseUpdatedTimeEntity; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; +import java.time.LocalDate; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.time.LocalDate; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class GoalRoomToDo extends BaseUpdatedTimeEntity { + private Long goalRoomId; + @Embedded private GoalRoomTodoContent content; @Embedded - private Period period; + private ToDoPeriod period; - public GoalRoomToDo(final GoalRoomTodoContent content, final Period period) { - this(null, content, period); + public GoalRoomToDo(final Long goalRoomId, final GoalRoomTodoContent content, final ToDoPeriod period) { + this(null, goalRoomId, content, period); } - public GoalRoomToDo(final Long id, final GoalRoomTodoContent content, final Period period) { + public GoalRoomToDo(final Long id, final Long goalRoomId, final GoalRoomTodoContent content, + final ToDoPeriod period) { this.id = id; + this.goalRoomId = goalRoomId; this.content = content; this.period = period; } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDoCheck.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDoCheck.java similarity index 66% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDoCheck.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDoCheck.java index 6a4b11c7f..1aa418fae 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDoCheck.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDoCheck.java @@ -1,4 +1,4 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.todo.domain; import co.kirikiri.common.entity.BaseEntity; import jakarta.persistence.Entity; @@ -12,16 +12,14 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class GoalRoomToDoCheck extends BaseEntity { - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "goal_room_member_id", nullable = false) - private GoalRoomMember goalRoomMember; + private Long goalRoomMemberId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "goal_room_to_do_id", nullable = false) private GoalRoomToDo goalRoomToDo; - public GoalRoomToDoCheck(final GoalRoomMember goalRoomMember, final GoalRoomToDo goalRoomToDo) { - this.goalRoomMember = goalRoomMember; + public GoalRoomToDoCheck(final Long goalRoomMemberId, final GoalRoomToDo goalRoomToDo) { + this.goalRoomMemberId = goalRoomMemberId; this.goalRoomToDo = goalRoomToDo; } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDos.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDos.java similarity index 97% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDos.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDos.java index 1a1106bba..d6498e679 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/GoalRoomToDos.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/GoalRoomToDos.java @@ -1,15 +1,15 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.todo.domain; import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/exception/GoalRoomToDoException.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/exception/GoalRoomToDoException.java new file mode 100644 index 000000000..5f7ac3426 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/exception/GoalRoomToDoException.java @@ -0,0 +1,10 @@ +package co.kirikiri.todo.domain.exception; + +import co.kirikiri.common.exception.domain.DomainException; + +public class GoalRoomToDoException extends DomainException { + + public GoalRoomToDoException(final String message) { + super(message); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContent.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContent.java similarity index 78% rename from backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContent.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContent.java index 4b5096307..862c6baa2 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContent.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContent.java @@ -1,6 +1,6 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.todo.domain.vo; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.todo.domain.exception.GoalRoomToDoException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import lombok.AccessLevel; @@ -23,7 +23,7 @@ public GoalRoomTodoContent(final String value) { private void validate(final String value) { if (value.length() < MIN_LENGTH || value.length() > MAX_LENGTH) { - throw new GoalRoomException("투두 컨텐츠의 길이가 적절하지 않습니다."); + throw new GoalRoomToDoException("투두 컨텐츠의 길이가 적절하지 않습니다."); } } diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/ToDoPeriod.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/ToDoPeriod.java new file mode 100644 index 000000000..ed8f5b8ba --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/domain/vo/ToDoPeriod.java @@ -0,0 +1,50 @@ +package co.kirikiri.todo.domain.vo; + +import co.kirikiri.todo.domain.exception.GoalRoomToDoException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import java.time.LocalDate; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ToDoPeriod { + + @Column(nullable = false) + private LocalDate startDate; + + @Column(nullable = false) + private LocalDate endDate; + + public ToDoPeriod(final LocalDate startDate, final LocalDate endDate) { + validate(startDate, endDate); + this.startDate = startDate; + this.endDate = endDate; + } + + private void validate(final LocalDate startDate, final LocalDate endDate) { + validateStartDateAfterNow(startDate); + validateStartDateBeforeOrEqualEndDate(startDate, endDate); + } + + private void validateStartDateAfterNow(final LocalDate startDate) { + if (startDate.isBefore(LocalDate.now())) { + throw new GoalRoomToDoException("시작일은 오늘보다 전일 수 없습니다."); + } + } + + private void validateStartDateBeforeOrEqualEndDate(final LocalDate startDate, final LocalDate endDate) { + if (startDate.isAfter(endDate)) { + throw new GoalRoomToDoException("시작일은 종료일보다 후일 수 없습니다."); + } + } + + public LocalDate getStartDate() { + return startDate; + } + + public LocalDate getEndDate() { + return endDate; + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepository.java new file mode 100644 index 000000000..d28d52bdd --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepository.java @@ -0,0 +1,27 @@ +package co.kirikiri.todo.persistence; + +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface GoalRoomToDoCheckRepository extends JpaRepository { + + Optional findByGoalRoomToDoAndGoalRoomMemberId(final GoalRoomToDo goalRoomToDo, + final Long goalRoomMemberId); + + @Query("select gc from GoalRoomToDoCheck gc " + + "inner join fetch gc.goalRoomToDo gt " + + "where gc.goalRoomMemberId = :goalRoomMemberId " + + "and gt.goalRoomId = :goalRoomId ") + List findByGoalRoomIdAndGoalRoomMemberId( + @Param("goalRoomId") final Long goalRoomId, + @Param("goalRoomMemberId") final Long goalRoomMemberId); + + @Modifying + void deleteByGoalRoomMemberIdAndGoalRoomToDoId(final Long goalRoomMemberId, final Long goalRoomToDoId); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoRepository.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoRepository.java new file mode 100644 index 000000000..1e21859c1 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/persistence/GoalRoomToDoRepository.java @@ -0,0 +1,10 @@ +package co.kirikiri.todo.persistence; + +import co.kirikiri.todo.domain.GoalRoomToDo; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GoalRoomToDoRepository extends JpaRepository { + + List findGoalRoomToDosByGoalRoomId(final Long goalRoomId); +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/service/DashBoardToDoServiceImpl.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/DashBoardToDoServiceImpl.java new file mode 100644 index 000000000..b60b3bfc2 --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/DashBoardToDoServiceImpl.java @@ -0,0 +1,52 @@ +package co.kirikiri.todo.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.service.DashBoardToDoService; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import co.kirikiri.todo.domain.GoalRoomToDos; +import co.kirikiri.todo.persistence.GoalRoomToDoCheckRepository; +import co.kirikiri.todo.persistence.GoalRoomToDoRepository; +import co.kirikiri.todo.service.mapper.GoalRoomToDoMapper; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +@ExceptionConvert +public class DashBoardToDoServiceImpl implements DashBoardToDoService { + + private final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; + private final GoalRoomToDoRepository goalRoomToDoRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + + @Override + @Transactional(readOnly = true) + public List findMemberCheckedGoalRoomToDoIds(final GoalRoom goalRoom, + final Long memberId) { + final Optional goalRoomMember = goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, + memberId); + if (goalRoomMember.isEmpty()) { + return Collections.emptyList(); + } + final List goalRoomToDoChecks = findByGoalRoomIdAndMemberIdentifier(goalRoom.getId(), + goalRoomMember.get().getId()); + final List goalRoomTodos = goalRoomToDoRepository.findGoalRoomToDosByGoalRoomId(goalRoom.getId()); + return GoalRoomToDoMapper.convertToDashBoardTodoResponsesLimit(new GoalRoomToDos(goalRoomTodos), + goalRoomToDoChecks); + } + + private List findByGoalRoomIdAndMemberIdentifier(final Long goalRoomId, + final Long goalRoomMemberId) { + return goalRoomToDoCheckRepository.findByGoalRoomIdAndGoalRoomMemberId(goalRoomId, goalRoomMemberId); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/service/GoalRoomToDoService.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/GoalRoomToDoService.java new file mode 100644 index 000000000..b6245268d --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/GoalRoomToDoService.java @@ -0,0 +1,129 @@ +package co.kirikiri.todo.service; + +import co.kirikiri.common.aop.ExceptionConvert; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.ForbiddenException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomMembers; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import co.kirikiri.todo.domain.GoalRoomToDos; +import co.kirikiri.todo.persistence.GoalRoomToDoCheckRepository; +import co.kirikiri.todo.persistence.GoalRoomToDoRepository; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import co.kirikiri.todo.service.mapper.GoalRoomToDoMapper; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +@ExceptionConvert +public class GoalRoomToDoService { + + private final GoalRoomToDoRepository goalRoomToDoRepository; + private final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final MemberRepository memberRepository; + + public Long addGoalRoomTodo(final Long goalRoomId, final String identifier, + final GoalRoomTodoRequest goalRoomTodoRequest) { + final Member member = findMemberByIdentifier(identifier); + final GoalRoom goalRoom = findGoalRoomById(goalRoomId); + checkGoalRoomCompleted(goalRoom); + checkGoalRoomLeader(member.getId(), goalRoom); + final GoalRoomToDo goalRoomToDo = GoalRoomToDoMapper.convertToGoalRoomTodo(goalRoomTodoRequest, goalRoomId); + return goalRoomToDoRepository.save(goalRoomToDo).getId(); + } + + private Member findMemberByIdentifier(final String memberIdentifier) { + return memberRepository.findByIdentifier(new Identifier(memberIdentifier)) + .orElseThrow(() -> new NotFoundException("존재하지 않는 회원입니다.")); + } + + private GoalRoom findGoalRoomById(final Long goalRoomId) { + return goalRoomRepository.findById(goalRoomId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = " + goalRoomId)); + } + + private void checkGoalRoomCompleted(final GoalRoom goalRoom) { + if (goalRoom.isCompleted()) { + throw new BadRequestException("이미 종료된 골룸입니다."); + } + } + + private void checkGoalRoomLeader(final Long memberId, final GoalRoom goalRoom) { + final GoalRoomMembers goalRoomMembers = new GoalRoomMembers( + goalRoomMemberRepository.findAllByGoalRoom(goalRoom)); + if (goalRoomMembers.isNotLeader(memberId)) { + throw new BadRequestException("골룸의 리더만 투두리스트를 추가할 수 있습니다."); + } + } + + public GoalRoomToDoCheckResponse checkGoalRoomTodo(final Long goalRoomId, final Long todoId, + final String identifier) { + final Identifier memberIdentifier = new Identifier(identifier); + final GoalRoom goalRoom = findGoalRoomById(goalRoomId); + final GoalRoomToDo goalRoomToDo = findGoalRoomTodoById(todoId); + final GoalRoomMember goalRoomMember = findGoalRoomMember(memberIdentifier, goalRoom); + + final boolean isAlreadyChecked = goalRoomToDoCheckRepository.findByGoalRoomToDoAndGoalRoomMemberId(goalRoomToDo, + goalRoomMember.getId()).isPresent(); + if (isAlreadyChecked) { + goalRoomToDoCheckRepository.deleteByGoalRoomMemberIdAndGoalRoomToDoId(goalRoomMember.getId(), todoId); + return new GoalRoomToDoCheckResponse(false); + } + final GoalRoomToDoCheck goalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember.getId(), goalRoomToDo); + goalRoomToDoCheckRepository.save(goalRoomToDoCheck); + return new GoalRoomToDoCheckResponse(true); + } + + private GoalRoomToDo findGoalRoomTodoById(final Long todoId) { + return goalRoomToDoRepository.findById(todoId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 투두입니다. todoId = " + todoId)); + } + + private GoalRoomMember findGoalRoomMember(final Identifier memberIdentifier, final GoalRoom goalRoom) { + final Member member = findMemberByIdentifier(memberIdentifier.getValue()); + return goalRoomMemberRepository.findByGoalRoomAndMemberId(goalRoom, member.getId()) + .orElseThrow(() -> new NotFoundException( + "골룸에 사용자가 존재하지 않습니다. goalRoomId = " + goalRoom.getId() + " memberIdentifier = " + + memberIdentifier.getValue())); + } + + @Transactional(readOnly = true) + public List findAllGoalRoomTodo(final Long goalRoomId, final String identifier) { + final GoalRoomToDos goalRoomToDos = findGoalRoomTodosByGoalRoomId(goalRoomId); + final GoalRoomMember goalRoomMember = findGoalRoomMember(goalRoomId, identifier); + final List checkedTodos = findMemberCheckedGoalRoomToDos(goalRoomId, goalRoomMember.getId()); + return GoalRoomToDoMapper.convertGoalRoomTodoResponses(goalRoomToDos, checkedTodos); + } + + private GoalRoomToDos findGoalRoomTodosByGoalRoomId(final Long goalRoomId) { + findGoalRoomById(goalRoomId); + return new GoalRoomToDos(goalRoomToDoRepository.findGoalRoomToDosByGoalRoomId(goalRoomId)); + } + + private GoalRoomMember findGoalRoomMember(final Long goalRoomId, final String identifier) { + final Member member = findMemberByIdentifier(identifier); + return goalRoomMemberRepository.findByGoalRoomIdAndMemberId(goalRoomId, member.getId()) + .orElseThrow(() -> new ForbiddenException( + "골룸에 참여하지 않은 사용자입니다. goalRoomId = " + goalRoomId + " memberIdentifier = " + identifier)); + } + + private List findMemberCheckedGoalRoomToDos(final Long goalRoomId, final Long goalRoomMemberId) { + return goalRoomToDoCheckRepository.findByGoalRoomIdAndGoalRoomMemberId(goalRoomId, goalRoomMemberId); + } +} diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomTodoRequest.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/request/GoalRoomTodoRequest.java similarity index 92% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomTodoRequest.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/request/GoalRoomTodoRequest.java index 8f7da0ce8..b3fffa76b 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/request/GoalRoomTodoRequest.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/request/GoalRoomTodoRequest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.request; +package co.kirikiri.todo.service.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotBlank; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomToDoCheckResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomToDoCheckResponse.java similarity index 59% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomToDoCheckResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomToDoCheckResponse.java index 5da8c9c95..d8cbe2534 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomToDoCheckResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomToDoCheckResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.todo.service.dto.response; public record GoalRoomToDoCheckResponse( boolean isChecked diff --git a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomTodoResponse.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomTodoResponse.java similarity index 80% rename from backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomTodoResponse.java rename to backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomTodoResponse.java index be7247689..2242744a2 100644 --- a/backend/kirikiri/src/main/java/co/kirikiri/service/dto/goalroom/response/GoalRoomTodoResponse.java +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/dto/response/GoalRoomTodoResponse.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.goalroom.response; +package co.kirikiri.todo.service.dto.response; import java.time.LocalDate; diff --git a/backend/kirikiri/src/main/java/co/kirikiri/todo/service/mapper/GoalRoomToDoMapper.java b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/mapper/GoalRoomToDoMapper.java new file mode 100644 index 000000000..32c47209e --- /dev/null +++ b/backend/kirikiri/src/main/java/co/kirikiri/todo/service/mapper/GoalRoomToDoMapper.java @@ -0,0 +1,67 @@ +package co.kirikiri.todo.service.mapper; + +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoCheckResponse; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import co.kirikiri.todo.domain.GoalRoomToDos; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import java.util.List; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GoalRoomToDoMapper { + + private static final int MAX_MEMBER_GOAL_ROOM_TODO_NUMBER = 3; + + public static GoalRoomToDo convertToGoalRoomTodo(final GoalRoomTodoRequest goalRoomTodoRequest, final Long goalRoomId) { + return new GoalRoomToDo(goalRoomId, new GoalRoomTodoContent(goalRoomTodoRequest.content()), + new ToDoPeriod(goalRoomTodoRequest.startDate(), goalRoomTodoRequest.endDate())); + } + + public static List convertGoalRoomTodoResponses(final GoalRoomToDos goalRoomToDos, + final List checkedTodos) { + return goalRoomToDos.getValues().stream() + .map(goalRoomToDo -> convertGoalRoomTodoResponse(checkedTodos, goalRoomToDo)) + .toList(); + } + + private static GoalRoomTodoResponse convertGoalRoomTodoResponse(final List checkedTodos, + final GoalRoomToDo goalRoomToDo) { + final GoalRoomToDoCheckResponse checkResponse = new GoalRoomToDoCheckResponse( + isCheckedTodo(goalRoomToDo.getId(), checkedTodos)); + return new GoalRoomTodoResponse(goalRoomToDo.getId(), goalRoomToDo.getContent(), goalRoomToDo.getStartDate(), + goalRoomToDo.getEndDate(), checkResponse); + } + + private static boolean isCheckedTodo(final Long targetTodoId, final List checkedTodos) { + final List checkTodoIds = checkedTodos.stream() + .map(goalRoomToDoCheck -> goalRoomToDoCheck.getGoalRoomToDo().getId()) + .toList(); + return checkTodoIds.contains(targetTodoId); + } + + public static List convertToDashBoardTodoResponsesLimit(final GoalRoomToDos goalRoomToDos, + final List checkedTodos) { + return goalRoomToDos.getValues() + .stream() + .map(goalRoomToDo -> convertToDashBoardTodoResponse(checkedTodos, goalRoomToDo)) + .limit(MAX_MEMBER_GOAL_ROOM_TODO_NUMBER) + .toList(); + } + + private static DashBoardToDoResponse convertToDashBoardTodoResponse(final List checkedTodos, + final GoalRoomToDo goalRoomToDo) { + final DashBoardToDoCheckResponse checkResponse = new DashBoardToDoCheckResponse( + isCheckedTodo(goalRoomToDo.getId(), checkedTodos)); + return new DashBoardToDoResponse(goalRoomToDo.getId(), + goalRoomToDo.getContent(), + goalRoomToDo.getStartDate(), goalRoomToDo.getEndDate(), + checkResponse); + } +} diff --git a/backend/kirikiri/src/main/resources/properties b/backend/kirikiri/src/main/resources/properties index c5f41e5e1..ae1361d17 160000 --- a/backend/kirikiri/src/main/resources/properties +++ b/backend/kirikiri/src/main/resources/properties @@ -1 +1 @@ -Subproject commit c5f41e5e110dcb998446e18a52b31f6f4cc33d6f +Subproject commit ae1361d17d2ab3ba82f0f519589b36181ad4dd6e diff --git a/backend/kirikiri/src/test/java/co/kirikiri/auth/controller/AuthCreateApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/auth/controller/AuthCreateApiTest.java index d6d76ae48..2cd666391 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/auth/controller/AuthCreateApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/auth/controller/AuthCreateApiTest.java @@ -6,7 +6,9 @@ import static org.mockito.Mockito.doThrow; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -17,10 +19,11 @@ import co.kirikiri.auth.service.dto.response.AuthenticationResponse; import co.kirikiri.auth.service.dto.response.OauthRedirectResponse; import co.kirikiri.common.exception.AuthenticationException; -import co.kirikiri.controller.helper.ControllerTestHelper; -import co.kirikiri.controller.helper.FieldDescriptionHelper.FieldDescription; -import co.kirikiri.service.dto.ErrorResponse; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.helper.FieldDescriptionHelper.FieldDescription; +import co.kirikiri.common.service.dto.ErrorResponse; import com.fasterxml.jackson.core.type.TypeReference; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -28,7 +31,6 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; -import java.util.List; @WebMvcTest(AuthController.class) class AuthCreateApiTest extends ControllerTestHelper { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedApiTest.java new file mode 100644 index 000000000..d75d76fd7 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/controller/GoalRoomCheckFeedApiTest.java @@ -0,0 +1,326 @@ +package co.kirikiri.checkfeed.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.partWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParts; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import co.kirikiri.checkfeed.service.GoalRoomCheckFeedService; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedMemberResponse; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedResponse; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.service.dto.ErrorResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.MvcResult; + +@WebMvcTest(GoalRoomCheckFeedController.class) +class GoalRoomCheckFeedApiTest extends ControllerTestHelper { + + @MockBean + private GoalRoomCheckFeedService goalRoomCheckFeedService; + + @Test + void 인증_피드_등록_요청을_보낸다() throws Exception { + //given + final String imageName = "image"; + final String originalImageName = "originalImageName.jpeg"; + final String contentType = "image/jpeg"; + final String image = "테스트 이미지"; + final String description = "이미지 설명"; + final String filePath = "path/to/directories/" + contentType; + final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, + contentType, image.getBytes()); + + given(goalRoomCheckFeedService.createCheckFeed(anyString(), anyLong(), any())) + .willReturn(filePath); + + //expect + mockMvc.perform( + RestDocumentationRequestBuilders + .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .file(imageFile) + .file("text", description.getBytes()) + .header("Authorization", "Bearer accessToken") + .contextPath(API_PREFIX) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) + .andExpect(status().isCreated()) + .andExpect(header().string("Location", filePath)) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName("Authorization").description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + requestParts( + partWithName("image").description("업로드한 이미지"), + partWithName("text").description("인증 피드 본문") + ), + responseHeaders( + headerWithName("Location").description("저장된 이미지 경로") + ))); + } + + @Test + void 인증_피드_등록시_노드_기간에_해당하지_않으면_예외가_발생한다() throws Exception { + // given + final String imageName = "image"; + final String originalImageName = "originalImageName.jpeg"; + final String contentType = "image/jpeg"; + final String image = "테스트 이미지"; + final String description = "이미지 설명"; + final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, + contentType, image.getBytes()); + + doThrow(new BadRequestException("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")) + .when(goalRoomCheckFeedService) + .createCheckFeed(anyString(), anyLong(), any()); + + //when + mockMvc.perform( + RestDocumentationRequestBuilders + .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .file(imageFile) + .param("description", description) + .header("Authorization", "Bearer accessToken") + .contextPath(API_PREFIX) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName("Authorization").description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + requestParts( + partWithName("image").description("업로드한 이미지") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))); + } + + @Test + void 인증_피드_등록_요청시_멤버가_존재하지_않을_경우_예외를_반환한다() throws Exception { + //given + final String imageName = "image"; + final String originalImageName = "originalImageName.jpeg"; + final String contentType = "image/jpeg"; + final String image = "테스트 이미지"; + final String description = "이미지 설명"; + final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, + contentType, image.getBytes()); + + doThrow(new NotFoundException("존재하지 않는 회원입니다.")) + .when(goalRoomCheckFeedService) + .createCheckFeed(anyString(), anyLong(), any()); + + //when + mockMvc.perform( + RestDocumentationRequestBuilders + .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .file(imageFile) + .file("text", description.getBytes()) + .header("Authorization", "Bearer accessToken") + .contextPath(API_PREFIX) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("존재하지 않는 회원입니다.")) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName("Authorization").description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + requestParts( + partWithName("image").description("업로드한 이미지"), + partWithName("text").description("인증 피드 본문") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))); + } + + @Test + void 인증_피드_등록_요청시_로드맵이_존재하지_않을_경우_예외를_반환한다() throws Exception { + //given + final String imageName = "image"; + final String originalImageName = "originalImageName.jpeg"; + final String contentType = "image/jpeg"; + final String image = "테스트 이미지"; + final String description = "이미지 설명"; + final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, + contentType, image.getBytes()); + + doThrow(new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = 1L")) + .when(goalRoomCheckFeedService) + .createCheckFeed(anyString(), anyLong(), any()); + + //when + mockMvc.perform( + RestDocumentationRequestBuilders + .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .file(imageFile) + .file("text", description.getBytes()) + .header("Authorization", "Bearer accessToken") + .contextPath(API_PREFIX) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("골룸 정보가 존재하지 않습니다. goalRoomId = 1L")) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName("Authorization").description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + requestParts( + partWithName("image").description("업로드한 이미지"), + partWithName("text").description("인증 피드 본문") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))); + } + + @Test + void 골룸의_인증피드를_전체_조회한다() throws Exception { + // given + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(1L, "name1", "imageUrl"), + new CheckFeedResponse(1L, "imageUrl", "image description1", LocalDate.now())); + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(2L, "name2", "imageUrl"), + new CheckFeedResponse(2L, "imageUrl", "image description2", LocalDate.now())); + + final List expected = List.of(goalRoomCheckFeedResponse2, + goalRoomCheckFeedResponse1); + + when(goalRoomCheckFeedService.findGoalRoomCheckFeeds(any(), any())) + .thenReturn(expected); + + // when + final String response = mockMvc.perform( + get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isOk()) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("[0].member.id").description("사용자 ID"), + fieldWithPath("[0].member.name").description("사용자 닉네임"), + fieldWithPath("[0].member.imageUrl").description("사용자 이미지 Url"), + fieldWithPath("[0].checkFeed.id").description("인증 피드 ID"), + fieldWithPath("[0].checkFeed.imageUrl").description("인증 피드 이미지 Url"), + fieldWithPath("[0].checkFeed.description").description("인증 피드 설명"), + fieldWithPath("[0].checkFeed.createdAt").description("인증 피드 등록 날짜")))) + .andReturn().getResponse() + .getContentAsString(); + + // then + final List 골룸_인증피드_전체_조회_응답 = objectMapper.readValue(response, + new TypeReference<>() { + }); + assertThat(골룸_인증피드_전체_조회_응답) + .isEqualTo(expected); + } + + @Test + void 골룸_인증피드_전체_조회_시_존재하지_않는_골룸일_경우_예외가_발생한다() throws Exception { + //given + doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) + .when(goalRoomCheckFeedService) + .findGoalRoomCheckFeeds(any(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo( + documentationResultHandler.document( + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))) + .andReturn(); + + // then + final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(responses).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸_인증피드_전체_조회_시_골룸에_참여하지_않은_사용자일_경우_예외_발생() throws Exception { + //given + doThrow(new BadRequestException("골룸에 참여하지 않은 회원입니다.")) + .when(goalRoomCheckFeedService) + .findGoalRoomCheckFeeds(any(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isBadRequest()) + .andDo( + documentationResultHandler.document( + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))) + .andReturn(); + + // then + final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(responses).isEqualTo(new ErrorResponse("골룸에 참여하지 않은 회원입니다.")); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/CheckFeedRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/persistence/CheckFeedRepositoryTest.java similarity index 58% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/CheckFeedRepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/checkfeed/persistence/CheckFeedRepositoryTest.java index cf3a3c241..48f2eb854 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/CheckFeedRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/persistence/CheckFeedRepositoryTest.java @@ -1,18 +1,21 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.checkfeed.persistence; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -22,7 +25,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -86,19 +88,21 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + member.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - 인증_피드를_저장한다(goalRoomRoadmapNode, joinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode, savedJoinedMember); //when - final boolean isUpdateToday = checkFeedRepository.findByGoalRoomMemberAndDateTime(joinedMember, + final boolean isUpdateToday = checkFeedRepository.findByGoalRoomMemberIdAndDateTime(joinedMember.getId(), TODAY_START, TOMORROW_START).isPresent(); - final boolean isUpdateTomorrow = checkFeedRepository.findByGoalRoomMemberAndDateTime(joinedMember, + final boolean isUpdateTomorrow = checkFeedRepository.findByGoalRoomMemberIdAndDateTime(joinedMember.getId(), TOMORROW_START, DAY_AFTER_TOMORROW_START).isPresent(); //then @@ -118,57 +122,27 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + member.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - 인증_피드를_저장한다(goalRoomRoadmapNode, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode, joinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode, savedJoinedMember); //when - final int checkCount = checkFeedRepository.countByGoalRoomMemberAndGoalRoomRoadmapNode(joinedMember, - goalRoomRoadmapNode); + final int checkCount = checkFeedRepository.countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId(joinedMember.getId(), + goalRoomRoadmapNode.getId()); //then assertThat(checkCount).isEqualTo(4); } - @Test - void 사용자가_골룸에서_등록한_인증_피드_횟수를_확인한다() { - //given - final Member creator = 사용자를_저장한다("cokiri", "코끼리"); - final Member member = 사용자를_저장한다("participant", "참여자"); - final RoadmapCategory category = 카테고리를_생성한다("여행"); - final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); - final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - - //when - final int checkCount = checkFeedRepository.countByGoalRoomMember(joinedMember); - - //then - assertThat(checkCount).isEqualTo(6); - } - @Test void 특정_골룸에서_등록된_모든_인증_피드들을_조회한다() { //given @@ -180,28 +154,31 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final GoalRoom goalRoom1 = 골룸을_저장한다(roadmapContent, creator); final GoalRoom goalRoom2 = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom1, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom1, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom1, - member); + member.getId()); final GoalRoomMember otherLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom2, - creator); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + creator.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); + final GoalRoomMember savedOtherLeader = goalRoomMemberRepository.save(otherLeader); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom1.getGoalRoomRoadmapNodes().getValues().get(0); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom1.getGoalRoomRoadmapNodes().getValues().get(1); final GoalRoomRoadmapNode otherGoalRoomRoadmapNode = goalRoom2.getGoalRoomRoadmapNodes().getValues().get(0); - final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - 인증_피드를_저장한다(otherGoalRoomRoadmapNode, otherLeader); - 인증_피드를_저장한다(otherGoalRoomRoadmapNode, otherLeader); + final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + 인증_피드를_저장한다(otherGoalRoomRoadmapNode, savedOtherLeader); + 인증_피드를_저장한다(otherGoalRoomRoadmapNode, savedOtherLeader); //when - final List checkFeeds = checkFeedRepository.findByGoalRoom(goalRoom1); + final List checkFeeds = checkFeedRepository.findByGoalRoomIdOrderByCreatedAtDesc(goalRoom1.getId()); assertThat(checkFeeds) .hasSize(6) @@ -209,7 +186,7 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, } @Test - void 골룸이_진행중일_때_특정_노드_동안_등록된_인증_피드들을_조회한다() { + void 골룸이_완료됐을_때는_특정한_노드_동안이_아닌_모든_기간_동안_등록된_인증_피드들을_조회한다() { //given final Member creator = 사용자를_저장한다("cokiri", "코끼리"); final Member member = 사용자를_저장한다("participant", "참여자"); @@ -218,39 +195,33 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + member.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); + final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); //when - final List checkFeeds1 = checkFeedRepository - .findByRunningGoalRoomRoadmapNode(goalRoomRoadmapNode1); - final List checkFeeds2 = checkFeedRepository - .findByRunningGoalRoomRoadmapNode(goalRoomRoadmapNode2); + final List checkFeeds = checkFeedRepository.findByGoalRoomIdOrderByCreatedAtDesc(goalRoom.getId()); - assertAll( - () -> assertThat(checkFeeds1) - .hasSize(3) - .isEqualTo(List.of(checkFeed3, checkFeed2, checkFeed1)), - () -> assertThat(checkFeeds2) - .hasSize(3) - .isEqualTo(List.of(checkFeed6, checkFeed5, checkFeed4)) - ); + //then + assertThat(checkFeeds) + .isEqualTo(List.of(checkFeed6, checkFeed5, checkFeed4, checkFeed3, checkFeed2, checkFeed1)); } @Test - void 진행중인_골룸에서_특정_노드_기간이_아니면_빈_인증_피드들_반환한다() { + void 골룸이_진행중일_때_특정_노드_동안_등록된_인증_피드들을_조회한다() { //given final Member creator = 사용자를_저장한다("cokiri", "코끼리"); final Member member = 사용자를_저장한다("participant", "참여자"); @@ -259,30 +230,41 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + member.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); + final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); //when final List checkFeeds1 = checkFeedRepository - .findByGoalRoomRoadmapNode(null); + .findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(goalRoomRoadmapNode1.getId()); + final List checkFeeds2 = checkFeedRepository + .findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(goalRoomRoadmapNode2.getId()); - assertThat(checkFeeds1).isEmpty(); + assertAll( + () -> assertThat(checkFeeds1) + .hasSize(3) + .isEqualTo(List.of(checkFeed3, checkFeed2, checkFeed1)), + () -> assertThat(checkFeeds2) + .hasSize(3) + .isEqualTo(List.of(checkFeed6, checkFeed5, checkFeed4)) + ); } @Test - void 골룸이_완료됐을_때는_특정한_노드_동안이_아닌_모든_기간_동안_등록된_인증_피드들을_조회한다() { + void 진행중인_골룸에서_특정_노드_기간이_아니면_빈_인증_피드들_반환한다() { //given final Member creator = 사용자를_저장한다("cokiri", "코끼리"); final Member member = 사용자를_저장한다("participant", "참여자"); @@ -291,136 +273,40 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); + final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); + member.getId()); + final GoalRoomMember savedLeader = goalRoomMemberRepository.save(leader); + final GoalRoomMember savedJoinedMember = goalRoomMemberRepository.save(joinedMember); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - final CheckFeed checkFeed1 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed2 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed3 = 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - final CheckFeed checkFeed4 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed5 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); - final CheckFeed checkFeed6 = 인증_피드를_저장한다(goalRoomRoadmapNode2, joinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode1, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); + 인증_피드를_저장한다(goalRoomRoadmapNode2, savedJoinedMember); //when - final List checkFeeds = checkFeedRepository.findByGoalRoom(goalRoom); + final List checkFeeds1 = checkFeedRepository + .findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(null); - //then - assertThat(checkFeeds) - .isEqualTo(List.of(checkFeed6, checkFeed5, checkFeed4, checkFeed3, checkFeed2, checkFeed1)); + assertThat(checkFeeds1).isEmpty(); } @Test void 골룸_노드값으로_null이_들어오면_빈_리스트를_반환한다() { //given //when - final List checkFeeds = checkFeedRepository.findByGoalRoomRoadmapNode(null); + final List checkFeeds = checkFeedRepository.findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(null); //then assertThat(checkFeeds).isEmpty(); } - @Test - void 골룸_진행_중에_특정_노드_동안_등록된_인증_피드들을_등록한_사용자의_정보와_함께_조회한다() { - //given - final Member creator = 사용자를_저장한다("cokiri", "코끼리"); - final Member member = 사용자를_저장한다("participant", "참여자"); - final RoadmapCategory category = 카테고리를_생성한다("여행"); - final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); - final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - - //when - final List checkFeeds1 = checkFeedRepository.findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage( - goalRoomRoadmapNode1); - final List checkFeeds2 = checkFeedRepository.findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage( - goalRoomRoadmapNode2); - - //then - final CheckFeed expected1 = new CheckFeed("src/test/resources/testImage", ImageContentType.JPEG, - "originalFileName", "인증 피드 본문", goalRoomRoadmapNode1, joinedMember); - final CheckFeed expected2 = new CheckFeed("src/test/resources/testImage", ImageContentType.JPEG, - "originalFileName", "인증 피드 본문", goalRoomRoadmapNode2, leader); - - assertAll( - () -> assertThat(checkFeeds1).hasSize(3) - .usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(List.of(expected1, expected1, expected1)), - () -> assertThat(checkFeeds1.get(0).getGoalRoomMember().getMember().getNickname().getValue()) - .isEqualTo("참여자"), - () -> assertThat(checkFeeds1.get(0).getGoalRoomMember().getMember().getImage().getServerFilePath()) - .isEqualTo("serverFilePath"), - () -> assertThat(checkFeeds2).hasSize(3) - .usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(List.of(expected2, expected2, expected2)), - () -> assertThat(checkFeeds2.get(0).getGoalRoomMember().getMember().getNickname().getValue()) - .isEqualTo("코끼리"), - () -> assertThat(checkFeeds2.get(0).getGoalRoomMember().getMember().getImage().getServerFilePath()) - .isEqualTo("serverFilePath") - ); - } - - @Test - void 골룸_완료_시_모든_기간_동안_등록된_인증_피드들을_등록한_사용자의_정보와_함께_조회한다() { - //given - final Member creator = 사용자를_저장한다("cokiri", "코끼리"); - final Member member = 사용자를_저장한다("participant", "참여자"); - final RoadmapCategory category = 카테고리를_생성한다("여행"); - final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - - final GoalRoomMember leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); - final GoalRoomMember joinedMember = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - member); - goalRoomMemberRepository.saveAllInBatch(List.of(leader, joinedMember)); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = goalRoom.getGoalRoomRoadmapNodes().getValues().get(1); - - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode1, joinedMember); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - 인증_피드를_저장한다(goalRoomRoadmapNode2, leader); - - //when - final List checkFeeds = checkFeedRepository.findByGoalRoomWithMemberAndMemberImage(goalRoom); - - //then - final CheckFeed expected1 = new CheckFeed("src/test/resources/testImage", ImageContentType.JPEG, - "originalFileName", "인증 피드 본문", goalRoomRoadmapNode1, joinedMember); - final CheckFeed expected2 = new CheckFeed("src/test/resources/testImage", ImageContentType.JPEG, - "originalFileName", "인증 피드 본문", goalRoomRoadmapNode2, leader); - - assertThat(checkFeeds).hasSize(6) - .usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(List.of(expected2, expected2, expected2, expected1, expected1, expected1)); - } - private Member 사용자를_저장한다(final String identifier, final String nickname) { final MemberImage memberImage = new MemberImage("originalFileName", "serverFilePath", ImageContentType.PNG); final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); @@ -460,29 +346,29 @@ public CheckFeedRepositoryTest(final MemberRepository memberRepository, } private GoalRoom 골룸을_저장한다(final RoadmapContent roadmapContent, final Member member) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), - roadmapContent, member); final List roadmapNodes = roadmapContent.getNodes().getValues(); final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TODAY, TEN_DAY_LATER), - 10, firstRoadmapNode); + 10, firstRoadmapNode.getId()); final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), - 10, secondRoadmapNode); + 10, secondRoadmapNode.getId()); final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); + + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), + roadmapContent.getId(), goalRoomRoadmapNodes); return goalRoomRepository.save(goalRoom); } private CheckFeed 인증_피드를_저장한다(final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember joinedMember) { return checkFeedRepository.save( new CheckFeed("src/test/resources/testImage", ImageContentType.JPEG, - "originalFileName", "인증 피드 본문", goalRoomRoadmapNode, joinedMember)); + "originalFileName", "인증 피드 본문", goalRoomRoadmapNode.getId(), joinedMember.getId())); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListenerTest.java b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListenerTest.java new file mode 100644 index 000000000..457020469 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/CheckFeedCreateEventListenerTest.java @@ -0,0 +1,145 @@ +package co.kirikiri.checkfeed.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.checkfeed.service.event.CheckFeedCreateEvent; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.type.ImageContentType; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +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; + +@ExtendWith(MockitoExtension.class) +class CheckFeedCreateEventListenerTest { + + private static final Member MEMBER = new Member(1L, new Identifier("identifier1"), + null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임"), + null, new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); + + @Mock + private CheckFeedRepository checkFeedRepository; + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private GoalRoomMemberRepository goalRoomMemberRepository; + + @InjectMocks + private CheckFeedCreateEventListener checkFeedCreateEventListener; + + @Test + void 정상적으로_달성률을_업데이트한다() { + // given + final CheckFeed checkFeed = 인증_피드를_생성한다(); + final GoalRoom goalRoom = 골룸을_생성한다(1L, 1L, 10); + final GoalRoomMember goalRoomMember = new GoalRoomMember(1L, GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + MEMBER.getId()); + + given(checkFeedRepository.findById(anyLong())) + .willReturn(Optional.of(checkFeed)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoomMember)); + + // when + // then + assertDoesNotThrow(() -> checkFeedCreateEventListener.handleUpdateAccomplishmentRate( + new CheckFeedCreateEvent(1L, 1L))); + } + + @Test + void 달성률_업데이트_시_존재하지_않은_인증피드이면_예외를_던진다() { + // given + given(checkFeedRepository.findById(anyLong())) + .willReturn(Optional.empty()); + + // when + // then + assertThatThrownBy( + () -> checkFeedCreateEventListener.handleUpdateAccomplishmentRate( + new CheckFeedCreateEvent(1L, 1L))) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 달성률_업데이트_시_존재하지_않은_골룸이면_예외를_던진다() { + // given + final CheckFeed checkFeed = 인증_피드를_생성한다(); + + given(checkFeedRepository.findById(anyLong())) + .willReturn(Optional.of(checkFeed)); + + // when + // then + assertThatThrownBy( + () -> checkFeedCreateEventListener.handleUpdateAccomplishmentRate( + new CheckFeedCreateEvent(1L, 1L))) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 달성률_업데이트_시_존재하지_않은_골룸_멤버이면_예외를_던진다() { + // given + final CheckFeed checkFeed = 인증_피드를_생성한다(); + final GoalRoom goalRoom = 골룸을_생성한다(1L, 1L, 10); + + given(checkFeedRepository.findById(anyLong())) + .willReturn(Optional.of(checkFeed)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + + // when + // then + assertThatThrownBy( + () -> checkFeedCreateEventListener.handleUpdateAccomplishmentRate( + new CheckFeedCreateEvent(1L, 1L))) + .isInstanceOf(NotFoundException.class); + } + + private CheckFeed 인증_피드를_생성한다() { + return new CheckFeed("src/test/resources/testImage/originalFileName.jpeg", ImageContentType.JPEG, + "originalFileName.jpeg", "인증 피드 설명", 1L, MEMBER.getId()); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Long roadmapContentId, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContentId, 골룸_로드맵_노드들을_생성한다()); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(1L, new Period(LocalDate.now(), LocalDate.now().plusDays(10)), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedServiceTest.java new file mode 100644 index 000000000..598f141b1 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/checkfeed/service/GoalRoomCheckFeedServiceTest.java @@ -0,0 +1,646 @@ +package co.kirikiri.checkfeed.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; + +import co.kirikiri.checkfeed.domain.CheckFeed; +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedMemberResponse; +import co.kirikiri.checkfeed.service.dto.response.CheckFeedResponse; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.ForbiddenException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.exception.domain.ImageExtensionException; +import co.kirikiri.common.service.FilePathGenerator; +import co.kirikiri.common.service.FileService; +import co.kirikiri.common.type.ImageContentType; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberImage; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapCategory; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapDifficulty; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodeImage; +import co.kirikiri.roadmap.domain.RoadmapNodeImages; +import co.kirikiri.roadmap.domain.RoadmapNodes; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +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 org.springframework.context.ApplicationEventPublisher; +import org.springframework.mock.web.MockMultipartFile; + +@ExtendWith(MockitoExtension.class) +class GoalRoomCheckFeedServiceTest { + + private static final LocalDate TODAY = LocalDate.now(); + private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); + private static final LocalDate TWENTY_DAY_LATER = TODAY.plusDays(20); + private static final LocalDate THIRTY_DAY_LATER = TODAY.plusDays(30); + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private GoalRoomMemberRepository goalRoomMemberRepository; + + @Mock + private CheckFeedRepository checkFeedRepository; + + @Mock + private MemberRepository memberRepository; + + @Mock + private FileService fileService; + + @Mock + private FilePathGenerator filePathGenerator; + + @Mock + private ApplicationEventPublisher applicationEventPublisher; + + @InjectMocks + private GoalRoomCheckFeedService goalRoomCheckFeedService; + + @Test + void 인증_피드_등록을_요청한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + final CheckFeed checkFeed = 인증_피드를_생성한다(goalRoomRoadmapNode, goalRoomLeader); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomLeader)); + when(checkFeedRepository.countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId(any(), any())) + .thenReturn(0); + when(checkFeedRepository.findByGoalRoomMemberIdAndDateTime(any(), any(), any())) + .thenReturn(Optional.empty()); + when(checkFeedRepository.save(any())) + .thenReturn(checkFeed); + when(filePathGenerator.makeFilePath(any(), any())) + .thenReturn("originalFileName.jpeg"); + when(fileService.generateUrl(anyString(), any())) + .thenReturn(makeUrl("originalFileName.jpeg")); + + // when + final String response = goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request); + + // then + assertThat(response).contains("originalFileName"); + } + + @Test + void 인증_피드_등록시_노드_기간에_해당하지_않으면_예외가_발생한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 시작_날짜가_미래인_골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomLeader)); + + // expected + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("인증 피드는 노드 기간 내에만 작성할 수 있습니다."); + } + + @Test + void 하루에_두_번_이상_인증_피드_등록_요청_시_예외를_반환한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + final CheckFeed checkFeed = 인증_피드를_생성한다(goalRoomRoadmapNode, goalRoomLeader); + + when(goalRoomRepository.findByIdWithNodes(any())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomLeader)); + when(checkFeedRepository.findByGoalRoomMemberIdAndDateTime(any(), any(), any())) + .thenReturn(Optional.of(checkFeed)); + + //expect + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("이미 오늘 인증 피드를 등록하였습니다."); + } + + @Test + void 골룸_노드에서_허가된_인증_횟수보다_많은_인증_피드_등록_요청_시_예외를_반환한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + + when(goalRoomRepository.findByIdWithNodes(any())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomLeader)); + when(checkFeedRepository.countByGoalRoomMemberIdAndGoalRoomRoadmapNodeId(any(), any())) + .thenReturn(goalRoomRoadmapNode.getCheckCount()); + + //expect + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request)) + .isInstanceOf(BadRequestException.class) + .hasMessage("이번 노드에는 최대 " + goalRoomRoadmapNode.getCheckCount() + "번만 인증 피드를 등록할 수 있습니다."); + } + + @Test + void 인증_피드_등록_요청_시_허용되지_않는_확장자_형식이라면_예외를_반환한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/gif"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + + when(goalRoomRepository.findByIdWithNodes(any())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomLeader)); + + // when + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request)) + .isInstanceOf(ImageExtensionException.class) + .hasMessage("허용되지 않는 확장자입니다."); + } + + @Test + void 인증_피드_등록_요청_시_존재하지_않는_골룸이라면_예외를_반환한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + + when(goalRoomRepository.findByIdWithNodes(any())) + .thenReturn(Optional.empty()); + + //expect + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("cokirikiri", 1L, request)) + .isInstanceOf(NotFoundException.class) + .hasMessage("존재하지 않는 골룸입니다. goalRoomId = 1"); + } + + @Test + void 인증_피드_등록_요청_시_사용자가_참여하지_않은_골룸이라면_예외를_반환한다() { + // given + final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); + + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + goalRoomMemberRepository.save(goalRoomLeader); + + when(goalRoomRepository.findByIdWithNodes(any())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.empty()); + + //expect + assertThatThrownBy( + () -> goalRoomCheckFeedService.createCheckFeed("identifier", 1L, request)) + .isInstanceOf(NotFoundException.class) + .hasMessage("골룸에 해당 사용자가 존재하지 않습니다. 사용자 아이디 = " + "identifier"); + } + + @Test + void 진행중인_골룸의_인증피드를_전체_조회한다() throws MalformedURLException { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri1", "password1!", "코끼리1", "kirikiri1@email"); + final Member follower = 사용자를_생성한다(2L, "cokirikiri2", "password2!", "코끼리2", "kirikiri2@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + goalRoom.start(); + + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(1L, GoalRoomRole.LEADER, LocalDateTime.now(), + goalRoom, creator.getId()); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(2L, GoalRoomRole.FOLLOWER, LocalDateTime.now(), + goalRoom, follower.getId()); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + + final CheckFeed checkFeed1 = 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, + goalRoomMember1); + final CheckFeed checkFeed2 = 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, + goalRoomMember1); + final CheckFeed checkFeed3 = 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, + goalRoomMember2); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember1)); + when(goalRoomMemberRepository.findById(1L)) + .thenReturn(Optional.of(goalRoomMember1)); + when(goalRoomMemberRepository.findById(2L)) + .thenReturn(Optional.of(goalRoomMember2)); + when(memberRepository.findWithMemberProfileAndImageById(goalRoomMember1.getMemberId())) + .thenReturn(Optional.of(creator)); + when(memberRepository.findWithMemberProfileAndImageById(goalRoomMember2.getMemberId())) + .thenReturn(Optional.of(follower)); + when(checkFeedRepository.findByGoalRoomRoadmapNodeIdOrderByCreatedAtDesc(any())) + .thenReturn(List.of(checkFeed3, checkFeed2, checkFeed1)); + when(fileService.generateUrl(anyString(), any())) + .thenReturn(new URL("http://example.com/serverFilePath")); + + // when + final List responses = goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", + 1L); + + // then + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(1L, "코끼리1", "http://example.com/serverFilePath"), + new CheckFeedResponse(1L, "http://example.com/serverFilePath", "description1", LocalDate.now())); + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(1L, "코끼리1", "http://example.com/serverFilePath"), + new CheckFeedResponse(2L, "http://example.com/serverFilePath", "description2", LocalDate.now())); + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse3 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(2L, "코끼리2", "http://example.com/serverFilePath"), + new CheckFeedResponse(3L, "http://example.com/serverFilePath", "description3", LocalDate.now())); + final List expected = List.of(goalRoomCheckFeedResponse3, + goalRoomCheckFeedResponse2, goalRoomCheckFeedResponse1); + + assertThat(responses).usingRecursiveComparison() + .ignoringFields("checkFeed.id", "checkFeed.createdAt") + .isEqualTo(expected); + } + + @Test + void 모집중인_골룸의_인증피드를_조회시_빈_값을_반환한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri1", "password1!", "코끼리1", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember)); + + // when + final List responses = goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", + 1L); + + // then + final List expected = Collections.emptyList(); + + assertThat(responses).isEqualTo(expected); + } + + @Test + void 종료된_골룸의_인증피드를_전체_조회시_모든_기간의_인증피드를_대상으로_반환한다() throws MalformedURLException { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri1", "password1!", "코끼리1", "kirikiri1@email"); + final Member follower = 사용자를_생성한다(2L, "cokirikiri2", "password2!", "코끼리2", "kirikiri2@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + goalRoom.complete(); + + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(1L, GoalRoomRole.LEADER, LocalDateTime.now(), + goalRoom, + creator.getId()); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(2L, GoalRoomRole.FOLLOWER, LocalDateTime.now(), + goalRoom, + follower.getId()); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + + final CheckFeed checkFeed1 = 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, + goalRoomMember1); + final CheckFeed checkFeed2 = 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, + goalRoomMember1); + final CheckFeed checkFeed3 = 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, + goalRoomMember2); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember1)); + when(goalRoomMemberRepository.findById(1L)) + .thenReturn(Optional.of(goalRoomMember1)); + when(goalRoomMemberRepository.findById(2L)) + .thenReturn(Optional.of(goalRoomMember2)); + when(memberRepository.findWithMemberProfileAndImageById(goalRoomMember1.getMemberId())) + .thenReturn(Optional.of(creator)); + when(memberRepository.findWithMemberProfileAndImageById(goalRoomMember2.getMemberId())) + .thenReturn(Optional.of(follower)); + given(checkFeedRepository.findByGoalRoomIdOrderByCreatedAtDesc(any())) + .willReturn(List.of(checkFeed3, checkFeed2, checkFeed1)); + given(fileService.generateUrl(anyString(), any())) + .willReturn(new URL("http://example.com/serverFilePath")); + + // when + final List responses = goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", + 1L); + + // then + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(1L, "코끼리1", "http://example.com/serverFilePath"), + new CheckFeedResponse(1L, "http://example.com/serverFilePath", "description1", LocalDate.now())); + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(1L, "코끼리1", "http://example.com/serverFilePath"), + new CheckFeedResponse(2L, "http://example.com/serverFilePath", "description2", LocalDate.now())); + final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse3 = new GoalRoomCheckFeedResponse( + new CheckFeedMemberResponse(2L, "코끼리2", "http://example.com/serverFilePath"), + new CheckFeedResponse(3L, "http://example.com/serverFilePath", "description3", LocalDate.now())); + final List expected = List.of(goalRoomCheckFeedResponse3, + goalRoomCheckFeedResponse2, goalRoomCheckFeedResponse1); + + assertThat(responses).usingRecursiveComparison() + .ignoringFields("checkFeed.id", "checkFeed.createdAt") + .isEqualTo(expected); + } + + @Test + void 골룸의_인증피드를_전체_조회시_현재_진행중인_노드가_없으면_빈_리스트를_반환한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri1", "password1!", "코끼리1", "kirikiri1@email"); + final Member follower = 사용자를_생성한다(2L, "cokirikiri2", "password2!", "코끼리2", "kirikiri2@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 진행중인_노드가_없는_골룸을_생성한다(creator, roadmapContent); + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, + follower.getId()); + final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); + + 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, goalRoomMember1); + 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, goalRoomMember1); + 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, goalRoomMember2); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember1)); + + // when + final List responses = goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", + 1L); + + // then + assertThat(responses).isEmpty(); + } + + @Test + void 골룸의_인증피드를_전체_조회할_때_존재하지_않는_골룸이면_예외가_발생한다() { + // given + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")); + + // when + // then + assertThatThrownBy(() -> goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", 1L)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸의_인증피드를_전체_조회할_때_골룸에_참여하지_않은_회원이면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri1", "password1!", "코끼리1", "kirikiri1@email"); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 20); + + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomRepository.findByIdWithNodes(anyLong())) + .thenThrow(new ForbiddenException("골룸에 참여하지 않은 회원입니다.")); + + // when + // then + assertThatThrownBy(() -> goalRoomCheckFeedService.findGoalRoomCheckFeeds("cokirikiri", 1L)) + .isInstanceOf(ForbiddenException.class); + } + + private Member 사용자를_생성한다(final Long memberId, final String identifier, final String password, final String nickname, + final String email) { + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); + final MemberImage memberImage = new MemberImage("orifinalFileName", "serveFilePath", ImageContentType.PNG); + return new Member(memberId, new Identifier(identifier), null, new EncryptedPassword(new Password(password)), + new Nickname(nickname), memberImage, memberProfile); + } + + private Roadmap 로드맵을_생성한다(final Member creator) { + final RoadmapCategory category = new RoadmapCategory("게임"); + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), + category, null); + return roadmap; + } + + private List 로드맵_노드들을_생성한다() { + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); + roadmapNode1.addImages(new RoadmapNodeImages(Collections.emptyList())); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); + return List.of(roadmapNode1, roadmapNode2); + } + + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + final RoadmapContent roadmapContent = new RoadmapContent(1L, "로드맵 본문", roadmapId, + new RoadmapNodes(roadmapNodes)); + return roadmapContent; + } + + private List 노드_이미지들을_생성한다() { + return List.of( + new RoadmapNodeImage("node-image1.png", "node-image1-save-path", ImageContentType.PNG), + new RoadmapNodeImage("node-image2.png", "node-image2-save-path", ImageContentType.PNG) + ); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final RoadmapContent roadmapContent, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), 골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); + } + + private GoalRoom 시작_날짜가_미래인_골룸을_생성한다(final Long goalRoomId, final Member creator, + final RoadmapContent roadmapContent, final Integer limitedMemberCount) { + final GoalRoomRoadmapNode goalRoomRoadmapNode = new GoalRoomRoadmapNode( + new Period(TEN_DAY_LATER, TWENTY_DAY_LATER), 5, roadmapContent.getNodes().getValues().get(0).getId()); + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode))); + } + + private GoalRoom 진행중인_노드가_없는_골룸을_생성한다(final Member member, final RoadmapContent roadmapContent) { + final List roadmapNodes = roadmapContent.getNodes().getValues(); + + final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); + final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( + 1L, new Period(TEN_DAY_LATER, TWENTY_DAY_LATER), 10, firstRoadmapNode.getId()); + + final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); + final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( + 2L, new Period(THIRTY_DAY_LATER, THIRTY_DAY_LATER.plusDays(10)), 2, secondRoadmapNode.getId()); + + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( + List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); + + return new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), + roadmapContent.getId(), goalRoomRoadmapNodes); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다(final RoadmapNodes roadmapNodes) { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0).getId()), + new GoalRoomRoadmapNode(new Period(TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER), 5, + roadmapNodes.getValues().get(1).getId())) + ); + } + + private CheckFeedRequest 인증_피드_요청_DTO를_생성한다(final String contentType) { + return new CheckFeedRequest( + new MockMultipartFile("image", "originalFileName.jpeg", contentType, + "test image".getBytes()), "인증 피드 설명"); + } + + private CheckFeed 인증_피드를_생성한다(final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember joinedMember) { + return new CheckFeed("src/test/resources/testImage/originalFileName.jpeg", ImageContentType.JPEG, + "originalFileName.jpeg", "인증 피드 설명", goalRoomRoadmapNode.getId(), joinedMember.getId()); + } + + private CheckFeed 인증피드를_생성한다(final String serverFilePath, final String description, + final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember goalRoomMember) { + return new CheckFeed(serverFilePath, ImageContentType.PNG, "fileName", description, goalRoomRoadmapNode.getId(), + goalRoomMember.getId(), LocalDateTime.now()); + } + + private URL makeUrl(final String path) { + try { + return new URL("http://example.com/" + path); + } catch (final MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/ControllerTestHelper.java b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/ControllerTestHelper.java similarity index 85% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/helper/ControllerTestHelper.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/helper/ControllerTestHelper.java index 2a4d1e746..1555732bb 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/ControllerTestHelper.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/ControllerTestHelper.java @@ -1,14 +1,15 @@ -package co.kirikiri.controller.helper; +package co.kirikiri.common.helper; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import co.kirikiri.common.helper.FieldDescriptionHelper.FieldDescription; import co.kirikiri.common.interceptor.AuthInterceptor; import co.kirikiri.common.resolver.MemberIdentifierArgumentResolver; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.restdocs.payload.FieldDescriptor; -import java.util.List; public class ControllerTestHelper extends RestDocsHelper { @@ -31,8 +32,7 @@ void setUp() throws Exception { .thenReturn(true); } - protected List makeFieldDescriptor( - final List descriptions) { + protected List makeFieldDescriptor(final List descriptions) { return descriptions.stream() .map(FieldDescriptionHelper::getDescriptor) .toList(); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/FieldDescriptionHelper.java b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/FieldDescriptionHelper.java similarity index 93% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/helper/FieldDescriptionHelper.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/helper/FieldDescriptionHelper.java index e7cdaeb4f..6f5ba543f 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/FieldDescriptionHelper.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/FieldDescriptionHelper.java @@ -1,6 +1,6 @@ -package co.kirikiri.controller.helper; +package co.kirikiri.common.helper; -import static co.kirikiri.controller.helper.RestDocsHelper.RESTRICT; +import static co.kirikiri.common.helper.RestDocsHelper.RESTRICT; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import org.springframework.restdocs.payload.FieldDescriptor; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/helper/RepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/RepositoryTest.java similarity index 93% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/helper/RepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/helper/RepositoryTest.java index fc8ae1932..b6c2b84a6 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/helper/RepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/RepositoryTest.java @@ -1,4 +1,4 @@ -package co.kirikiri.persistence.helper; +package co.kirikiri.common.helper; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.ActiveProfiles; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/RestDocsHelper.java b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/RestDocsHelper.java similarity index 98% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/helper/RestDocsHelper.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/helper/RestDocsHelper.java index f977002c7..647456cd7 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/RestDocsHelper.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/RestDocsHelper.java @@ -1,10 +1,11 @@ -package co.kirikiri.controller.helper; +package co.kirikiri.common.helper; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.UnsupportedEncodingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -21,7 +22,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.filter.CharacterEncodingFilter; -import java.io.UnsupportedEncodingException; @ActiveProfiles("test") @Import(TestObjectMapperConfig.class) diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/TestObjectMapperConfig.java b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/TestObjectMapperConfig.java similarity index 91% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/helper/TestObjectMapperConfig.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/helper/TestObjectMapperConfig.java index 1d0fb9f35..681c83655 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/helper/TestObjectMapperConfig.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/helper/TestObjectMapperConfig.java @@ -1,4 +1,4 @@ -package co.kirikiri.controller.helper; +package co.kirikiri.common.helper; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/service/UUIDFilePathGeneratorTest.java b/backend/kirikiri/src/test/java/co/kirikiri/common/service/UUIDFilePathGeneratorTest.java similarity index 96% rename from backend/kirikiri/src/test/java/co/kirikiri/service/UUIDFilePathGeneratorTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/common/service/UUIDFilePathGeneratorTest.java index 44e2319f6..03edc31cd 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/service/UUIDFilePathGeneratorTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/common/service/UUIDFilePathGeneratorTest.java @@ -1,10 +1,9 @@ -package co.kirikiri.service; +package co.kirikiri.common.service; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertTrue; import co.kirikiri.common.exception.BadRequestException; -import co.kirikiri.common.service.FilePathGenerator; import co.kirikiri.common.type.ImageDirType; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMemberTest.java b/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMemberTest.java deleted file mode 100644 index 70d3dd296..000000000 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMemberTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import static org.assertj.core.api.Assertions.assertThat; - -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Gender; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.MemberProfile; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.roadmap.domain.RoadmapContent; -import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; - -class GoalRoomPendingMemberTest { - - @Test - void 골룸의_리더이면_True를_반환한다() { - // given - final Member member = new Member(new Identifier("identifier"), new EncryptedPassword(new Password("password1")), - new Nickname("nickname"), null, - new MemberProfile(Gender.FEMALE, "kirikiri1@email.com")); - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), - new RoadmapContent("content", null, null), member); - - // when - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, - member); - - // then - assertThat(goalRoomPendingMember.isLeader()).isTrue(); - } - - @Test - void 골룸의_리더가_아니면_false를_반환한다() { - // given - final Member member = new Member(new Identifier("identifier"), new EncryptedPassword(new Password("password1")), - new Nickname("nickname"), null, - new MemberProfile(Gender.FEMALE, "kirikiri1@email.com")); - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), - new RoadmapContent("content", 1L, null), member); - - // when - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, - member); - - // then - assertThat(goalRoomPendingMember.isLeader()).isFalse(); - } - - @Test - void 입력받은_멤버가_자신과_같은_멤버이면_true를_반환한다() { - // given - final Member member = new Member(new Identifier("identifier"), - new EncryptedPassword(new Password("password1!")), - new Nickname("name"), null, null); - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, - member); - - // when - final boolean result = goalRoomPendingMember.isSameMember(member); - - // then - assertThat(result).isTrue(); - } - - @Test - void 입력받은_멤버가_자신과_다른_멤버이면_false를_반환한다() { - // given - final Member member1 = new Member(1L, new Identifier("identifier1"), - null, new EncryptedPassword(new Password("password1!")), - new Nickname("name1"), null, null); - final Member member2 = new Member(2L, new Identifier("identifier2"), - null, new EncryptedPassword(new Password("password2!")), - new Nickname("name2"), null, null); - - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, - member1); - - // when - final boolean result = goalRoomPendingMember.isSameMember(member2); - - // then - assertThat(result).isFalse(); - } - - @Test - void 팔로워가_리더로_변경된다() { - // given - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), null, - null); - - // when - goalRoomPendingMember.becomeLeader(); - - // then - assertThat(goalRoomPendingMember.isLeader()).isTrue(); - } -} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembersTest.java b/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembersTest.java deleted file mode 100644 index b232582d6..000000000 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomPendingMembersTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import co.kirikiri.common.exception.domain.UnexpectedDomainException; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.roadmap.domain.RoadmapContent; -import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; -import java.util.List; - -class GoalRoomPendingMembersTest { - - private static final Member MEMBER1 = new Member(1L, new Identifier("identifier1"), - null, new EncryptedPassword(new Password("password1!")), - new Nickname("name1"), null, null); - private static final Member MEMBER2 = new Member(2L, new Identifier("identifier2"), - null, new EncryptedPassword(new Password("password2!")), - new Nickname("name2"), null, null); - - @Test - void 골룸의_리더를_찾는다() { - // given - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), - new RoadmapContent("content", 1L, null), MEMBER1); - - // when - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(List.of( - new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, MEMBER2), - new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, MEMBER1) - )); - - // then - assertThat(goalRoomPendingMembers.findGoalRoomLeader()).isEqualTo(MEMBER2); - } - - @Test - void 골룸의_리더가_없으면_예외가_발생한다() { - // given - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), - new RoadmapContent("content", 1L, null), MEMBER1); - - // when - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(List.of( - new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, MEMBER1), - new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, MEMBER2) - )); - - // then - assertThatThrownBy(() -> assertThat(goalRoomPendingMembers.findGoalRoomLeader())) - .isInstanceOf(UnexpectedDomainException.class); - } - - @Test - void 입력받은_사용자를_골룸_사용자_중에서_찾는다() { - // given - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, MEMBER1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), null, MEMBER2); - - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( - List.of(goalRoomPendingMember1, goalRoomPendingMember2)); - - // when - final GoalRoomPendingMember findGoalRoomPendingMember = goalRoomPendingMembers.findByMember(MEMBER1).get(); - - // then - assertThat(findGoalRoomPendingMember).isEqualTo(goalRoomPendingMember1); - } - - @Test - void 다음_리더가_될_사용자를_찾는다() { - // given - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, - MEMBER1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), null, - MEMBER2); - - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( - List.of(goalRoomPendingMember1, goalRoomPendingMember2)); - - // when - final GoalRoomPendingMember nextLeader = goalRoomPendingMembers.findNextLeader().get(); - - // then - assertThat(nextLeader).isEqualTo(goalRoomPendingMember2); - } - - @Test - void 골룸_사용자의_수를_구한다() { - // given - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, - MEMBER1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), null, - MEMBER2); - - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( - List.of(goalRoomPendingMember1, goalRoomPendingMember2)); - - // when - final int size = goalRoomPendingMembers.size(); - - // then - assertThat(size).isEqualTo(2); - } - - @Test - void 골룸_사용자에서_입렵받은_사용자를_제거한다() { - // given - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), null, - MEMBER1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), null, - MEMBER2); - - final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( - List.of(goalRoomPendingMember1, goalRoomPendingMember2)); - - // when - goalRoomPendingMembers.remove(goalRoomPendingMember1); - - // then - assertThat(goalRoomPendingMembers) - .usingRecursiveComparison() - .isEqualTo(new GoalRoomPendingMembers(List.of(goalRoomPendingMember2))); - } -} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomTest.java b/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomTest.java deleted file mode 100644 index 20d62b87e..000000000 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomTest.java +++ /dev/null @@ -1,274 +0,0 @@ -package co.kirikiri.domain.goalroom; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Gender; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.MemberProfile; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.roadmap.domain.RoadmapContent; -import co.kirikiri.roadmap.domain.RoadmapNode; -import co.kirikiri.roadmap.domain.RoadmapNodeImages; -import co.kirikiri.roadmap.domain.RoadmapNodes; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import java.time.LocalDate; -import java.util.Collections; -import java.util.List; - -class GoalRoomTest { - - private static final GoalRoomName GOAL_ROOM_NAME = new GoalRoomName("골룸 이름"); - private static final LocalDate TODAY = LocalDate.now(); - private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); - private static final LocalDate TWENTY_DAY_LAYER = TODAY.plusDays(20); - private static final LocalDate THIRTY_DAY_LATER = TODAY.plusDays(30); - - private static Member member; - - @BeforeAll - static void setUp() { - final Identifier identifier = new Identifier("identifier1"); - final Password password = new Password("password1!"); - final EncryptedPassword encryptedPassword = new EncryptedPassword(password); - final Nickname nickname = new Nickname("nickname"); - final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); - member = new Member(1L, identifier, null, encryptedPassword, nickname, null, memberProfile); - } - - @Test - void 골룸의_총_기간을_계산한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final RoadmapContent roadmapContent = 로드맵_콘텐츠를_생성한다(); - final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); - - // when - final int totalPeriod = goalRoom.calculateTotalPeriod(); - - // then - assertThat(totalPeriod) - .isSameAs(31); - } - - @Test - void 골룸에_대기중인_인원수를_계산한다() { - // given - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), - new RoadmapContent("content", 1L, null), member); - final Member member1 = new Member(2L, new Identifier("identifier2"), - null, new EncryptedPassword(new Password("password1")), new Nickname("닉네임2"), - null, - new MemberProfile(Gender.FEMALE, "kirikiri1@email.com")); - final Member member2 = new Member(3L, new Identifier("identifier3"), - null, new EncryptedPassword(new Password("password1")), new Nickname("닉네임3"), - null, - new MemberProfile(Gender.FEMALE, "kirikiri1@email.com")); - - // when - goalRoom.join(member1); - goalRoom.join(member2); - - // then - assertThat(goalRoom.getCurrentMemberCount()).isEqualTo(3); - } - - @Test - void 골룸에_사용자를_추가한다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(10), - new RoadmapContent("로드맵 내용", 1L, null), member); - final Member follower = 사용자를_생성한다(2L, "identifier12", "시진이"); - - //when - goalRoom.join(follower); - - //then - final Integer currentMemberCount = goalRoom.getCurrentMemberCount(); - assertThat(currentMemberCount) - .isEqualTo(2); - } - - @Test - void 모집중이_아닌_골룸에_사용자를_추가하면_예외가_발생한다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(10), new RoadmapContent("로드맵 내용", 1L, null), - 사용자를_생성한다(2L, "identifier1", "시진이")); - goalRoom.start(); - - //when, then - assertThatThrownBy(() -> goalRoom.join(member)) - .isInstanceOf(GoalRoomException.class) - .hasMessage("모집 중이지 않은 골룸에는 참여할 수 없습니다."); - } - - @Test - void 제한_인원이_가득_찬_골룸에_사용자를_추가하면_예외가_발생한다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(1), new RoadmapContent("로드맵 내용", 1L, null), - 사용자를_생성한다(2L, "identifier1", "시진이")); - - //when,then - assertThatThrownBy(() -> goalRoom.join(member)) - .isInstanceOf(GoalRoomException.class) - .hasMessage("제한 인원이 꽉 찬 골룸에는 참여할 수 없습니다."); - } - - @Test - void 이미_참여_중인_사용자를_골룸에_추가하면_예외가_발생한다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(2), - new RoadmapContent("로드맵 내용", 1L, null), member); - - //when,then - assertThatThrownBy(() -> goalRoom.join(member)) - .isInstanceOf(GoalRoomException.class) - .hasMessage("이미 참여한 골룸에는 참여할 수 없습니다."); - } - - @Test - void 골룸의_총_인증_횟수를_구한다() { - //given - final Member creator = 크리에이터를_생성한다(); - final RoadmapContent roadmapContent = 로드맵_콘텐츠를_생성한다(); - final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); - - //expect - assertThat(goalRoom.getAllCheckCount()).isEqualTo(20); - } - - @Test - void 골룸이_시작하기_전에_참여_멤버를_확인한다() { - //given - final Member creator = 크리에이터를_생성한다(); - final RoadmapContent roadmapContent = 로드맵_콘텐츠를_생성한다(); - final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); - - final Member 참여자 = 사용자를_생성한다(2L, "identifier1", "팔로워"); - goalRoom.join(참여자); - - //expect - assertAll( - () -> assertThat(goalRoom.isGoalRoomMember(참여자)).isTrue(), - () -> assertThat(goalRoom.getCurrentMemberCount()).isEqualTo(2) - ); - } - - @Test - void 골룸이_시작한_후에_참여_멤버를_확인한다() { - //given - final Member creator = 크리에이터를_생성한다(); - final RoadmapContent roadmapContent = 로드맵_콘텐츠를_생성한다(); - final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); - - final Member 참여자 = 사용자를_생성한다(2L, "identifier1", "팔로워"); -// goalRoom.join(참여자); - goalRoom.start(); - - //expect - assertAll( - () -> assertThat(goalRoom.isGoalRoomMember(참여자)).isFalse(), - () -> assertThat(goalRoom.getCurrentMemberCount()).isEqualTo(0) - ); - } - - @Test - void 골룸을_나간다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(2), - new RoadmapContent("로드맵 내용", 1L, null), member); - - // when - goalRoom.leave(member); - - // then - assertThat(goalRoom.isEmptyGoalRoom()).isTrue(); - } - - @Test - void 골룸에_참여하지_않은_멤버가_나가면_예외가_발생한다() { - //given - final GoalRoom goalRoom = new GoalRoom(GOAL_ROOM_NAME, new LimitedMemberCount(2), - new RoadmapContent("로드맵 내용", 1L, null), member); - - final Member notJoinMember = new Member(new Identifier("identifier2"), - new EncryptedPassword(new Password("password2!")), - new Nickname("name2"), null, null); - - // when - // then - assertThatThrownBy(() -> goalRoom.leave(notJoinMember)) - .isInstanceOf(GoalRoomException.class); - } - - @Test - void 골룸이_종료된지_3개월_이상_지나지_않으면_false를_반환한다() { - //given - final Member creator = 크리에이터를_생성한다(); - final RoadmapContent roadmapContent = 로드맵_콘텐츠를_생성한다(); - final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); - - // when - final boolean result = goalRoom.isCompletedAfterMonths(3); - - // then - assertThat(result).isEqualTo(false); - } - - private Member 크리에이터를_생성한다() { - final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); - return new Member(new Identifier("cokirikiri"), - new EncryptedPassword(new Password("password1!")), new Nickname("코끼리"), null, memberProfile); - } - - private Member 사용자를_생성한다(final Long id, final String identifier, final String nickname) { - final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); - return new Member(id, new Identifier(identifier), null, new EncryptedPassword(new Password("password1!")), - new Nickname(nickname), null, memberProfile); - } - - private RoadmapContent 로드맵_콘텐츠를_생성한다() { - final List roadmapNodes = 로드맵_노드들을_생성한다(); - return 로드맵_본문을_생성한다(roadmapNodes); - } - - private List 로드맵_노드들을_생성한다() { - final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); - roadmapNode1.addImages(new RoadmapNodeImages(Collections.emptyList())); - final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); - return List.of(roadmapNode1, roadmapNode2); - } - - private RoadmapContent 로드맵_본문을_생성한다(final List roadmapNodes) { - final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", 1L, new RoadmapNodes(roadmapNodes)); - return roadmapContent; - } - - private GoalRoom 골룸을_생성한다(final RoadmapContent roadmapContent, final Member creator) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), - new LimitedMemberCount(10), roadmapContent, creator); - final List roadmapNodes = roadmapContent.getNodes().getValues(); - - final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); - final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode); - - final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); - final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), 10, secondRoadmapNode); - - final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( - List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoom; - } -} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomCreateApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomCreateApiTest.java similarity index 54% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomCreateApiTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomCreateApiTest.java index 0c6c70681..3e18c2aa0 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomCreateApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomCreateApiTest.java @@ -1,41 +1,46 @@ -package co.kirikiri.controller; +package co.kirikiri.goalroom.controller; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; -import static org.springframework.restdocs.headers.HeaderDocumentation.*; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import co.kirikiri.common.exception.BadRequestException; import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.controller.helper.ControllerTestHelper; -import co.kirikiri.controller.helper.FieldDescriptionHelper.FieldDescription; -import co.kirikiri.service.dto.ErrorResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.goalroom.GoalRoomCreateService; -import co.kirikiri.service.goalroom.GoalRoomReadService; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.helper.FieldDescriptionHelper.FieldDescription; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.service.GoalRoomCreateService; +import co.kirikiri.goalroom.service.GoalRoomReadService; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; import com.fasterxml.jackson.core.type.TypeReference; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; @WebMvcTest(GoalRoomController.class) class GoalRoomCreateApiTest extends ControllerTestHelper { @@ -382,511 +387,7 @@ class GoalRoomCreateApiTest extends ControllerTestHelper { pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), responseFields(fieldWithPath("message").description("예외 메세지")))); } - - @Test - void 정상적으로_골룸에_투두리스트를_추가한다() throws Exception { - //given - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - given(goalRoomCreateService.addGoalRoomTodo(anyLong(), anyString(), any())) - .willReturn(1L); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isCreated()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰")), - responseHeaders(headerWithName(HttpHeaders.LOCATION).description("골룸 투두 단일 조회 api 경로")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")))) - .andReturn(); - - //then - assertThat(mvcResult.getResponse().getHeader(HttpHeaders.LOCATION)).isEqualTo( - API_PREFIX + "/goal-rooms/1/todos/1"); - } - - @Test - void 골룸_투두_추가시_존재하지_않는_회원일_경우() throws Exception { - //given - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - doThrow(new NotFoundException("존재하지 않는 회원입니다.")) - .when(goalRoomCreateService) - .addGoalRoomTodo(anyLong(), anyString(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("존재하지 않는 회원입니다.")); - } - - @Test - void 골룸_투두_추가시_존재하지_않는_골룸일_경우() throws Exception { - //given - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) - .when(goalRoomCreateService) - .addGoalRoomTodo(anyLong(), anyString(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); - } - - @Test - void 골룸_투두_추가시_이미_종료된_골룸일_경우() throws Exception { - //given - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - doThrow(new BadRequestException("이미 종료된 골룸입니다.")) - .when(goalRoomCreateService) - .addGoalRoomTodo(anyLong(), anyString(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isBadRequest()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("이미 종료된 골룸입니다.")); - } - - @Test - void 골룸_투두_추가시_리더가_아닌_경우() throws Exception { - //given - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - doThrow(new BadRequestException("골룸의 리더만 투두리스트를 추가할 수 있습니다.")) - .when(goalRoomCreateService) - .addGoalRoomTodo(anyLong(), anyString(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isBadRequest()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("골룸의 리더만 투두리스트를 추가할 수 있습니다.")); - } - - @Test - void 골룸_투두_추가시_컨텐츠가_250글자가_넘을_경우() throws Exception { - //given - final String content = "a".repeat(251); - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest(content, TODAY, TEN_DAY_LATER); - final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); - doThrow(new BadRequestException("투두 컨텐츠의 길이가 적절하지 않습니다.")) - .when(goalRoomCreateService) - .addGoalRoomTodo(anyLong(), anyString(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .content(jsonRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isBadRequest()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("투두 컨텐츠의 길이가 적절하지 않습니다.")); - } - - @Test - void 골룸_투두리스트에_대해_체크한다() throws Exception { - // given - final GoalRoomToDoCheckResponse expected = new GoalRoomToDoCheckResponse(true); - when(goalRoomCreateService.checkGoalRoomTodo(anyLong(), anyLong(), anyString())) - .thenReturn(expected); - - // when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isOk()) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디"), - parameterWithName("todoId").description("골룸 투두 아이디")), - responseFields( - fieldWithPath("isChecked").description( - "투두 체크 현황 (true: 체크됨, false: 체크되지 않음)")))) - .andReturn(); - - // then - final GoalRoomToDoCheckResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(response) - .isEqualTo(expected); - } - - @Test - void 골룸_투두리스트_체크시_체크_이력이_있으면_제거한다() throws Exception { - // given - final GoalRoomToDoCheckResponse expected = new GoalRoomToDoCheckResponse(false); - when(goalRoomCreateService.checkGoalRoomTodo(anyLong(), anyLong(), anyString())) - .thenReturn(expected); - - // when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isOk()) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디"), - parameterWithName("todoId").description("골룸 투두 아이디")), - responseFields( - fieldWithPath("isChecked").description( - "투두 체크 현황 (true: 체크됨, false: 체크되지 않음)")))) - .andReturn(); - - // then - final GoalRoomToDoCheckResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(response) - .isEqualTo(expected); - } - - @Test - void 골룸_투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() throws Exception { - //given - doThrow(new NotFoundException("골룸이 존재하지 않습니다. goalRoomId = 1")) - .when(goalRoomCreateService) - .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디"), - parameterWithName("todoId").description("골룸 투두 아이디")), - responseFields(fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response).isEqualTo(new ErrorResponse("골룸이 존재하지 않습니다. goalRoomId = 1")); - } - - @Test - void 골룸_투두리스트_체크시_해당_투두가_존재하지_않으면_예외가_발생한다() throws Exception { - //given - doThrow(new NotFoundException("존재하지 않는 투두입니다. todoId = 1")) - .when(goalRoomCreateService) - .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디"), - parameterWithName("todoId").description("골룸 투두 아이디")), - responseFields( - fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response) - .isEqualTo(new ErrorResponse("존재하지 않는 투두입니다. todoId = 1")); - } - - @Test - void 골룸_투두리스트_체크시_사용자가_없으면_예외가_발생한다() throws Exception { - //given - doThrow(new NotFoundException("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri")) - .when(goalRoomCreateService) - .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); - - //when - final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") - .contentType(MediaType.APPLICATION_JSON) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo(print()) - .andDo(documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰")), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디"), - parameterWithName("todoId").description("골룸 투두 아이디")), - responseFields( - fieldWithPath("message").description("예외 메세지")))) - .andReturn(); - - //then - final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { - }); - assertThat(response) - .isEqualTo(new ErrorResponse("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri")); - } - - @Test - void 인증_피드_등록_요청을_보낸다() throws Exception { - //given - final String imageName = "image"; - final String originalImageName = "originalImageName.jpeg"; - final String contentType = "image/jpeg"; - final String image = "테스트 이미지"; - final String description = "이미지 설명"; - final String filePath = "path/to/directories/" + contentType; - final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, - contentType, image.getBytes()); - - given(goalRoomCreateService.createCheckFeed(anyString(), anyLong(), any())) - .willReturn(filePath); - - //expect - mockMvc.perform( - RestDocumentationRequestBuilders - .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .file(imageFile) - .file("text", description.getBytes()) - .header("Authorization", "Bearer accessToken") - .contextPath(API_PREFIX) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) - .andExpect(status().isCreated()) - .andExpect(header().string("Location", filePath)) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName("Authorization").description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - requestParts( - partWithName("image").description("업로드한 이미지"), - partWithName("text").description("인증 피드 본문") - ), - responseHeaders( - headerWithName("Location").description("저장된 이미지 경로") - ))); - } - - @Test - void 인증_피드_등록시_노드_기간에_해당하지_않으면_예외가_발생한다() throws Exception { - // given - final String imageName = "image"; - final String originalImageName = "originalImageName.jpeg"; - final String contentType = "image/jpeg"; - final String image = "테스트 이미지"; - final String description = "이미지 설명"; - final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, - contentType, image.getBytes()); - - doThrow(new BadRequestException("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")) - .when(goalRoomCreateService) - .createCheckFeed(anyString(), anyLong(), any()); - - //when - mockMvc.perform( - RestDocumentationRequestBuilders - .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .file(imageFile) - .param("description", description) - .header("Authorization", "Bearer accessToken") - .contextPath(API_PREFIX) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value("인증 피드는 노드 기간 내에만 작성할 수 있습니다.")) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName("Authorization").description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - requestParts( - partWithName("image").description("업로드한 이미지") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))); - } - - @Test - void 인증_피드_등록_요청시_멤버가_존재하지_않을_경우_예외를_반환한다() throws Exception { - //given - final String imageName = "image"; - final String originalImageName = "originalImageName.jpeg"; - final String contentType = "image/jpeg"; - final String image = "테스트 이미지"; - final String description = "이미지 설명"; - final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, - contentType, image.getBytes()); - - doThrow(new NotFoundException("존재하지 않는 회원입니다.")) - .when(goalRoomCreateService) - .createCheckFeed(anyString(), anyLong(), any()); - - //when - mockMvc.perform( - RestDocumentationRequestBuilders - .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .file(imageFile) - .file("text", description.getBytes()) - .header("Authorization", "Bearer accessToken") - .contextPath(API_PREFIX) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.message").value("존재하지 않는 회원입니다.")) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName("Authorization").description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - requestParts( - partWithName("image").description("업로드한 이미지"), - partWithName("text").description("인증 피드 본문") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))); - } - - @Test - void 인증_피드_등록_요청시_로드맵이_존재하지_않을_경우_예외를_반환한다() throws Exception { - //given - final String imageName = "image"; - final String originalImageName = "originalImageName.jpeg"; - final String contentType = "image/jpeg"; - final String image = "테스트 이미지"; - final String description = "이미지 설명"; - final MockMultipartFile imageFile = new MockMultipartFile(imageName, originalImageName, - contentType, image.getBytes()); - - doThrow(new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = 1L")) - .when(goalRoomCreateService) - .createCheckFeed(anyString(), anyLong(), any()); - - //when - mockMvc.perform( - RestDocumentationRequestBuilders - .multipart(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .file(imageFile) - .file("text", description.getBytes()) - .header("Authorization", "Bearer accessToken") - .contextPath(API_PREFIX) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.message").value("골룸 정보가 존재하지 않습니다. goalRoomId = 1L")) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName("Authorization").description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - requestParts( - partWithName("image").description("업로드한 이미지"), - partWithName("text").description("인증 피드 본문") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))); - } - + @Test void 정상적으로_골룸을_나간다() throws Exception { // given @@ -1199,12 +700,4 @@ private List makeCreateGoalRoomSuccessRequestFieldDescription( new FieldDescription("goalRoomRoadmapNodeRequests[].endDate", "골룸 노드의 종료일", "- yyyyMMdd 형식") ); } - - private List makeAddTodoSuccessRequestFieldDescription() { - return List.of( - new FieldDescription("content", "골룸 투두 컨텐츠", "- 길이 : 1 ~ 250"), - new FieldDescription("startDate", "골룸 투두 시작일", "- yyyyMMdd 형식"), - new FieldDescription("endDate", "골룸 투두 종료일", "- yyyyMMdd 형식") - ); - } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomReadApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomReadApiTest.java similarity index 70% rename from backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomReadApiTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomReadApiTest.java index 62f877ab6..4571b9f0d 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/controller/GoalRoomReadApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/controller/GoalRoomReadApiTest.java @@ -1,6 +1,6 @@ -package co.kirikiri.controller; +package co.kirikiri.goalroom.controller; -import static co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto.ACCOMPLISHMENT_RATE; +import static co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto.ACCOMPLISHMENT_RATE; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -12,40 +12,40 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import co.kirikiri.common.exception.BadRequestException; import co.kirikiri.common.exception.ForbiddenException; import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.controller.helper.ControllerTestHelper; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.member.service.dto.response.MemberResponse; -import co.kirikiri.service.dto.ErrorResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomStatusTypeRequest; -import co.kirikiri.service.dto.goalroom.response.CheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodesResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import co.kirikiri.service.goalroom.GoalRoomCreateService; -import co.kirikiri.service.goalroom.GoalRoomReadService; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.service.GoalRoomCreateService; +import co.kirikiri.goalroom.service.GoalRoomReadService; +import co.kirikiri.goalroom.service.dto.request.GoalRoomStatusTypeRequest; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoCheckResponse; +import co.kirikiri.goalroom.service.dto.response.DashBoardToDoResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodesResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.MemberResponse; import com.fasterxml.jackson.core.type.TypeReference; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MvcResult; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; @WebMvcTest(GoalRoomController.class) class GoalRoomReadApiTest extends ControllerTestHelper { @@ -428,108 +428,6 @@ class GoalRoomReadApiTest extends ControllerTestHelper { assertThat(responses).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); } - @Test - void 골룸의_투두리스트를_조회한다() throws Exception { - // given - final LocalDate today = LocalDate.now(); - final List goalRoomTodoResponses = List.of( - new co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse(1L, "투두 1", today, today.plusDays(10), new co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse(true)), - new co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse(2L, "투두 2", today.plusDays(20), today.plusDays(30), - new co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse(false))); - - when(goalRoomReadService.findAllGoalRoomTodo(any(), any())) - .thenReturn(goalRoomTodoResponses); - - // when - final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isOk()) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("[0].id").description("투두 아이디"), - fieldWithPath("[0].content").description("투두 내용"), - fieldWithPath("[0].startDate").description("투두 시작 날짜"), - fieldWithPath("[0].endDate").description("투두 종료 날짜"), - fieldWithPath("[0].check.isChecked").description("투두 체크 여부") - ))) - .andReturn(); - - // then - final List response = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(response) - .isEqualTo(goalRoomTodoResponses); - } - - @Test - void 골룸_투두리스트_조회시_존재하지_않은_골룸일_경우() throws Exception { - // given - doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) - .when(goalRoomReadService) - .findAllGoalRoomTodo(any(), any()); - - // when - final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo( - documentationResultHandler.document( - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))) - .andReturn(); - - // then - final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(responses) - .isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); - } - - @Test - void 골룸_투두리스트_조회시_참여하지_않은_사용자일_경우() throws Exception { - // given - doThrow(new ForbiddenException("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier")) - .when(goalRoomReadService) - .findAllGoalRoomTodo(any(), any()); - - // when - final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isForbidden()) - .andDo( - documentationResultHandler.document( - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))) - .andReturn(); - - // then - final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(responses) - .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier")); - } - @Test void 골룸의_노드를_조회한다() throws Exception { // given @@ -635,127 +533,20 @@ class GoalRoomReadApiTest extends ControllerTestHelper { .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier")); } - @Test - void 골룸의_인증피드를_전체_조회한다() throws Exception { - // given - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( - new MemberResponse(1L, "name1", "imageUrl"), - new co.kirikiri.service.dto.goalroom.response.CheckFeedResponse(1L, "imageUrl", "image description1", LocalDate.now())); - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( - new MemberResponse(2L, "name2", "imageUrl"), - new co.kirikiri.service.dto.goalroom.response.CheckFeedResponse(2L, "imageUrl", "image description2", LocalDate.now())); - - final List expected = List.of(goalRoomCheckFeedResponse2, - goalRoomCheckFeedResponse1); - - when(goalRoomReadService.findGoalRoomCheckFeeds(any(), any())) - .thenReturn(expected); - - // when - final String response = mockMvc.perform( - get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isOk()) - .andDo( - documentationResultHandler.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("액세스 토큰") - ), - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("[0].member.id").description("사용자 ID"), - fieldWithPath("[0].member.name").description("사용자 닉네임"), - fieldWithPath("[0].member.imageUrl").description("사용자 이미지 Url"), - fieldWithPath("[0].checkFeed.id").description("인증 피드 ID"), - fieldWithPath("[0].checkFeed.imageUrl").description("인증 피드 이미지 Url"), - fieldWithPath("[0].checkFeed.description").description("인증 피드 설명"), - fieldWithPath("[0].checkFeed.createdAt").description("인증 피드 등록 날짜")))) - .andReturn().getResponse() - .getContentAsString(); - - // then - final List 골룸_인증피드_전체_조회_응답 = objectMapper.readValue(response, - new TypeReference<>() { - }); - assertThat(골룸_인증피드_전체_조회_응답) - .isEqualTo(expected); - } - - @Test - void 골룸_인증피드_전체_조회_시_존재하지_않는_골룸일_경우_예외가_발생한다() throws Exception { - //given - doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) - .when(goalRoomReadService) - .findGoalRoomCheckFeeds(any(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isNotFound()) - .andDo( - documentationResultHandler.document( - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))) - .andReturn(); - - // then - final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(responses).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); - } - - @Test - void 골룸_인증피드_전체_조회_시_골룸에_참여하지_않은_사용자일_경우_예외_발생() throws Exception { - //given - doThrow(new BadRequestException("골룸에 참여하지 않은 회원입니다.")) - .when(goalRoomReadService) - .findGoalRoomCheckFeeds(any(), any()); - - //when - final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/checkFeeds", 1L) - .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) - .contextPath(API_PREFIX)) - .andExpect(status().isBadRequest()) - .andDo( - documentationResultHandler.document( - pathParameters( - parameterWithName("goalRoomId").description("골룸 아이디") - ), - responseFields( - fieldWithPath("message").description("예외 메세지") - ))) - .andReturn(); - - // then - final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { - }); - - assertThat(responses).isEqualTo(new ErrorResponse("골룸에 참여하지 않은 회원입니다.")); - } - private GoalRoomResponse 골룸_조회_응답을_생성한다() { - final List goalRoomNodeResponses = List.of( - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", LocalDate.of(2023, 7, 19), + final List goalRoomNodeResponses = List.of( + new GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", LocalDate.of(2023, 7, 19), LocalDate.of(2023, 7, 30), 10), - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", LocalDate.of(2023, 8, 1), + new GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", LocalDate.of(2023, 8, 1), LocalDate.of(2023, 8, 5), 2)); return new GoalRoomResponse("골룸", 1, 10, goalRoomNodeResponses, 17); } private GoalRoomCertifiedResponse 로그인시_골룸_조회_응답을_생성한다(final boolean isJoined) { - final List goalRoomNodeResponses = List.of( - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", LocalDate.of(2023, 7, 19), + final List goalRoomNodeResponses = List.of( + new GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", LocalDate.of(2023, 7, 19), LocalDate.of(2023, 7, 30), 10), - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", LocalDate.of(2023, 8, 1), + new GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", LocalDate.of(2023, 8, 1), LocalDate.of(2023, 8, 5), 2)); return new GoalRoomCertifiedResponse("골룸", 1, 10, goalRoomNodeResponses, 17, isJoined); } @@ -769,13 +560,13 @@ class GoalRoomReadApiTest extends ControllerTestHelper { LocalDate.of(2023, 1, 31), 15), new GoalRoomRoadmapNodeResponse(2L, "두번째 골룸 노드 제목", LocalDate.of(2023, 2, 1), LocalDate.of(2023, 2, 28), 14))), - List.of(new GoalRoomTodoResponse(1L, "첫 번째 할일", + List.of(new DashBoardToDoResponse(1L, "첫 번째 할일", LocalDate.of(2023, 1, 15), LocalDate.of(2023, 1, 31), - new GoalRoomToDoCheckResponse(false))), - List.of(new CheckFeedResponse(1L, "imageUrl1", "인증 피드 설명 1", LocalDate.now()), - new CheckFeedResponse(2L, "imageUrl2", "인증 피드 설명 2", LocalDate.now()), - new CheckFeedResponse(3L, "imageUrl3", "인증 피드 설명 3", LocalDate.now()), - new CheckFeedResponse(4L, "imageUrl4", "인증 피드 설명 4", LocalDate.now()))); + new DashBoardToDoCheckResponse(false))), + List.of(new DashBoardCheckFeedResponse(1L, "imageUrl1", "인증 피드 설명 1", LocalDate.now()), + new DashBoardCheckFeedResponse(2L, "imageUrl2", "인증 피드 설명 2", LocalDate.now()), + new DashBoardCheckFeedResponse(3L, "imageUrl3", "인증 피드 설명 3", LocalDate.now()), + new DashBoardCheckFeedResponse(4L, "imageUrl4", "인증 피드 설명 4", LocalDate.now()))); } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMemberTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMemberTest.java similarity index 60% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMemberTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMemberTest.java index 816bbfb86..76decf520 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMemberTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMemberTest.java @@ -1,14 +1,9 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import org.junit.jupiter.api.Test; class GoalRoomMemberTest { @@ -40,14 +35,10 @@ class GoalRoomMemberTest { @Test void 입력받은_멤버가_자신과_같은_멤버이면_true를_반환한다() { // given - final Member member = new Member(new Identifier("identifier"), - new EncryptedPassword(new Password("password1!")), - new Nickname("name"), null, null); - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - member); + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); // when - final boolean result = goalRoomMember.isSameMember(member); + final boolean result = goalRoomMember.isSameMember(1L); // then assertThat(result).isTrue(); @@ -56,18 +47,10 @@ class GoalRoomMemberTest { @Test void 입력받은_멤버가_자신과_다른_멤버이면_false를_반환한다() { // given - final Member member1 = new Member(1L, new Identifier("identifier1"), - null, new EncryptedPassword(new Password("password1!")), - new Nickname("name1"), null, null); - final Member member2 = new Member(2L, new Identifier("identifier2"), - null, new EncryptedPassword(new Password("password2!")), - new Nickname("name2"), null, null); - - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - member1); + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); // when - final boolean result = goalRoomMember.isSameMember(member2); + final boolean result = goalRoomMember.isSameMember(2L); // then assertThat(result).isFalse(); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMembersTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMembersTest.java similarity index 54% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMembersTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMembersTest.java index db95bf5ce..b98992ec7 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomMembersTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomMembersTest.java @@ -1,92 +1,86 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import org.junit.jupiter.api.Test; import java.time.LocalDateTime; import java.util.List; +import org.junit.jupiter.api.Test; class GoalRoomMembersTest { - private static final Member MEMBER1 = new Member(1L, new Identifier("identifier1"), - null, new EncryptedPassword(new Password("password1!")), - new Nickname("name1"), null, null); - private static final Member MEMBER2 = new Member(2L, new Identifier("identifier2"), - null, new EncryptedPassword(new Password("password2!")), - new Nickname("name2"), null, null); - @Test void 입력받은_사용자를_골룸_사용자_중에서_찾는다() { // given - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - MEMBER1); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, - MEMBER2); + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, 2L); final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(List.of(goalRoomMember1, goalRoomMember2)); // when - final GoalRoomMember findGoalRoomMember = goalRoomMembers.findByMember(MEMBER1).get(); + final GoalRoomMember findGoalRoomMember = goalRoomMembers.findByMemberId(1L).get(); // then assertThat(findGoalRoomMember).isEqualTo(goalRoomMember1); } @Test - void 다음_리더가_될_사용자를_찾는다() { + void 골룸의_멤버를_멤버_아이디로_찾는다() { // given - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - MEMBER1); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, - MEMBER2); + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, 2L); final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(List.of(goalRoomMember1, goalRoomMember2)); // when - final GoalRoomMember nextLeader = goalRoomMembers.findNextLeader().get(); + final GoalRoomMember goalRoomMember = goalRoomMembers.findByMemberId(1L).get(); // then - assertThat(nextLeader).isEqualTo(goalRoomMember2); + assertThat(goalRoomMember).isEqualTo(goalRoomMember1); } @Test - void 골룸_사용자의_수를_구한다() { + void 리더가_떠나면_리더가_바뀐다() { // given - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - MEMBER1); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, - MEMBER2); + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, 2L); final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(List.of(goalRoomMember1, goalRoomMember2)); // when - final int size = goalRoomMembers.size(); + goalRoomMembers.getValues().remove(goalRoomMember1); + goalRoomMembers.changeLeaderIfLeaderLeave(goalRoomMember1); // then - assertThat(size).isEqualTo(2); + assertThat(goalRoomMember2.isLeader()).isTrue(); } @Test - void 골룸_사용자에서_입렵받은_사용자를_제거한다() { + void 다음_리더가_될_사용자를_찾는다() { // given - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, - MEMBER1); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, - MEMBER2); + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, 2L); final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(List.of(goalRoomMember1, goalRoomMember2)); // when - goalRoomMembers.remove(goalRoomMember1); + final GoalRoomMember nextLeader = goalRoomMembers.findNextLeader().get(); // then - assertThat(goalRoomMembers) - .usingRecursiveComparison() - .isEqualTo(new GoalRoomMembers(List.of(goalRoomMember2))); + assertThat(nextLeader).isEqualTo(goalRoomMember2); + } + + @Test + void 골룸_사용자의_수를_구한다() { + // given + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), null, 1L); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), null, 2L); + + final GoalRoomMembers goalRoomMembers = new GoalRoomMembers(List.of(goalRoomMember1, goalRoomMember2)); + + // when + final int size = goalRoomMembers.size(); + + // then + assertThat(size).isEqualTo(2); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMemberTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMemberTest.java new file mode 100644 index 000000000..5e319605f --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMemberTest.java @@ -0,0 +1,87 @@ +package co.kirikiri.goalroom.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; + +class GoalRoomPendingMemberTest { + + @Test + void 골룸의_리더이면_True를_반환한다() { + // given + final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("goalroom"), new LimitedMemberCount(10), 1L, + 골룸_로드맵_노드들을_생성한다()); + + // when + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, null, + goalRoom, 1L); + + // then + assertThat(goalRoomPendingMember.isLeader()).isTrue(); + } + + @Test + void 골룸의_리더가_아니면_false를_반환한다() { + // given + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), 1L, + 골룸_로드맵_노드들을_생성한다()); + + // when + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(2L, GoalRoomRole.FOLLOWER, null, + goalRoom, 2L); + + // then + assertThat(goalRoomPendingMember.isLeader()).isFalse(); + } + + @Test + void 입력받은_멤버가_자신과_같은_멤버이면_true를_반환한다() { + // given + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + + // when + final boolean result = goalRoomPendingMember.isSameMember(1L); + + // then + assertThat(result).isTrue(); + } + + @Test + void 입력받은_멤버가_자신과_다른_멤버이면_false를_반환한다() { + // given + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + + // when + final boolean result = goalRoomPendingMember.isSameMember(2L); + + // then + assertThat(result).isFalse(); + } + + @Test + void 팔로워가_리더로_변경된다() { + // given + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), null, null); + + // when + goalRoomPendingMember.becomeLeader(); + + // then + assertThat(goalRoomPendingMember.isLeader()).isTrue(); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(LocalDate.now(), LocalDate.now().plusDays(5)), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembersTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembersTest.java new file mode 100644 index 000000000..3292cf6b5 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomPendingMembersTest.java @@ -0,0 +1,128 @@ +package co.kirikiri.goalroom.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import co.kirikiri.common.exception.domain.UnexpectedDomainException; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; + +class GoalRoomPendingMembersTest { + + @Test + void 골룸의_리더를_찾는다() { + // given + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), 1L, + 골룸_로드맵_노드들을_생성한다()); + + // when + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(List.of( + new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, null, goalRoom, 1L), + new GoalRoomPendingMember(2L, GoalRoomRole.FOLLOWER, null, goalRoom, 2L) + )); + + // then + assertThat(goalRoomPendingMembers.findGoalRoomLeaderId()).isEqualTo(1L); + } + + @Test + void 골룸의_리더가_없으면_예외가_발생한다() { + // given + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("goalroom"), new LimitedMemberCount(10), 1L, + 골룸_로드맵_노드들을_생성한다()); + + // when + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers(List.of( + new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, null, goalRoom, 1L), + new GoalRoomPendingMember(2L, GoalRoomRole.FOLLOWER, null, goalRoom, 2L) + )); + + // then + assertThatThrownBy(() -> goalRoomPendingMembers.findGoalRoomLeaderId()) + .isInstanceOf(UnexpectedDomainException.class); + } + + @Test + void 골룸의_멤버를_멤버_아이디로_찾는다() { + // given + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), null, 2L); + + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + List.of(goalRoomPendingMember1, goalRoomPendingMember2)); + + // when + final GoalRoomPendingMember goalRoomPendingMember = goalRoomPendingMembers.findByMemberId(1L).get(); + + // then + assertThat(goalRoomPendingMember).isEqualTo(goalRoomPendingMember1); + } + + @Test + void 리더가_떠나면_리더가_바뀐다() { + // given + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), null, 2L); + + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + List.of(goalRoomPendingMember1, goalRoomPendingMember2)); + + // when + goalRoomPendingMembers.getValues().remove(goalRoomPendingMember1); + goalRoomPendingMembers.changeLeaderIfLeaderLeave(goalRoomPendingMember1); + + // then + assertThat(goalRoomPendingMember2.isLeader()).isTrue(); + } + + @Test + void 다음_리더가_될_사용자를_찾는다() { + // given + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), null, 2L); + + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + List.of(goalRoomPendingMember1, goalRoomPendingMember2)); + + // when + final GoalRoomPendingMember nextLeader = goalRoomPendingMembers.findNextLeader().get(); + + // then + assertThat(nextLeader).isEqualTo(goalRoomPendingMember2); + } + + @Test + void 골룸_사용자의_수를_구한다() { + // given + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), null, 1L); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(1L, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), null, 2L); + + final GoalRoomPendingMembers goalRoomPendingMembers = new GoalRoomPendingMembers( + List.of(goalRoomPendingMember1, goalRoomPendingMember2)); + + // when + final int size = goalRoomPendingMembers.size(); + + // then + assertThat(size).isEqualTo(2); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(LocalDate.now(), LocalDate.now().plusDays(5)), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodeTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodeTest.java similarity index 82% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodeTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodeTest.java index 5dda912e9..dc1d09722 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodeTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodeTest.java @@ -1,15 +1,14 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.vo.Period; +import java.time.LocalDate; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.time.LocalDate; class GoalRoomRoadmapNodeTest { @@ -22,8 +21,7 @@ class GoalRoomRoadmapNodeTest { //when //then - assertDoesNotThrow(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), daysToAdd + 1, - new RoadmapNode("title", "content"))); + assertDoesNotThrow(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), daysToAdd + 1, 1L)); } @ParameterizedTest @@ -35,8 +33,7 @@ class GoalRoomRoadmapNodeTest { //when //then - assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, - new RoadmapNode("title", "content"))) + assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, 1L)) .isInstanceOf(GoalRoomException.class); } @@ -49,8 +46,7 @@ class GoalRoomRoadmapNodeTest { //when //then - assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, - new RoadmapNode("title", "content"))) + assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, 1L)) .isInstanceOf(GoalRoomException.class); } @@ -64,8 +60,7 @@ class GoalRoomRoadmapNodeTest { //when //then - assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, - new RoadmapNode("title", "content"))) + assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, 1L)) .isInstanceOf(GoalRoomException.class); } @@ -79,8 +74,7 @@ class GoalRoomRoadmapNodeTest { //when //then - assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, - new RoadmapNode("title", "content"))) + assertThatThrownBy(() -> new GoalRoomRoadmapNode(new Period(startDate, endDate), checkCount, 1L)) .isInstanceOf(GoalRoomException.class); } @@ -90,7 +84,7 @@ class GoalRoomRoadmapNodeTest { final GoalRoomRoadmapNode goalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(LocalDate.of(2300, 7, 1), LocalDate.of(2300, 7, 15)), - 7, new RoadmapNode("제목", "내용")); + 7, 1L); assertThat(goalRoomRoadmapNode.isDayOfNode(date)).isTrue(); } @@ -101,7 +95,7 @@ class GoalRoomRoadmapNodeTest { final GoalRoomRoadmapNode goalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(LocalDate.of(2300, 7, 1), LocalDate.of(2300, 7, 15)), - 7, new RoadmapNode("제목", "내용")); + 7, 1L); assertThat(goalRoomRoadmapNode.isDayOfNode(date)).isFalse(); } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodesTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodesTest.java similarity index 90% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodesTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodesTest.java index cd91bfdad..ee32e0ccc 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomRoadmapNodesTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomRoadmapNodesTest.java @@ -1,20 +1,19 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.goalroom.domain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.roadmap.domain.RoadmapNode; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.vo.Period; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class GoalRoomRoadmapNodesTest { @@ -117,21 +116,21 @@ class GoalRoomRoadmapNodesTest { assertAll( () -> assertThat(goalRoomRoadmapNodes.getNodeByDate(TODAY).get()) .isEqualTo(new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), - 10, new RoadmapNode("로드맵 제목 1", "로드맵 내용 1"))), + 10, 1L)), () -> assertThat(goalRoomRoadmapNodes.getNodeByDate(TEN_DAY_LATER).get()) .isEqualTo(new GoalRoomRoadmapNode(new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), - 10, new RoadmapNode("로드맵 제목 2", "로드맵 내용 2"))) + 10, 2L)) ); } private GoalRoomRoadmapNodes 골룸_노드를_생성한다() { final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TODAY, TEN_DAY_LATER), - 10, new RoadmapNode("로드맵 제목 1", "로드맵 내용 1")); + 10, 1L); final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), - 10, new RoadmapNode("로드맵 제목 2", "로드맵 내용 2")); + 10, 2L); return new GoalRoomRoadmapNodes( List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomTest.java new file mode 100644 index 000000000..90a52ea2c --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/GoalRoomTest.java @@ -0,0 +1,126 @@ +package co.kirikiri.goalroom.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapCategory; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapDifficulty; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodeImages; +import co.kirikiri.roadmap.domain.RoadmapNodes; +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +class GoalRoomTest { + + private static final GoalRoomName GOAL_ROOM_NAME = new GoalRoomName("골룸 이름"); + private static final LocalDate TODAY = LocalDate.now(); + private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); + private static final LocalDate TWENTY_DAY_LAYER = TODAY.plusDays(20); + private static final LocalDate THIRTY_DAY_LATER = TODAY.plusDays(30); + + @Test + void 골룸의_총_기간을_계산한다() { + // given + final Member creator = 크리에이터를_생성한다(); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); + + // when + final int totalPeriod = goalRoom.calculateTotalPeriod(); + + // then + assertThat(totalPeriod).isEqualTo(31); + } + + @Test + void 골룸의_총_인증_횟수를_구한다() { + //given + final Member creator = 크리에이터를_생성한다(); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); + + //expect + assertThat(goalRoom.getAllCheckCount()).isEqualTo(20); + } + + @Test + void 골룸이_종료된지_3개월_이상_지나지_않으면_false를_반환한다() { + //given + final Member creator = 크리에이터를_생성한다(); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 골룸을_생성한다(roadmapContent, creator); + + // when + final boolean result = goalRoom.isCompletedAfterMonths(3); + + // then + assertThat(result).isEqualTo(false); + } + + private Member 크리에이터를_생성한다() { + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); + return new Member(1L, new Identifier("cokirikiri"), null, + new EncryptedPassword(new Password("password1!")), new Nickname("코끼리"), null, memberProfile); + } + + private Roadmap 로드맵을_생성한다(final Member creator) { + final RoadmapCategory category = new RoadmapCategory("게임"); + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), + category, null); + return roadmap; + } + + private List 로드맵_노드들을_생성한다() { + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); + roadmapNode1.addImages(new RoadmapNodeImages(Collections.emptyList())); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); + return List.of(roadmapNode1, roadmapNode2); + } + + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + final RoadmapContent roadmapContent = new RoadmapContent(1L, "로드맵 본문", roadmapId, + new RoadmapNodes(roadmapNodes)); + return roadmapContent; + } + + private GoalRoom 골룸을_생성한다(final RoadmapContent roadmapContent, final Member creator) { + final List roadmapNodes = roadmapContent.getNodes().getValues(); + + final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); + final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( + new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode.getId()); + + final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); + final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( + new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), 10, secondRoadmapNode.getId()); + + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( + List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); + + return new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), roadmapContent.getId(), + goalRoomRoadmapNodes); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomNameTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/GoalRoomNameTest.java similarity index 90% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomNameTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/GoalRoomNameTest.java index b1eafe64e..1d8fb04d8 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomNameTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/GoalRoomNameTest.java @@ -1,9 +1,9 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.goalroom.domain.vo; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCountTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCountTest.java similarity index 89% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCountTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCountTest.java index 66445598a..c87735cc8 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/LimitedMemberCountTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/domain/vo/LimitedMemberCountTest.java @@ -1,9 +1,9 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.goalroom.domain.vo; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.goalroom.domain.exception.GoalRoomException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepositoryTest.java similarity index 86% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepositoryTest.java index 4ef2ecc37..b10f5a655 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomMemberRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomMemberRepositoryTest.java @@ -1,16 +1,18 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; import static org.assertj.core.api.Assertions.assertThat; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -20,8 +22,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -81,12 +81,12 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator.getId()); final GoalRoomMember expected = goalRoomMemberRepository.save(goalRoomMember); // when - final Optional findGoalRoomMember = goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier( - goalRoom, new Identifier("cokirikiri")); + final Optional findGoalRoomMember = goalRoomMemberRepository.findByGoalRoomAndMemberId( + goalRoom, creator.getId()); // then assertThat(findGoalRoomMember.get()) @@ -105,8 +105,8 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); // when - final Optional findGoalRoomMember = goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier( - goalRoom, new Identifier("cokirikiri2")); + final Optional findGoalRoomMember = goalRoomMemberRepository.findByGoalRoomAndMemberId( + goalRoom, 2L); // then assertThat(findGoalRoomMember) @@ -127,11 +127,11 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1.getId()); final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2); + LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2.getId()); final GoalRoomMember goalRoomMember3 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3); + LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3.getId()); final List expected = goalRoomMemberRepository.saveAll( List.of(goalRoomMember1, goalRoomMember2, goalRoomMember3)); @@ -144,6 +144,32 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, .isEqualTo(expected); } + @Test + void 골룸의_리더를_찾는다() { + // given + final Member creator = 크리에이터를_저장한다(); + final RoadmapCategory category = 카테고리를_생성한다("여행"); + final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); + final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); + final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); + final Member member1 = 사용자를_생성한다("identifier1", "password2!", "name1", "kirikiri1@email.com"); + + final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator.getId()); + final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, + LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member1.getId()); + final List expected = goalRoomMemberRepository.saveAll( + List.of(goalRoomMember1, goalRoomMember2)); + + // when + final GoalRoomMember goalRoomLeader = goalRoomMemberRepository.findLeaderByGoalRoomAndRole( + goalRoom, GoalRoomRole.LEADER).get(); + + // then + assertThat(goalRoomLeader) + .isEqualTo(goalRoomMember1); + } + @Test void 골룸_아이디로_골룸_사용자를_조회하고_들어온지_오래된_순서대로_정렬한다() { // given @@ -158,11 +184,11 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1.getId()); final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2); + LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2.getId()); final GoalRoomMember goalRoomMember3 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3); + LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3.getId()); final List expected = goalRoomMemberRepository.saveAll( List.of(goalRoomMember1, goalRoomMember2, goalRoomMember3)); @@ -189,11 +215,11 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1.getId()); final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2); + LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2.getId()); final GoalRoomMember goalRoomMember3 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3); + LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3.getId()); final GoalRoomMember savedGoalRoomMember1 = goalRoomMemberRepository.save(goalRoomMember1); final GoalRoomMember savedGoalRoomMember2 = goalRoomMemberRepository.save(goalRoomMember2); final GoalRoomMember savedGoalRoomMember3 = goalRoomMemberRepository.save(goalRoomMember3); @@ -221,13 +247,13 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, member1.getId()); goalRoomMember1.updateAccomplishmentRate(30.0); final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2); + LocalDateTime.of(2023, 7, 20, 12, 0, 0), goalRoom, member2.getId()); goalRoomMember2.updateAccomplishmentRate(70.0); final GoalRoomMember goalRoomMember3 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3); + LocalDateTime.of(2023, 7, 21, 12, 0, 0), goalRoom, member3.getId()); goalRoomMember3.updateAccomplishmentRate(10.0); final List expected = goalRoomMemberRepository.saveAll( List.of(goalRoomMember2, goalRoomMember1, goalRoomMember3)); @@ -255,13 +281,13 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); final GoalRoomMember goalRoomMember = new GoalRoomMember( - GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator + GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator.getId() ); goalRoomMemberRepository.save(goalRoomMember); // when - final GoalRoomMember findGoalRoomMember = goalRoomMemberRepository.findGoalRoomMember(goalRoom.getId(), - creator.getIdentifier()).get(); + final GoalRoomMember findGoalRoomMember = goalRoomMemberRepository.findByGoalRoomIdAndMemberId(goalRoom.getId(), + creator.getId()).get(); // then Assertions.assertThat(findGoalRoomMember) @@ -318,22 +344,22 @@ public GoalRoomMemberRepositoryTest(final MemberRepository memberRepository, } private GoalRoom 골룸을_저장한다(final RoadmapContent roadmapContent, final Member member) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), - roadmapContent, member); final List roadmapNodes = roadmapContent.getNodes().getValues(); final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode); + new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode.getId()); final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), - 10, secondRoadmapNode); + 10, secondRoadmapNode.getId()); final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); + + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), roadmapContent.getId(), + goalRoomRoadmapNodes); return goalRoomRepository.save(goalRoom); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepositoryTest.java similarity index 74% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepositoryTest.java index 63d98d128..99ec97e97 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomPendingMemberRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomPendingMemberRepositoryTest.java @@ -1,16 +1,18 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; import static org.assertj.core.api.Assertions.assertThat; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.dto.GoalRoomMemberSortType; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -20,8 +22,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.dto.GoalRoomMemberSortType; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -34,13 +34,13 @@ import co.kirikiri.roadmap.persistence.RoadmapCategoryRepository; import co.kirikiri.roadmap.persistence.RoadmapContentRepository; import co.kirikiri.roadmap.persistence.RoadmapRepository; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; @RepositoryTest class GoalRoomPendingMemberRepositoryTest { @@ -80,18 +80,19 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); - final GoalRoomPendingMember expected = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator); + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator.getId()); + goalRoomPendingMemberRepository.save(goalRoomPendingMember); // when - final Optional findGoalRoomPendingMember = goalRoomPendingMemberRepository.findByGoalRoomAndMemberIdentifier( - goalRoom, new Identifier("cokirikiri")); + final Optional findGoalRoomPendingMember = goalRoomPendingMemberRepository.findByGoalRoomAndMemberId( + goalRoom, creator.getId()); // then assertThat(findGoalRoomPendingMember.get()) .usingRecursiveComparison() .ignoringFields("id", "joinedAt") - .isEqualTo(expected); + .isEqualTo(goalRoomPendingMember); } @Test @@ -104,8 +105,8 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); // when - final Optional findGoalRoomPendingMember = goalRoomPendingMemberRepository.findByGoalRoomAndMemberIdentifier( - goalRoom, new Identifier("cokirikiri2")); + final Optional findGoalRoomPendingMember = goalRoomPendingMemberRepository.findByGoalRoomAndMemberId( + goalRoom, 2L); // then assertThat(findGoalRoomPendingMember) @@ -125,16 +126,16 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final Member member2 = 사용자를_생성한다("identifier2", "password3!", "name2", "kirikiri1@email.com"); final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), goalRoom, creator); - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member2); - final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member3); + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(null, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, creator.getId()); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member1.getId()); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member2.getId()); + final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member3.getId()); goalRoomPendingMemberRepository.saveAll( - List.of(goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3)); + List.of(goalRoomPendingMember, goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3)); final List expected = List.of(goalRoomPendingMember, goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3); @@ -158,26 +159,57 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, + goalRoom, creator.getId()); + goalRoomPendingMemberRepository.save(goalRoomPendingMember1); final Member follower = 사용자를_생성한다("identifier2", "password!2", "name", "kirikiri1@email.com"); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, + goalRoom, follower.getId()); //when - goalRoom.join(follower); + goalRoomPendingMemberRepository.save(goalRoomPendingMember1); + goalRoomPendingMemberRepository.save(goalRoomPendingMember2); //then final List goalRoomPendingMembers = goalRoomPendingMemberRepository.findByGoalRoom( goalRoom); - final List members = goalRoomPendingMembers.stream() - .map(GoalRoomPendingMember::getMember) + final List memberIds = goalRoomPendingMembers.stream() + .map(GoalRoomPendingMember::getMemberId) .toList(); Assertions.assertAll( () -> assertThat(goalRoomPendingMembers).hasSize(2), - () -> assertThat(members).contains(follower) + () -> assertThat(memberIds).contains(follower.getId()) ); } + @Test + void 골룸의_리더를_찾는다() { + //given + final Member creator = 크리에이터를_저장한다(); + final RoadmapCategory category = 카테고리를_생성한다("여행"); + final Roadmap roadmap = 로드맵을_저장한다("title", creator, category); + final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(roadmap.getId()); + final GoalRoom goalRoom = 골룸을_저장한다(roadmapContent, creator); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, + goalRoom, creator.getId()); + goalRoomPendingMemberRepository.save(goalRoomPendingMember1); + + final Member follower = 사용자를_생성한다("identifier2", "password!2", "name", "kirikiri1@email.com"); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, + goalRoom, follower.getId()); + goalRoomPendingMemberRepository.save(goalRoomPendingMember2); + + //when + final GoalRoomPendingMember goalRoomLeader = goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole( + goalRoom, GoalRoomRole.LEADER).get(); + + //then + assertThat(goalRoomLeader).isEqualTo(goalRoomPendingMember1); + } + @Test void 골룸_아이디로_골룸_사용자를_조회하고_들어온지_오래된_순서대로_정렬한다() { // given @@ -191,15 +223,17 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final Member member2 = 사용자를_생성한다("identifier2", "password3!", "name2", "kirikiri1@email.com"); final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); - final GoalRoomPendingMember goalRoomPendingMember0 = goalRoom.getGoalRoomPendingMembers().getValues().get(0); - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member2); - final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member3); + final GoalRoomPendingMember goalRoomPendingMember0 = new GoalRoomPendingMember(null, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, creator.getId()); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member1.getId()); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member2.getId()); + final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member3.getId()); goalRoomPendingMemberRepository.saveAll( - List.of(goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3)); + List.of(goalRoomPendingMember0, goalRoomPendingMember1, goalRoomPendingMember2, + goalRoomPendingMember3)); final List expected = List.of(goalRoomPendingMember0, goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3); @@ -209,6 +243,8 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito // then assertThat(goalRoomPendingMembers) + .usingRecursiveComparison() + .ignoringFields("id") .isEqualTo(expected); } @@ -225,13 +261,16 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final Member member2 = 사용자를_생성한다("identifier2", "password3!", "name2", "kirikiri1@email.com"); final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); - final GoalRoomPendingMember goalRoomPendingMember0 = goalRoom.getGoalRoomPendingMembers().getValues().get(0); - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), goalRoom, member1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member2); - final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member3); + final GoalRoomPendingMember goalRoomPendingMember0 = new GoalRoomPendingMember(null, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, creator.getId()); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member1.getId()); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member2.getId()); + final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member3.getId()); + final GoalRoomPendingMember savedGoalRoomPendingMember0 = goalRoomPendingMemberRepository.save( + goalRoomPendingMember0); final GoalRoomPendingMember savedGoalRoomPendingMember1 = goalRoomPendingMemberRepository.save( goalRoomPendingMember1); final GoalRoomPendingMember savedGoalRoomPendingMember2 = goalRoomPendingMemberRepository.save( @@ -239,7 +278,7 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final GoalRoomPendingMember savedGoalRoomPendingMember3 = goalRoomPendingMemberRepository.save( goalRoomPendingMember3); final List expected = List.of(savedGoalRoomPendingMember3, savedGoalRoomPendingMember2, - savedGoalRoomPendingMember1, goalRoomPendingMember0); + savedGoalRoomPendingMember1, savedGoalRoomPendingMember0); // when final List goalRoomPendingMembers = goalRoomPendingMemberRepository.findByGoalRoomIdOrderedBySortType( @@ -263,15 +302,17 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito final Member member2 = 사용자를_생성한다("identifier2", "password3!", "name2", "kirikiri1@email.com"); final Member member3 = 사용자를_생성한다("identifier3", "password4!", "name3", "kirikiri1@email.com"); - final GoalRoomPendingMember goalRoomPendingMember0 = goalRoom.getGoalRoomPendingMembers().getValues().get(0); - final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), goalRoom, member1); - final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member2); - final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, member3); + final GoalRoomPendingMember goalRoomPendingMember0 = new GoalRoomPendingMember(null, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, creator.getId()); + final GoalRoomPendingMember goalRoomPendingMember1 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member1.getId()); + final GoalRoomPendingMember goalRoomPendingMember2 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member2.getId()); + final GoalRoomPendingMember goalRoomPendingMember3 = new GoalRoomPendingMember(null, GoalRoomRole.FOLLOWER, + LocalDateTime.now(), goalRoom, member3.getId()); goalRoomPendingMemberRepository.saveAll( - List.of(goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3)); + List.of(goalRoomPendingMember0, goalRoomPendingMember1, goalRoomPendingMember2, + goalRoomPendingMember3)); final List expected = List.of(goalRoomPendingMember0, goalRoomPendingMember1, goalRoomPendingMember2, goalRoomPendingMember3); @@ -283,8 +324,12 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito // then assertThat(goalRoomPendingMembers1) + .usingRecursiveComparison() + .ignoringFields("id") .isEqualTo(expected); assertThat(goalRoomPendingMembers2) + .usingRecursiveComparison() + .ignoringFields("id") .isEqualTo(expected); } @@ -311,7 +356,8 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito } private Roadmap 로드맵을_저장한다(final String title, final Member creator, final RoadmapCategory category) { - final Roadmap roadmap = new Roadmap(title, "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), category, new RoadmapTags(new ArrayList<>())); + final Roadmap roadmap = new Roadmap(title, "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), category, + new RoadmapTags(new ArrayList<>())); return roadmapRepository.save(roadmap); } @@ -336,23 +382,24 @@ public GoalRoomPendingMemberRepositoryTest(final MemberRepository memberReposito } private GoalRoom 골룸을_저장한다(final RoadmapContent roadmapContent, final Member member) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), - roadmapContent, member); final List roadmapNodes = roadmapContent.getNodes().getValues(); final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TODAY, TEN_DAY_LATER), - 10, firstRoadmapNode); + 10, firstRoadmapNode.getId()); final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), - 10, secondRoadmapNode); + 10, secondRoadmapNode.getId()); final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); + + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), + roadmapContent.getId(), + goalRoomRoadmapNodes); return goalRoomRepository.save(goalRoom); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomRepositoryTest.java similarity index 80% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomRepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomRepositoryTest.java index fdf033eb9..6a257de5e 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/persistence/GoalRoomRepositoryTest.java @@ -1,17 +1,20 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.goalroom.persistence; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomStatus; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.common.helper.RepositoryTest; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -20,8 +23,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -34,6 +35,7 @@ import co.kirikiri.roadmap.persistence.RoadmapRepository; import org.junit.jupiter.api.Test; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -50,17 +52,23 @@ class GoalRoomRepositoryTest { private final RoadmapRepository roadmapRepository; private final RoadmapContentRepository roadmapContentRepository; private final GoalRoomRepository goalRoomRepository; + private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; public GoalRoomRepositoryTest(final MemberRepository memberRepository, final RoadmapCategoryRepository roadmapCategoryRepository, final RoadmapRepository roadmapRepository, - final RoadmapContentRepository roadmapContentRepository, - final GoalRoomRepository goalRoomRepository) { + final GoalRoomRepository goalRoomRepository, + final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository, + final GoalRoomMemberRepository goalRoomMemberRepository, + final RoadmapContentRepository roadmapContentRepository) { this.memberRepository = memberRepository; this.roadmapCategoryRepository = roadmapCategoryRepository; this.roadmapRepository = roadmapRepository; this.roadmapContentRepository = roadmapContentRepository; this.goalRoomRepository = goalRoomRepository; + this.goalRoomPendingMemberRepository = goalRoomPendingMemberRepository; + this.goalRoomMemberRepository = goalRoomMemberRepository; } @Test @@ -84,7 +92,7 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.save(goalRoom); // when - final GoalRoom findGoalRoom = goalRoomRepository.findByIdWithRoadmapContent(goalRoom.getId()) + final GoalRoom findGoalRoom = goalRoomRepository.findById(goalRoom.getId()) .get(); // then @@ -124,9 +132,11 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.save(goalRoom2); // when - final List goalRooms1 = goalRoomRepository.findGoalRoomsByRoadmapIdAndCond(roadmap.getId(), + final List goalRooms1 = goalRoomRepository.findGoalRoomsByRoadmapContentIdAndCond( + roadmapContent.getId(), RoadmapGoalRoomsOrderType.LATEST, null, 1); - final List goalRooms2 = goalRoomRepository.findGoalRoomsByRoadmapIdAndCond(roadmap.getId(), + final List goalRooms2 = goalRoomRepository.findGoalRoomsByRoadmapContentIdAndCond( + roadmapContent.getId(), RoadmapGoalRoomsOrderType.LATEST, goalRoom2.getId(), 10); assertThat(goalRooms1) @@ -164,9 +174,11 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.save(goalRoom2); // when - final List goalRooms1 = goalRoomRepository.findGoalRoomsByRoadmapIdAndCond(roadmap.getId(), + final List goalRooms1 = goalRoomRepository.findGoalRoomsByRoadmapContentIdAndCond( + roadmapContent.getId(), RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE, null, 1); - final List goalRooms2 = goalRoomRepository.findGoalRoomsByRoadmapIdAndCond(roadmap.getId(), + final List goalRooms2 = goalRoomRepository.findGoalRoomsByRoadmapContentIdAndCond( + roadmapContent.getId(), RoadmapGoalRoomsOrderType.CLOSE_TO_DEADLINE, goalRoom1.getId(), 10); // then @@ -174,129 +186,6 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, assertThat(goalRooms2).isEqualTo(List.of(goalRoom2)); } - @Test - void 골룸의_노드의_시작날짜가_오늘인_골룸을_조회한다() { - // given - final Member creator = 크리에이터를_저장한다(); - final RoadmapCategory category = 카테고리를_저장한다("게임"); - final Roadmap roadmap = 로드맵을_저장한다(creator, category); - - final RoadmapNode roadmapNode1 = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(List.of(roadmapNode1), roadmap.getId()); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), - roadmapNode1); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), - roadmapNode1); - final GoalRoomRoadmapNode goalRoomRoadmapNode3 = 골룸_로드맵_노드를_생성한다(TODAY.plusDays(10), TODAY.plusDays(20), - roadmapNode1); - - final GoalRoom goalRoom1 = 골룸을_생성한다("goalroom1", 20, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1)), creator); - final GoalRoom goalRoom2 = 골룸을_생성한다("goalroom2", 20, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode2)), creator); - final GoalRoom goalRoom3 = 골룸을_생성한다("goalroom3", 20, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode3)), creator); - - final GoalRoom savedGoalRoom1 = goalRoomRepository.save(goalRoom1); - final GoalRoom savedGoalRoom2 = goalRoomRepository.save(goalRoom2); - goalRoomRepository.save(goalRoom3); - - // when - final List findGoalRooms = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan(LocalDate.now()); - - // then - assertThat(findGoalRooms) - .usingRecursiveComparison() - .ignoringFields("id") - .isEqualTo(List.of(savedGoalRoom1, savedGoalRoom2)); - } - - @Test - void 투두리스트와_함께_골룸을_조회한다() { - final Member creator = 사용자를_생성한다("name1", "kirikiri@email.com", "identifier1", "password!1"); - final RoadmapCategory category = 카테고리를_저장한다("여가"); - final Roadmap roadmap = 로드맵을_저장한다(creator, category); - - final RoadmapNode roadmapNode1 = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapNode roadmapNode2 = 로드맵_노드를_생성한다("로드맵 2주차", "로드맵 2주차 내용"); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(List.of(roadmapNode1, roadmapNode2), roadmap.getId()); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), - roadmapNode1); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY.plusDays(11), TODAY.plusDays(20), - roadmapNode2); - final Member goalRoomPendingMember = 사용자를_생성한다("name2", "kirikiri@email.com", "identifier2", "password!2"); - final GoalRoom goalRoom = 골룸을_생성한다("goalroom1", 6, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)), goalRoomPendingMember); - - final LocalDate today = LocalDate.now(); - final LocalDate threeDaysAfter = today.plusDays(3); - goalRoom.addGoalRoomTodo(new GoalRoomToDo( - new GoalRoomTodoContent("투두1"), new Period(today, threeDaysAfter) - )); - goalRoom.addGoalRoomTodo(new GoalRoomToDo( - new GoalRoomTodoContent("투두2"), new Period(today, threeDaysAfter) - )); - goalRoomRepository.save(goalRoom); - - // when - final GoalRoom findGoalRoom = goalRoomRepository.findByIdWithTodos(goalRoom.getId()).get(); - - // then - assertThat(findGoalRoom) - .usingRecursiveComparison() - .ignoringFields("id", "createdAt", "updatedAt") - .isEqualTo(goalRoom); - } - - @Test - void 골룸_아이디로_골룸과_로드맵컨텐츠_골룸노드_투두_정보를_조회한다() { - final Member creator = 크리에이터를_저장한다(); - final RoadmapCategory category = 카테고리를_저장한다("게임"); - final Roadmap roadmap = 로드맵을_저장한다(creator, category); - - final RoadmapNode roadmapNode = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(List.of(roadmapNode), roadmap.getId()); - - final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), - roadmapNode); - final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), - roadmapNode); - - final GoalRoom goalRoom1 = 골룸을_생성한다("goalroom1", 20, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1)), creator); - final GoalRoomToDo goalRoomToDo1 = new GoalRoomToDo(new GoalRoomTodoContent("할 일 목록"), - new Period(TODAY, TEN_DAY_LATER)); - goalRoom1.addGoalRoomTodo(goalRoomToDo1); - final GoalRoom savedGoalRoom1 = goalRoomRepository.save(goalRoom1); - - final GoalRoom goalRoom2 = 골룸을_생성한다("goalroom2", 20, roadmapContent, - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode2)), creator); - final GoalRoomToDo goalRoomToDo2 = new GoalRoomToDo(new GoalRoomTodoContent("우리만의 투두"), - new Period(TEN_DAY_LATER, TWENTY_DAY_LAYER)); - goalRoom2.addGoalRoomTodo(goalRoomToDo2); - final GoalRoom savedGoalRoom2 = goalRoomRepository.save(goalRoom2); - - // when - final GoalRoom findGoalRoom1 = goalRoomRepository.findByIdWithContentAndTodos(goalRoom1.getId()) - .get(); - final GoalRoom findGoalRoom2 = goalRoomRepository.findByIdWithContentAndTodos(goalRoom2.getId()) - .get(); - - //then - assertAll( - () -> assertThat(findGoalRoom1) - .usingRecursiveComparison() - .ignoringFields("id") - .isEqualTo(savedGoalRoom1), - () -> assertThat(findGoalRoom2) - .usingRecursiveComparison() - .ignoringFields("id") - .isEqualTo(savedGoalRoom2) - ); - } - @Test void 사용자가_참가한_모든_골룸들을_조회한다() { //given @@ -326,20 +215,35 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode4)), creator); final Member member = 사용자를_생성한다("팔로워", "010-111-1111", "identifier2", "password2@"); - goalRoom1.join(member); - goalRoom2.join(member); - goalRoom3.join(member); - goalRoom2.start(); - goalRoom3.complete(); + final Long memberId = member.getId(); goalRoomRepository.save(goalRoom1); goalRoomRepository.save(goalRoom2); goalRoomRepository.save(goalRoom3); goalRoomRepository.save(goalRoom4); + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom1, creator.getId())); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom2, creator.getId())); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator.getId())); + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom4, creator.getId())); + + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom1, memberId)); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom2, memberId)); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, memberId)); + + goalRoom2.start(); + goalRoom3.complete(); + //when - final List creatorMemberGoalRooms = goalRoomRepository.findByMember(creator); - final List followerMemberGoalRooms = goalRoomRepository.findByMember(member); + final List creatorMemberGoalRooms = goalRoomRepository.findByMemberId(creator.getId()); + final List followerMemberGoalRooms = goalRoomRepository.findByMemberId(member.getId()); //then assertAll( @@ -381,25 +285,41 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode4)), creator); final Member member = 사용자를_생성한다("팔로워", "010-111-1111", "identifier2", "password2@"); - goalRoom1.join(member); - goalRoom2.join(member); - goalRoom3.join(member); - goalRoom4.join(member); - goalRoom2.start(); - goalRoom3.complete(); + final Long memberId = member.getId(); goalRoomRepository.save(goalRoom1); goalRoomRepository.save(goalRoom2); goalRoomRepository.save(goalRoom3); goalRoomRepository.save(goalRoom4); + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom1, creator.getId())); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom2, creator.getId())); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator.getId())); + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom4, creator.getId())); + + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom1, memberId)); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom2, memberId)); + goalRoomMemberRepository.save( + new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, memberId)); + goalRoomPendingMemberRepository.save( + new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom4, memberId)); + + goalRoom2.start(); + goalRoom3.complete(); + //when - final List memberRecruitingGoalRooms = goalRoomRepository.findByMemberAndStatus(member, - GoalRoomStatus.RECRUITING); - final List memberRunningGoalRooms = goalRoomRepository.findByMemberAndStatus(member, - GoalRoomStatus.RUNNING); - final List memberCompletedGoalRooms = goalRoomRepository.findByMemberAndStatus(member, - GoalRoomStatus.COMPLETED); + final List memberRecruitingGoalRooms = goalRoomRepository.findByMemberAndStatus(memberId, + GoalRoomStatus.RECRUITING.name()); + final List memberRunningGoalRooms = goalRoomRepository.findByMemberAndStatus(memberId, + GoalRoomStatus.RUNNING.name()); + final List memberCompletedGoalRooms = goalRoomRepository.findByMemberAndStatus(memberId, + GoalRoomStatus.COMPLETED.name()); //then assertAll( @@ -470,7 +390,7 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.save(goalRoom3); // when - final List goalRooms = goalRoomRepository.findByRoadmapId(roadmap.getId()); + final List goalRooms = goalRoomRepository.findByRoadmapContentId(roadmapContent.getId()); // then assertThat(goalRooms) @@ -512,12 +432,51 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.saveAll(List.of(goalRoom1, goalRoom2, goalRoom3)); // when - final List goalRooms = goalRoomRepository.findByRoadmapId(roadmap1.getId()); + final List goalRooms = goalRoomRepository.findByRoadmapContentId(roadmapContent1.getId()); // then assertThat(goalRooms).isEqualTo(List.of(goalRoom1, goalRoom2)); } + @Test + void 골룸의_노드의_시작날짜가_오늘인_골룸을_조회한다() { + // given + final Member creator = 크리에이터를_저장한다(); + final RoadmapCategory category = 카테고리를_저장한다("게임"); + final Roadmap roadmap = 로드맵을_저장한다(creator, category); + + final RoadmapNode roadmapNode1 = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(List.of(roadmapNode1), roadmap.getId()); + + final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), + roadmapNode1); + final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), + roadmapNode1); + final GoalRoomRoadmapNode goalRoomRoadmapNode3 = 골룸_로드맵_노드를_생성한다(TODAY.plusDays(10), TODAY.plusDays(20), + roadmapNode1); + + final GoalRoom goalRoom1 = 골룸을_생성한다("goalroom1", 20, roadmapContent, + new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1)), creator); + final GoalRoom goalRoom2 = 골룸을_생성한다("goalroom2", 20, roadmapContent, + new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode2)), creator); + final GoalRoom goalRoom3 = 골룸을_생성한다("goalroom3", 20, roadmapContent, + new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode3)), creator); + + final GoalRoom savedGoalRoom1 = goalRoomRepository.save(goalRoom1); + final GoalRoom savedGoalRoom2 = goalRoomRepository.save(goalRoom2); + goalRoomRepository.save(goalRoom3); + + // when + final List findGoalRooms = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan( + LocalDate.now()); + + // then + assertThat(findGoalRooms) + .usingRecursiveComparison() + .ignoringFields("id") + .isEqualTo(List.of(savedGoalRoom1, savedGoalRoom2)); + } + @Test void 시작날짜가_오늘보다_작거나_같은_모집중인_골룸들을_조회한다() { //given @@ -542,7 +501,8 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, goalRoomRepository.saveAll(List.of(todayStartGoalRoom1, futureStartGoalRoom, todayStartGoalRoom2)); // when - final List goalRooms = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan(LocalDate.now()); + final List goalRooms = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan( + LocalDate.now()); // then assertThat(goalRooms).isEqualTo(List.of(todayStartGoalRoom1, todayStartGoalRoom2)); @@ -585,14 +545,12 @@ public GoalRoomRepositoryTest(final MemberRepository memberRepository, private GoalRoomRoadmapNode 골룸_로드맵_노드를_생성한다(final LocalDate startDate, final LocalDate endDate, final RoadmapNode roadmapNode) { - return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode); + return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode.getId()); } private GoalRoom 골룸을_생성한다(final String name, final Integer limitedMemberCount, final RoadmapContent roadmapContent, final GoalRoomRoadmapNodes goalRoomRoadmapNodes, final Member member) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName(name), new LimitedMemberCount(limitedMemberCount), - roadmapContent, member); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoom; + return new GoalRoom(new GoalRoomName(name), new LimitedMemberCount(limitedMemberCount), roadmapContent.getId(), + goalRoomRoadmapNodes); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListenerTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListenerTest.java new file mode 100644 index 000000000..98a1e3de3 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateEventListenerTest.java @@ -0,0 +1,111 @@ +package co.kirikiri.goalroom.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; + +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.event.GoalRoomCreateEvent; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.member.persistence.MemberRepository; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +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; + +@ExtendWith(MockitoExtension.class) +class GoalRoomCreateEventListenerTest { + + private static final Member MEMBER = new Member(1L, new Identifier("identifier1"), + null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임"), + null, new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private MemberRepository memberRepository; + + @Mock + private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + + @InjectMocks + private GoalRoomCreateEventListener goalRoomCreateEventListener; + + @Test + void 정상적으로_골룸의_리더를_저장한다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(MEMBER)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + + // when + // then + assertDoesNotThrow(() -> goalRoomCreateEventListener.handleGoalRoomLeaderUpdate( + new GoalRoomCreateEvent(1L, "identifier1"))); + } + + @Test + void 골룸_리더_저장_시_존재하지_않은_회원의_Identifier이면_예외를_던진다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + // when + // then + assertThatThrownBy( + () -> goalRoomCreateEventListener.handleGoalRoomLeaderUpdate( + new GoalRoomCreateEvent(1L, "identifier2"))) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸_리더_저장_시_존재하지_않은_골룸이면_예외를_던진다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(MEMBER)); + + // when + // then + assertThatThrownBy( + () -> goalRoomCreateEventListener.handleGoalRoomLeaderUpdate( + new GoalRoomCreateEvent(2L, "identifier1"))) + .isInstanceOf(NotFoundException.class); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final Long roadmapContentId, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContentId, 골룸_로드맵_노드들을_생성한다()); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(LocalDate.now(), LocalDate.now().plusDays(10)), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateServiceTest.java new file mode 100644 index 000000000..738a7010e --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomCreateServiceTest.java @@ -0,0 +1,572 @@ +package co.kirikiri.goalroom.service; + +import static co.kirikiri.goalroom.domain.GoalRoomStatus.RUNNING; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import co.kirikiri.checkfeed.persistence.CheckFeedRepository; +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.service.FilePathGenerator; +import co.kirikiri.common.service.FileService; +import co.kirikiri.common.type.ImageContentType; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapCategory; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapDifficulty; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodeImage; +import co.kirikiri.roadmap.domain.RoadmapNodeImages; +import co.kirikiri.roadmap.domain.RoadmapNodes; +import co.kirikiri.roadmap.domain.RoadmapStatus; +import co.kirikiri.roadmap.persistence.RoadmapContentRepository; +import co.kirikiri.roadmap.persistence.RoadmapRepository; +import co.kirikiri.todo.persistence.GoalRoomToDoCheckRepository; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeAll; +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 org.springframework.context.ApplicationEventPublisher; + +@ExtendWith(MockitoExtension.class) +class GoalRoomCreateServiceTest { + + private static final LocalDate TODAY = LocalDate.now(); + private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); + private static final LocalDate TWENTY_DAY_LATER = TODAY.plusDays(20); + private static final Member MEMBER = new Member(1L, new Identifier("identifier2"), null, + new EncryptedPassword(new Password("password!2")), + new Nickname("name2"), null, + new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); + private static final Roadmap ROADMAP = new Roadmap("roadmap", "introduction", 30, RoadmapDifficulty.DIFFICULT, + MEMBER.getId(), new RoadmapCategory("IT"), null); + private static final Roadmap DELETED_ROADMAP = new Roadmap("roadmap", "introduction", 30, + RoadmapDifficulty.DIFFICULT, RoadmapStatus.DELETED, MEMBER.getId(), new RoadmapCategory("IT"), null); + private static final RoadmapNode ROADMAP_NODE = new RoadmapNode(1L, "title", "content"); + private static final RoadmapNodes ROADMAP_NODES = new RoadmapNodes(new ArrayList<>(List.of(ROADMAP_NODE))); + private static final RoadmapContent ROADMAP_CONTENT = new RoadmapContent(1L, "content", ROADMAP.getId(), + ROADMAP_NODES); + private static final RoadmapContent DELETED_ROADMAP_CONTENT = new RoadmapContent(2L, "content2", + DELETED_ROADMAP.getId(), ROADMAP_NODES); + + private static Member member; + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private GoalRoomMemberRepository goalRoomMemberRepository; + + @Mock + private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + + @Mock + private RoadmapContentRepository roadmapContentRepository; + + @Mock + private RoadmapRepository roadmapRepository; + + @Mock + private MemberRepository memberRepository; + + @Mock + private GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; + + @Mock + private CheckFeedRepository checkFeedRepository; + + @Mock + private FileService fileService; + + @Mock + private FilePathGenerator filePathGenerator; + + @Mock + private ApplicationEventPublisher applicationEventPublisher; + + @InjectMocks + private GoalRoomCreateService goalRoomCreateService; + + @BeforeAll + static void setUp() { + final Identifier identifier = new Identifier("identifier1"); + final Password password = new Password("password1!"); + final EncryptedPassword encryptedPassword = new EncryptedPassword(password); + final Nickname nickname = new Nickname("nickname"); + final String email = "kirikiri@email.com"; + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); + member = new Member(identifier, encryptedPassword, nickname, null, memberProfile); + } + + @Test + void 정상적으로_골룸을_생성한다() { + //given + final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", + 20, new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)))); + final List goalRoomRoadmapNodes = List.of( + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 10, 1L)); + + given(roadmapContentRepository.findById(anyLong())) + .willReturn(Optional.of(ROADMAP_CONTENT)); + given(roadmapRepository.findById(any())) + .willReturn(Optional.of(ROADMAP)); + given(goalRoomRepository.save(any())) + .willReturn(new GoalRoom(1L, new GoalRoomName("name"), new LimitedMemberCount(20), 1L, + new GoalRoomRoadmapNodes(goalRoomRoadmapNodes))); + + //when + assertDoesNotThrow(() -> goalRoomCreateService.create(request, member.getIdentifier().getValue())); + } + + @Test + void 골룸_생성_시_삭제된_로드맵이면_예외를_던진다() { + //given + final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", + 20, new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)))); + + given(roadmapContentRepository.findById(anyLong())) + .willReturn(Optional.of(DELETED_ROADMAP_CONTENT)); + given(roadmapRepository.findById(any())) + .willReturn(Optional.of(DELETED_ROADMAP)); + + //when + //then + assertThatThrownBy(() -> goalRoomCreateService.create(request, member.getIdentifier().getValue())) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 골룸_생성_시_존재하지_않은_로드맵_컨텐츠가_들어올때_예외를_던진다() { + //given + final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", + 20, new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)))); + + given(roadmapContentRepository.findById(anyLong())) + .willReturn(Optional.empty()); + + //when + //then + assertThatThrownBy(() -> goalRoomCreateService.create(request, member.getIdentifier().getValue())) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸_생성_시_로드맵_컨텐츠의_노드사이즈와_요청의_노드사이즈가_다를때_예외를_던진다() { + //given + final List wrongSizeGoalRoomRoadmapNodeRequest = new ArrayList<>(List.of( + new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER), + new GoalRoomRoadmapNodeRequest(2L, 10, TODAY, TEN_DAY_LATER))); + final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", + 20, wrongSizeGoalRoomRoadmapNodeRequest); + + given(roadmapContentRepository.findById(anyLong())) + .willReturn(Optional.of(ROADMAP_CONTENT)); + given(roadmapRepository.findById(any())) + .willReturn(Optional.of(ROADMAP)); + + //when + //then + assertThatThrownBy(() -> goalRoomCreateService.create(request, member.getIdentifier().getValue())) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 골룸_생성_시_로드맵에_존재하지_않는_노드가_요청으로_들어올때_예외를_던진다() { + //given + final long wrongRoadmapNodId = 2L; + final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", + 20, + new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(wrongRoadmapNodId, 10, TODAY, TEN_DAY_LATER)))); + + given(roadmapContentRepository.findById(anyLong())) + .willReturn(Optional.of(ROADMAP_CONTENT)); + given(roadmapRepository.findById(any())) + .willReturn(Optional.of(ROADMAP)); + + //when + //then + assertThatThrownBy(() -> goalRoomCreateService.create(request, member.getIdentifier().getValue())) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸에_참가한다() { + //given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + final Member follower = 사용자를_생성한다(2L, "identifier2", "password1!", "팔로워", "kirikiri1@email"); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(follower)); + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoomIdWithPessimisticLock(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + when(goalRoomPendingMemberRepository.save(any())) + .thenReturn(new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom, follower.getId())); + + //when + //then + assertDoesNotThrow(() -> goalRoomCreateService.join("identifier2", 1L)); + } + + @Test + void 골룸_참가_요청시_유효한_사용자_아이디가_아니면_예외가_발생한다() { + //given + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.empty()); + + //when, then + assertThatThrownBy(() -> goalRoomCreateService.join("identifier2", 1L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("존재하지 않는 회원입니다."); + } + + @Test + void 골룸_참가_요청시_유효한_골룸_아이디가_아니면_예외가_발생한다() { + //given + final Member follower = 사용자를_생성한다(1L, "identifier1", "password1!", "팔로워", "kirikiri1@email"); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(follower)); + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.empty()); + + //when, then + assertThatThrownBy(() -> goalRoomCreateService.join("identifier1", 1L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("존재하지 않는 골룸입니다. goalRoomId = 1"); + } + + @Test + void 골룸_참가_요청시_제한_인원이_가득_찼을_경우_예외가_발생한다() { + //given + final Member creator = 사용자를_생성한다(1L, "identifier1", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 1; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + final Member follower = 사용자를_생성한다(1L, "identifier2", "password1!", "팔로워", "kirikiri1@email"); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(follower)); + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoomIdWithPessimisticLock(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + + //when, then + assertThatThrownBy(() -> goalRoomCreateService.join("identifier2", 1L)) + .isInstanceOf(BadRequestException.class) + .hasMessage("제한 인원이 꽉 찬 골룸에는 참여할 수 없습니다."); + } + + @Test + void 골룸_참가_요청시_모집_중이_아닌_경우_예외가_발생한다() { + //given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + final Member follower = 사용자를_생성한다(2L, "identifier2", "password2!", "팔로워", "kirikiri1@email"); + goalRoom.start(); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(follower)); + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoomIdWithPessimisticLock(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + + //when, then + assertThatThrownBy(() -> goalRoomCreateService.join("identifier2", 1L)) + .isInstanceOf(BadRequestException.class) + .hasMessage("모집 중이지 않은 골룸에는 참여할 수 없습니다."); + } + + @Test + void 골룸_참가_요청시_이미_챰가한_골룸인_경우_예외가_발생한다() { + //given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoomIdWithPessimisticLock(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + + //when, then + assertThatThrownBy(() -> goalRoomCreateService.join("identifier1", 1L)) + .isInstanceOf(BadRequestException.class) + .hasMessage("이미 참여한 골룸에는 참여할 수 없습니다."); + } + + @Test + void 골룸을_시작한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomRepository.findById(any())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + + // when + goalRoomCreateService.startGoalRoom("cokirikiri", 1L); + + // then + verify(goalRoomMemberRepository, times(1)).saveAllInBatch(any()); + verify(goalRoomPendingMemberRepository, times(1)).deleteAllInBatch(any()); + + assertThat(goalRoom.getStatus()).isEqualTo(RUNNING); + } + + @Test + void 골룸_시작시_존재하지_않는_사용자면_예외가_발생한다() { + // given + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("identifier", 1L)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸_시작시_존재하지_않는_골룸이면_예외가_발생한다() { + // given + final Member member = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(member)); + when(goalRoomRepository.findById(any())) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("identifier", 1L)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸을_시작하는_사용자가_골룸의_리더가_아니면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Member follower = 사용자를_생성한다(2L, "kirikirico", "password2!", "끼리코", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(follower)); + when(goalRoomRepository.findById(any())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .thenReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); + + // expected + assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("identifier", 1L)) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 골룸_시작시_시작날짜가_아직_지나지_않았으면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + + final GoalRoom goalRoom = 시작_날짜가_미래인_골룸을_생성한다(1L, creator, roadmapContent, 10); + + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomRepository.findById(any())) + .thenReturn(Optional.of(goalRoom)); + + // expected + assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("cokirikiri", 1L)) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 골룸을_나간다() { + // given + final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("골룸"), new LimitedMemberCount(3), 1L, + 골룸_로드맵_노드들을_생성한다()); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(MEMBER)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, MEMBER.getId()))); + + // when + // then + assertDoesNotThrow(() -> goalRoomCreateService.leave("identifier2", 1L)); + + } + + @Test + void 골룸을_나갈때_존재하지_않는_회원일_경우_예외가_발생한다() { + // given + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.empty()); + + // when + // then + assertThatThrownBy(() -> goalRoomCreateService.leave("identifier2", 1L)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸을_나갈때_존재하지_않는_골룸일_경우_예외가_발생한다() { + // given + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.empty()); + + // when + // then + assertThatThrownBy(() -> goalRoomCreateService.leave("identifier2", 1L)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸을_나갈때_골룸이_진행중이면_예외가_발생한다() { + // given + final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("골룸"), new LimitedMemberCount(3), 1L, + 골룸_로드맵_노드들을_생성한다()); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + + // when + goalRoom.start(); + + // then + assertThatThrownBy(() -> goalRoomCreateService.leave("identifier2", 1L)) + .isInstanceOf(BadRequestException.class); + } + + private Member 사용자를_생성한다(final Long memberId, final String identifier, final String password, final String nickname, + final String email) { + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); + + return new Member(memberId, new Identifier(identifier), null, new EncryptedPassword(new Password(password)), + new Nickname(nickname), null, memberProfile); + } + + private Roadmap 로드맵을_생성한다(final Member creator) { + final RoadmapCategory category = new RoadmapCategory("게임"); + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), + category, null); + return roadmap; + } + + private List 로드맵_노드들을_생성한다() { + final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); + roadmapNode1.addImages(new RoadmapNodeImages(노드_이미지들을_생성한다())); + final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); + return List.of(roadmapNode1, roadmapNode2); + } + + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", roadmapId, new RoadmapNodes(roadmapNodes)); + return roadmapContent; + } + + private List 노드_이미지들을_생성한다() { + return List.of( + new RoadmapNodeImage("node-image1.png", "node-image1-save-path", ImageContentType.PNG), + new RoadmapNodeImage("node-image2.png", "node-image2-save-path", ImageContentType.PNG) + ); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final RoadmapContent roadmapContent, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), 골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); + } + + private GoalRoom 시작_날짜가_미래인_골룸을_생성한다(final Long goalRoomId, final Member creator, + final RoadmapContent roadmapContent, final Integer limitedMemberCount) { + final GoalRoomRoadmapNode goalRoomRoadmapNode = new GoalRoomRoadmapNode( + new Period(TEN_DAY_LATER, TWENTY_DAY_LATER), 5, roadmapContent.getNodes().getValues().get(0).getId()); + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode))); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다(final RoadmapNodes roadmapNodes) { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0).getId()), + new GoalRoomRoadmapNode(new Period(TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER), 5, + roadmapNodes.getValues().get(1).getId())) + ); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListenerTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListenerTest.java new file mode 100644 index 000000000..d8e941de8 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomLeaveEventListenerTest.java @@ -0,0 +1,122 @@ +package co.kirikiri.goalroom.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.event.GoalRoomLeaveEvent; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +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; + +@ExtendWith(MockitoExtension.class) +class GoalRoomLeaveEventListenerTest { + + private static final Member MEMBER = new Member(1L, new Identifier("identifier1"), + null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임"), + null, new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private GoalRoomMemberRepository goalRoomMemberRepository; + + @Mock + private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + + @InjectMocks + private GoalRoomLeaveEventListener goalRoomLeaveEventListener; + + @Test + void 정상적으로_빈_골룸을_삭제한다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findByGoalRoom(any())) + .willReturn(Collections.emptyList()); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(Collections.emptyList()); + + // when + // then + assertDoesNotThrow(() -> goalRoomLeaveEventListener.handleDeleteEmptyGoalRoom( + new GoalRoomLeaveEvent(1L))); + } + + @Test + void 빈_골룸_삭제_시_존재하지_않은_골룸이면_예외를_던진다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + // when + // then + assertThatThrownBy( + () -> goalRoomLeaveEventListener.handleDeleteEmptyGoalRoom( + new GoalRoomLeaveEvent(2L))) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸이_비어있지_않으면_골룸이_삭제되지_않는다() { + // given + final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, 1L, 10); + + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, 1L))); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(Collections.emptyList()); + + // when + // then + assertDoesNotThrow(() -> goalRoomLeaveEventListener.handleDeleteEmptyGoalRoom( + new GoalRoomLeaveEvent(1L))); + + verify(goalRoomRepository, never()).delete(any()); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final Long roadmapContentId, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContentId, 골룸_로드맵_노드들을_생성한다()); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(LocalDate.now(), LocalDate.now().plusDays(10)), 5, 1L)) + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomReadServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomReadServiceTest.java similarity index 54% rename from backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomReadServiceTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomReadServiceTest.java index b3102041c..3934ad781 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomReadServiceTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomReadServiceTest.java @@ -1,28 +1,42 @@ -package co.kirikiri.service; +package co.kirikiri.goalroom.service; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import co.kirikiri.common.exception.ForbiddenException; import co.kirikiri.common.exception.NotFoundException; import co.kirikiri.common.service.FileService; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomStatusTypeRequest; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodesResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.MemberResponse; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -32,12 +46,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.member.service.dto.response.MemberResponse; -import co.kirikiri.persistence.goalroom.CheckFeedRepository; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.goalroom.GoalRoomToDoCheckRepository; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -48,26 +56,8 @@ import co.kirikiri.roadmap.domain.RoadmapNodes; import co.kirikiri.roadmap.domain.RoadmapStatus; import co.kirikiri.roadmap.domain.RoadmapTags; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto; -import co.kirikiri.service.dto.goalroom.request.GoalRoomStatusTypeRequest; -import co.kirikiri.service.dto.goalroom.response.CheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodesResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import co.kirikiri.service.goalroom.GoalRoomReadService; -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 co.kirikiri.roadmap.persistence.RoadmapContentRepository; +import co.kirikiri.roadmap.persistence.RoadmapNodeRepository; import java.net.MalformedURLException; import java.net.URL; import java.time.LocalDate; @@ -76,6 +66,11 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +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; @ExtendWith(MockitoExtension.class) class GoalRoomReadServiceTest { @@ -98,42 +93,38 @@ class GoalRoomReadServiceTest { private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; @Mock - private GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; + private RoadmapNodeRepository roadmapNodeRepository; @Mock - private CheckFeedRepository checkFeedRepository; + private RoadmapContentRepository roadmapContentRepository; @Mock private FileService fileService; - @InjectMocks - private GoalRoomReadService goalRoomReadService; + @Mock + private DashBoardCheckFeedService dashBoardCheckFeedService; - private static GoalRoomResponse 예상하는_골룸_응답을_생성한다() { - final List goalRoomNodeResponses = List.of( - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", TODAY, TEN_DAY_LATER, 10), - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", TWENTY_DAY_LAYER, THIRTY_DAY_LATER, 2)); - return new GoalRoomResponse("골룸", 1, 10, goalRoomNodeResponses, 31); - } + @Mock + private DashBoardToDoService dashBoardToDoService; - private static GoalRoomCertifiedResponse 예상하는_로그인된_사용자의_골룸_응답을_생성한다(final Boolean isJoined, - final int currentMemberCount) { - final List goalRoomNodeResponses = List.of( - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", TODAY, TEN_DAY_LATER, 10), - new co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", TWENTY_DAY_LAYER, THIRTY_DAY_LATER, 2)); - return new GoalRoomCertifiedResponse("골룸", currentMemberCount, 10, goalRoomNodeResponses, 31, isJoined); - } + @InjectMocks + private GoalRoomReadService goalRoomReadService; @Test void 골룸_아이디로_골룸_정보를_조회한다() { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.of(goalRoom)); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); // when final GoalRoomResponse goalRoomResponse = goalRoomReadService.findGoalRoom(goalRoom.getId()); @@ -147,8 +138,8 @@ class GoalRoomReadServiceTest { @Test void 골룸_조회시_골룸_아이디가_유효하지_않으면_예외가_발생한다() { // given - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.empty()); // expected assertThatThrownBy(() -> goalRoomReadService.findGoalRoom(1L)) @@ -156,20 +147,27 @@ class GoalRoomReadServiceTest { } @Test - void 모집중인_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_대기_목록_조회시_참여하는_사용자면_참여여부가_true로_반환된다() { + void 모집중인_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_조회시_참여하는_사용자면_참여여부가_true로_반환된다() { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator); + final GoalRoomPendingMember goalRoomPendingMember = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator.getId()); - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomPendingMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomPendingMember)); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomPendingMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.of(goalRoomPendingMember)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); // when final GoalRoomCertifiedResponse goalRoomResponse = goalRoomReadService.findGoalRoom( @@ -182,17 +180,24 @@ class GoalRoomReadServiceTest { } @Test - void 모집중인_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_대기_목록_조회시_참여하지_않는_사용자면_참여여부가_false로_반환된다() { + void 모집중인_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_조회시_참여하지_않는_사용자면_참여여부가_false로_반환된다() { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomPendingMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomPendingMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.empty()); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, creator.getId()))); // when final GoalRoomCertifiedResponse goalRoomResponse = goalRoomReadService.findGoalRoom( @@ -205,21 +210,26 @@ class GoalRoomReadServiceTest { } @Test - void 모집중이지_않은_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_사용자_목록_조회시_참여하는_사용자면_참여여부가_true로_반환된다() { + void 모집중이지_않은_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_조회시_참여하는_사용자면_참여여부가_true로_반환된다() { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); goalRoom.start(); final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator); + LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, creator.getId()); - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomMember)); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.of(goalRoomMember)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); // when final GoalRoomCertifiedResponse goalRoomResponse = goalRoomReadService.findGoalRoom( @@ -232,18 +242,23 @@ class GoalRoomReadServiceTest { } @Test - void 모집중이지_않은_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_사용자_목록_조회시_참여하지_않는_사용자면_참여여부가_false로_반환된다() { + void 모집중이지_않은_골룸에_대해서_골룸_아이디와_사용자_아이디로_골룸_조회시_참여하지_않는_사용자면_참여여부가_false로_반환된다() { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); goalRoom.start(); - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.empty()); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); // when final GoalRoomCertifiedResponse goalRoomResponse = goalRoomReadService.findGoalRoom( @@ -258,8 +273,8 @@ class GoalRoomReadServiceTest { @Test void 골룸_아이디와_사용자_아이디로_골룸_대기_목록_조회시_골룸_아이디가_유효하지_않으면_예외가_발생한다() { // given - when(goalRoomRepository.findByIdWithRoadmapContent(any())) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findById(any())) + .willReturn(Optional.empty()); // expected assertThatThrownBy(() -> goalRoomReadService.findGoalRoom("cokirikiri", 1L)) @@ -272,19 +287,24 @@ class GoalRoomReadServiceTest { final Member creator = 크리에이터를_생성한다(); final Member follower = 사용자를_생성한다(2L); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); goalRoom.start(); final GoalRoomMember goalRoomMemberCreator = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), - goalRoom, creator); - final GoalRoomMember goalRoomMemberFollower = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), - goalRoom, follower); + goalRoom, creator.getId()); + final GoalRoomMember goalRoomMemberFollower = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), + goalRoom, follower.getId()); given(goalRoomRepository.findById(anyLong())) .willReturn(Optional.of(goalRoom)); given(goalRoomMemberRepository.findByGoalRoomIdOrderedBySortType(anyLong(), any())) .willReturn(List.of(goalRoomMemberCreator, goalRoomMemberFollower)); + given(memberRepository.findWithMemberImageById(1L)) + .willReturn(Optional.of(creator)); + given(memberRepository.findWithMemberImageById(2L)) + .willReturn(Optional.of(follower)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -308,19 +328,24 @@ class GoalRoomReadServiceTest { final Member follower = 사용자를_생성한다(2L); final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); goalRoom.complete(); final GoalRoomMember goalRoomMemberCreator = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), - goalRoom, creator); - final GoalRoomMember goalRoomMemberFollower = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), - goalRoom, follower); + goalRoom, creator.getId()); + final GoalRoomMember goalRoomMemberFollower = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), + goalRoom, follower.getId()); given(goalRoomRepository.findById(anyLong())) .willReturn(Optional.of(goalRoom)); given(goalRoomMemberRepository.findByGoalRoomIdOrderedBySortType(anyLong(), any())) .willReturn(List.of(goalRoomMemberCreator, goalRoomMemberFollower)); + given(memberRepository.findWithMemberImageById(1L)) + .willReturn(Optional.of(creator)); + given(memberRepository.findWithMemberImageById(2L)) + .willReturn(Optional.of(follower)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -344,18 +369,23 @@ class GoalRoomReadServiceTest { final Member creator = 사용자를_생성한다(1L); final Member follower = 사용자를_생성한다(2L); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - final GoalRoomPendingMember goalRoomMemberCreator = new GoalRoomPendingMember(GoalRoomRole.LEADER, - LocalDateTime.now(), goalRoom, creator); - final GoalRoomPendingMember goalRoomMemberFollower = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, - LocalDateTime.now(), goalRoom, follower); + final GoalRoomPendingMember goalRoomMemberCreator = new GoalRoomPendingMember(1L, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, creator.getId()); + final GoalRoomPendingMember goalRoomMemberFollower = new GoalRoomPendingMember(2L, GoalRoomRole.LEADER, + LocalDateTime.now(), goalRoom, follower.getId()); given(goalRoomRepository.findById(anyLong())) .willReturn(Optional.of(goalRoom)); given(goalRoomPendingMemberRepository.findByGoalRoomIdOrderedBySortType(anyLong(), any())) .willReturn(List.of(goalRoomMemberCreator, goalRoomMemberFollower)); + given(memberRepository.findWithMemberImageById(1L)) + .willReturn(Optional.of(creator)); + given(memberRepository.findWithMemberImageById(2L)) + .willReturn(Optional.of(follower)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -368,6 +398,7 @@ class GoalRoomReadServiceTest { "http://example.com/serverFilePath", 0.0); final GoalRoomMemberResponse expectedGoalRoomMemberResponse2 = new GoalRoomMemberResponse(2L, "name1", "http://example.com/serverFilePath", 0.0); + assertThat(result) .isEqualTo(List.of(expectedGoalRoomMemberResponse1, expectedGoalRoomMemberResponse2)); verify(goalRoomMemberRepository, never()).findByGoalRoomIdOrderedBySortType(anyLong(), any()); @@ -386,118 +417,52 @@ class GoalRoomReadServiceTest { .isInstanceOf(NotFoundException.class); } - @Test - void 골룸의_전체_투두리스트를_조회한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - - final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(1L, new GoalRoomTodoContent("투두 1"), - new Period(TODAY, TEN_DAY_LATER)); - final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(2L, new GoalRoomTodoContent("투두 2"), - new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER)); - goalRoom.addGoalRoomTodo(firstGoalRoomTodo); - goalRoom.addGoalRoomTodo(secondGoalRoomTodo); - - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - when(goalRoomMemberRepository.findGoalRoomMember(anyLong(), any())) - .thenReturn(Optional.of(goalRoomMember)); - when(goalRoomRepository.findByIdWithTodos(1L)) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomToDoCheckRepository.findByGoalRoomIdAndMemberIdentifier(anyLong(), any())) - .thenReturn(List.of( - new GoalRoomToDoCheck(goalRoomMember, firstGoalRoomTodo) - )); - - // when - final List responses = goalRoomReadService.findAllGoalRoomTodo(1L, "identifier"); - final List expected = List.of( - new GoalRoomTodoResponse(1L, "투두 1", TODAY, TEN_DAY_LATER, new GoalRoomToDoCheckResponse(true)), - new GoalRoomTodoResponse(2L, "투두 2", TWENTY_DAY_LAYER, THIRTY_DAY_LATER, - new GoalRoomToDoCheckResponse(false))); - - // then - assertThat(responses) - .isEqualTo(expected); - } - - @Test - void 골룸의_투두리스트_조회시_존재하지_않는_골룸이면_예외가_발생한다() { - // given - when(goalRoomRepository.findByIdWithTodos(1L)) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomReadService.findAllGoalRoomTodo(1L, "identifier")) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸의_투두리스트_조회시_골룸에_참여하지_않은_사용자면_예외가_발생한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - - final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(1L, new GoalRoomTodoContent("투두 1"), - new Period(TODAY, TEN_DAY_LATER)); - final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(2L, new GoalRoomTodoContent("투두 2"), - new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER)); - goalRoom.addGoalRoomTodo(firstGoalRoomTodo); - goalRoom.addGoalRoomTodo(secondGoalRoomTodo); - - when(goalRoomRepository.findByIdWithTodos(1L)) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findGoalRoomMember(anyLong(), any())) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomReadService.findAllGoalRoomTodo(1L, "identifier")) - .isInstanceOf(ForbiddenException.class); - } - @Test void 진행중인_사용자_단일_골룸을_조회한다() throws MalformedURLException { // given - final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); - final RoadmapNode roadmapNode3 = new RoadmapNode("로드맵 3주차", "로드맵 3주차 내용"); - final RoadmapNode roadmapNode4 = new RoadmapNode("로드맵 4주차", "로드맵 4주차 내용"); + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); + final RoadmapNode roadmapNode3 = new RoadmapNode(3L, "로드맵 3주차", "로드맵 3주차 내용"); + final RoadmapNode roadmapNode4 = new RoadmapNode(4L, "로드맵 4주차", "로드맵 4주차 내용"); final RoadmapNodes roadmapNodes = new RoadmapNodes( List.of(roadmapNode1, roadmapNode2, roadmapNode3, roadmapNode4)); final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", 1L, roadmapNodes); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = new GoalRoomRoadmapNode( - new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1); + new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2); + new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2.getId()); + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( + List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)); final Member member = 사용자를_생성한다(1L); final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("goalroom"), new LimitedMemberCount(10), - roadmapContent, member); + roadmapContent.getId(), goalRoomRoadmapNodes); goalRoom.start(); - goalRoom.addAllGoalRoomRoadmapNodes( - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2))); - goalRoom.addAllGoalRoomMembers( - List.of(new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member))); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + member.getId()); - final List checkFeeds = 인증_피드_목록을_생성한다(goalRoomRoadmapNode1, member, goalRoom); - given(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) + final List dashBoardCheckFeedResponses = 대시보드_인증_피드_목록_응답을_생성한다(); + given(goalRoomRepository.findByIdWithNodes(anyLong())) .willReturn(Optional.of(goalRoom)); given(memberRepository.findByIdentifier(any())) .willReturn(Optional.of(member)); - given(checkFeedRepository.findByRunningGoalRoomRoadmapNode(any())) - .willReturn(checkFeeds); - given(fileService.generateUrl(anyString(), any())) - .willReturn(new URL("http://example.com/serverFilePath")); + given(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(dashBoardCheckFeedService.findCheckFeedsByNodeAndGoalRoomStatus(any())) + .willReturn(dashBoardCheckFeedResponses); + given(dashBoardToDoService.findMemberCheckedGoalRoomToDoIds(any(), any())) + .willReturn(Collections.emptyList()); + given(goalRoomMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(goalRoomLeader)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); final MemberGoalRoomResponse expected = new MemberGoalRoomResponse(goalRoom.getName().getValue(), - goalRoom.getStatus().name(), member.getId(), goalRoom.getCurrentMemberCount(), + goalRoom.getStatus().name(), member.getId(), 1, goalRoom.getLimitedMemberCount().getValue(), goalRoom.getStartDate(), goalRoom.getEndDate(), roadmapContent.getId(), new GoalRoomRoadmapNodesResponse(false, true, List.of( @@ -509,10 +474,14 @@ class GoalRoomReadServiceTest { goalRoomRoadmapNode2.getEndDate(), goalRoomRoadmapNode2.getCheckCount()) )), Collections.emptyList(), List.of( - new CheckFeedResponse(1L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(2L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(3L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(4L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()) + new DashBoardCheckFeedResponse(1L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(2L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(3L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(4L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()) )); //when @@ -528,37 +497,50 @@ class GoalRoomReadServiceTest { @Test void 모집중인_사용자_단일_골룸_조회시_인증피드가_빈_응답을_반환한다() { // given - final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); - final RoadmapNode roadmapNode3 = new RoadmapNode("로드맵 3주차", "로드맵 3주차 내용"); - final RoadmapNode roadmapNode4 = new RoadmapNode("로드맵 4주차", "로드맵 4주차 내용"); + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); + final RoadmapNode roadmapNode3 = new RoadmapNode(3L, "로드맵 3주차", "로드맵 3주차 내용"); + final RoadmapNode roadmapNode4 = new RoadmapNode(4L, "로드맵 4주차", "로드맵 4주차 내용"); final RoadmapNodes roadmapNodes = new RoadmapNodes( List.of(roadmapNode1, roadmapNode2, roadmapNode3, roadmapNode4)); final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", 1L, roadmapNodes); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = new GoalRoomRoadmapNode( - new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1); + new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2); + new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode3 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(21), TODAY.plusDays(30)), 5, roadmapNode1); + new Period(TODAY.plusDays(21), TODAY.plusDays(30)), 5, roadmapNode1.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode4 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(31), TODAY.plusDays(40)), 5, roadmapNode2); + new Period(TODAY.plusDays(31), TODAY.plusDays(40)), 5, roadmapNode2.getId()); + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( + List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2, goalRoomRoadmapNode3, goalRoomRoadmapNode4)); final Member member = 사용자를_생성한다(1L); final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("goalroom"), new LimitedMemberCount(10), - roadmapContent, member); - goalRoom.addAllGoalRoomRoadmapNodes( - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2, goalRoomRoadmapNode3, - goalRoomRoadmapNode4))); + roadmapContent.getId(), goalRoomRoadmapNodes); + final GoalRoomPendingMember goalRoomLeader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom, + member.getId()); - given(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) + given(goalRoomRepository.findByIdWithNodes(anyLong())) .willReturn(Optional.of(goalRoom)); given(memberRepository.findByIdentifier(any())) .willReturn(Optional.of(member)); + given(goalRoomPendingMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(dashBoardCheckFeedService.findCheckFeedsByNodeAndGoalRoomStatus(any())) + .willReturn(Collections.emptyList()); + given(dashBoardToDoService.findMemberCheckedGoalRoomToDoIds(any(), any())) + .willReturn(Collections.emptyList()); + given(goalRoomPendingMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(goalRoomLeader)); + given(goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); final MemberGoalRoomResponse expected = new MemberGoalRoomResponse(goalRoom.getName().getValue(), - goalRoom.getStatus().name(), member.getId(), goalRoom.getCurrentMemberCount(), + goalRoom.getStatus().name(), member.getId(), 1, goalRoom.getLimitedMemberCount().getValue(), goalRoom.getStartDate(), goalRoom.getEndDate(), roadmapContent.getId(), new GoalRoomRoadmapNodesResponse(false, true, List.of( @@ -580,45 +562,54 @@ class GoalRoomReadServiceTest { @Test void 종료된_사용자_단일_골룸을_조회시_전체_인증피드를_대상으로_반환한다() throws MalformedURLException { // given - final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); - final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); - final RoadmapNode roadmapNode3 = new RoadmapNode("로드맵 3주차", "로드맵 3주차 내용"); - final RoadmapNode roadmapNode4 = new RoadmapNode("로드맵 4주차", "로드맵 4주차 내용"); + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); + final RoadmapNode roadmapNode3 = new RoadmapNode(3L, "로드맵 3주차", "로드맵 3주차 내용"); + final RoadmapNode roadmapNode4 = new RoadmapNode(4L, "로드맵 4주차", "로드맵 4주차 내용"); final RoadmapNodes roadmapNodes = new RoadmapNodes( List.of(roadmapNode1, roadmapNode2, roadmapNode3, roadmapNode4)); final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", 1L, roadmapNodes); final GoalRoomRoadmapNode goalRoomRoadmapNode1 = new GoalRoomRoadmapNode( - new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1); + new Period(TODAY, TODAY.plusDays(10)), 5, roadmapNode1.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode2 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2); + new Period(TODAY.plusDays(11), TODAY.plusDays(20)), 5, roadmapNode2.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode3 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(21), TODAY.plusDays(30)), 5, roadmapNode1); + new Period(TODAY.plusDays(21), TODAY.plusDays(30)), 5, roadmapNode1.getId()); final GoalRoomRoadmapNode goalRoomRoadmapNode4 = new GoalRoomRoadmapNode( - new Period(TODAY.plusDays(31), TODAY.plusDays(40)), 5, roadmapNode2); + new Period(TODAY.plusDays(31), TODAY.plusDays(40)), 5, roadmapNode2.getId()); + final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( + List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)); final Member member = 사용자를_생성한다(1L); final GoalRoom goalRoom = new GoalRoom(1L, new GoalRoomName("goalroom"), new LimitedMemberCount(10), - roadmapContent, member); + roadmapContent.getId(), goalRoomRoadmapNodes); + final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + member.getId()); + goalRoom.start(); goalRoom.complete(); - goalRoom.addAllGoalRoomRoadmapNodes( - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2))); - goalRoom.addAllGoalRoomMembers( - List.of(new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member))); - final List checkFeeds = 인증_피드_목록을_생성한다(goalRoomRoadmapNode1, member, goalRoom); - given(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) + final List dashBoardCheckFeedResponses = 대시보드_인증_피드_목록_응답을_생성한다(); + given(goalRoomRepository.findByIdWithNodes(anyLong())) .willReturn(Optional.of(goalRoom)); given(memberRepository.findByIdentifier(any())) .willReturn(Optional.of(member)); - given(checkFeedRepository.findByGoalRoom(any())) - .willReturn(checkFeeds); - given(fileService.generateUrl(anyString(), any())) - .willReturn(new URL("http://example.com/serverFilePath")); + given(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); + given(roadmapContentRepository.findByIdWithRoadmapNodes(any())) + .willReturn(Optional.of(roadmapContent)); + given(dashBoardCheckFeedService.findCheckFeedsByNodeAndGoalRoomStatus(any())) + .willReturn(dashBoardCheckFeedResponses); + given(dashBoardToDoService.findMemberCheckedGoalRoomToDoIds(any(), any())) + .willReturn(Collections.emptyList()); + given(goalRoomMemberRepository.findByGoalRoom(any())) + .willReturn(List.of(goalRoomLeader)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(any(), any())) + .willReturn(Optional.of(goalRoomLeader)); final MemberGoalRoomResponse expected = new MemberGoalRoomResponse(goalRoom.getName().getValue(), - goalRoom.getStatus().name(), member.getId(), goalRoom.getCurrentMemberCount(), + goalRoom.getStatus().name(), member.getId(), 1, goalRoom.getLimitedMemberCount().getValue(), goalRoom.getStartDate(), goalRoom.getEndDate(), roadmapContent.getId(), new GoalRoomRoadmapNodesResponse(false, true, List.of( @@ -630,10 +621,14 @@ class GoalRoomReadServiceTest { goalRoomRoadmapNode2.getEndDate(), goalRoomRoadmapNode2.getCheckCount()) )), Collections.emptyList(), List.of( - new CheckFeedResponse(1L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(2L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(3L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()), - new CheckFeedResponse(4L, "http://example.com/serverFilePath", "인증 피드 설명", LocalDate.now()) + new DashBoardCheckFeedResponse(1L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(2L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(3L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(4L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()) )); //when @@ -649,8 +644,8 @@ class GoalRoomReadServiceTest { @Test void 사용자_단일_목록_조회_시_유효하지_않은_골룸_아이디일_경우_예외를_반환한다() { //given - when(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) - .thenThrow(new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = 1")); + given(goalRoomRepository.findByIdWithNodes(anyLong())) + .willThrow(new NotFoundException("골룸 정보가 존재하지 않습니다. goalRoomId = 1")); //when, then assertThatThrownBy(() -> goalRoomReadService.findMemberGoalRoom("identifier1", 1L)) @@ -664,13 +659,14 @@ class GoalRoomReadServiceTest { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - when(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(memberRepository.findByIdentifier(any())) - .thenThrow(new NotFoundException("존재하지 않는 회원입니다.")); + given(goalRoomRepository.findByIdWithNodes(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(memberRepository.findByIdentifier(any())) + .willThrow(new NotFoundException("존재하지 않는 회원입니다.")); // when, then assertThatThrownBy(() -> goalRoomReadService.findMemberGoalRoom("identifier2", 1L)) @@ -684,13 +680,14 @@ class GoalRoomReadServiceTest { final Member creator = 크리에이터를_생성한다(); final Member member = 사용자를_생성한다(2L); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - when(goalRoomRepository.findByIdWithContentAndTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); + given(goalRoomRepository.findByIdWithNodes(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); // when, then assertThatThrownBy(() -> goalRoomReadService.findMemberGoalRoom("identifier2", 1L)) @@ -703,7 +700,8 @@ class GoalRoomReadServiceTest { // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom1 = 골룸을_생성한다(creator, roadmapContent); 골룸을_생성한다(creator, roadmapContent); @@ -711,15 +709,31 @@ class GoalRoomReadServiceTest { 골룸을_생성한다(creator, roadmapContent); final Member member = 사용자를_생성한다(2L); - goalRoom1.join(member); - goalRoom3.join(member); + final GoalRoomPendingMember goalRoom1Leader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom1, + creator.getId()); + final GoalRoomPendingMember goalRoom3Leader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom3, + creator.getId()); + final GoalRoomPendingMember goalRoom1Member = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom1, + member.getId()); + final GoalRoomPendingMember goalRoom3Member = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom3, + member.getId()); - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); - when(goalRoomRepository.findByMember(any())) - .thenReturn(List.of(goalRoom1, goalRoom3)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findByMemberId(any())) + .willReturn(List.of(goalRoom1, goalRoom3)); + given(goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom1, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom1Leader)); + given(goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom3, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom3Leader)); + given(memberRepository.findWithMemberProfileAndImageById(1L)) + .willReturn(Optional.of(creator)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); + given(goalRoomPendingMemberRepository.findByGoalRoom(goalRoom1)) + .willReturn(List.of(goalRoom1Leader, goalRoom1Member)); + given(goalRoomPendingMemberRepository.findByGoalRoom(goalRoom3)) + .willReturn(List.of(goalRoom3Leader, goalRoom3Member)); final List expected = List.of( new MemberGoalRoomForListResponse(1L, "골룸", "RECRUITING", 2, 10, LocalDateTime.now(), TODAY, @@ -744,15 +758,15 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV @Test void 사용자_골룸_목룩_조회_중_참여한_골룸이없으면_빈_리스트를_반환한다() { // given - final Member member = 사용자를_생성한다(1L); + final Member creator = 크리에이터를_생성한다(); - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); - when(goalRoomRepository.findByMember(any())) - .thenReturn(Collections.emptyList()); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomRepository.findByMemberId(any())) + .willReturn(Collections.emptyList()); // when - final List response = goalRoomReadService.findMemberGoalRooms(member.getIdentifier().getValue()); + final List response = goalRoomReadService.findMemberGoalRooms("identifier1"); // then assertThat(response).isEmpty(); @@ -762,27 +776,44 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV void 사용자_골룸_목록_중_모집_중인_상태만_조회한다() throws MalformedURLException { final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom1 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom2 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom3 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom4 = 골룸을_생성한다(creator, roadmapContent); final Member member = 사용자를_생성한다(2L); - goalRoom1.join(member); - goalRoom2.join(member); - goalRoom3.join(member); - goalRoom4.join(member); + final Long memberId = member.getId(); + + final GoalRoomPendingMember goalRoom1Leader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom1, + creator.getId()); + final GoalRoomPendingMember goalRoom2Leader = new GoalRoomPendingMember(GoalRoomRole.LEADER, goalRoom2, + creator.getId()); + final GoalRoomPendingMember goalRoom1Member = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom1, + member.getId()); + final GoalRoomPendingMember goalRoom2Member = new GoalRoomPendingMember(GoalRoomRole.FOLLOWER, goalRoom2, + member.getId()); goalRoom3.start(); goalRoom4.complete(); - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); - when(goalRoomRepository.findByMemberAndStatus(any(), any())) - .thenReturn(List.of(goalRoom1, goalRoom2)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findByMemberAndStatus(any(), any())) + .willReturn(List.of(goalRoom1, goalRoom2)); + given(goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom1, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom1Leader)); + given(goalRoomPendingMemberRepository.findLeaderByGoalRoomAndRole(goalRoom3, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom2Leader)); + given(memberRepository.findWithMemberProfileAndImageById(1L)) + .willReturn(Optional.of(creator)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); + given(goalRoomPendingMemberRepository.findByGoalRoom(goalRoom1)) + .willReturn(List.of(goalRoom1Leader, goalRoom1Member)); + given(goalRoomPendingMemberRepository.findByGoalRoom(goalRoom2)) + .willReturn(List.of(goalRoom2Leader, goalRoom2Member)); final List expected = List.of( new MemberGoalRoomForListResponse(1L, "골룸", "RECRUITING", 2, @@ -810,32 +841,41 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV void 사용자_골룸_목록_중_진행_중인_상태만_조회한다() throws MalformedURLException { final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom1 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom2 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom3 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom4 = 골룸을_생성한다(creator, roadmapContent); final Member member = 사용자를_생성한다(2L); - goalRoom1.join(member); - goalRoom2.join(member); - goalRoom3.join(member); - goalRoom4.join(member); goalRoom3.start(); goalRoom4.start(); - goalRoom3.addAllGoalRoomMembers(List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator), - new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, member))); - goalRoom4.addAllGoalRoomMembers(List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator), - new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, member))); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); - when(goalRoomRepository.findByMemberAndStatus(any(), any())) - .thenReturn(List.of(goalRoom3, goalRoom4)); + final GoalRoomMember goalRoom3Leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, + creator.getId()); + final GoalRoomMember goalRoom3Member = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, + member.getId()); + final GoalRoomMember goalRoom4Leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom4, + creator.getId()); + final GoalRoomMember goalRoom4Member = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom4, + member.getId()); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findByMemberAndStatus(any(), any())) + .willReturn(List.of(goalRoom3, goalRoom4)); + given(memberRepository.findWithMemberProfileAndImageById(1L)) + .willReturn(Optional.of(creator)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom3, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom3Leader)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom4, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom4Leader)); + given(goalRoomMemberRepository.findByGoalRoom(goalRoom3)) + .willReturn(List.of(goalRoom3Leader, goalRoom3Member)); + given(goalRoomMemberRepository.findByGoalRoom(goalRoom4)) + .willReturn(List.of(goalRoom4Leader, goalRoom4Member)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -865,32 +905,41 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV void 사용자_골룸_목록_중_종료된_상태만_조회한다() throws MalformedURLException { final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom1 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom2 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom3 = 골룸을_생성한다(creator, roadmapContent); final GoalRoom goalRoom4 = 골룸을_생성한다(creator, roadmapContent); final Member member = 사용자를_생성한다(2L); - goalRoom1.join(member); - goalRoom2.join(member); - goalRoom3.join(member); - goalRoom4.join(member); + + final GoalRoomMember goalRoom3Leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, + creator.getId()); + final GoalRoomMember goalRoom3Member = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, + member.getId()); + final GoalRoomMember goalRoom4Leader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom4, + creator.getId()); + final GoalRoomMember goalRoom4Member = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom4, + member.getId()); goalRoom3.complete(); goalRoom4.complete(); - goalRoom3.addAllGoalRoomMembers(List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator), - new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, member))); - goalRoom4.addAllGoalRoomMembers(List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom3, creator), - new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom3, member))); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(member)); - when(goalRoomRepository.findByMemberAndStatus(any(), any())) - .thenReturn(List.of(goalRoom3, goalRoom4)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findByMemberAndStatus(any(), any())) + .willReturn(List.of(goalRoom3, goalRoom4)); + given(memberRepository.findWithMemberProfileAndImageById(1L)) + .willReturn(Optional.of(creator)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom3, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom3Leader)); + given(goalRoomMemberRepository.findLeaderByGoalRoomAndRole(goalRoom4, GoalRoomRole.LEADER)) + .willReturn(Optional.of(goalRoom4Leader)); + given(goalRoomMemberRepository.findByGoalRoom(goalRoom3)) + .willReturn(List.of(goalRoom3Leader, goalRoom3Member)); + given(goalRoomMemberRepository.findByGoalRoom(goalRoom4)) + .willReturn(List.of(goalRoom4Leader, goalRoom4Member)); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -921,15 +970,22 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - when(goalRoomMemberRepository.findGoalRoomMember(anyLong(), any())) - .thenReturn(Optional.of(goalRoomMember)); - when(goalRoomRepository.findByIdWithNodes(1L)) - .thenReturn(Optional.of(goalRoom)); + creator.getId()); + given(goalRoomRepository.findByIdWithNodes(1L)) + .willReturn(Optional.of(goalRoom)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomMemberRepository.findByGoalRoomIdAndMemberId(anyLong(), any())) + .willReturn(Optional.of(goalRoomMember)); + given(roadmapNodeRepository.findByIdWithRoadmapNodeImages(1L)) + .willReturn(Optional.of(roadmapNodes.get(0))); + given(roadmapNodeRepository.findByIdWithRoadmapNodeImages(2L)) + .willReturn(Optional.of(roadmapNodes.get(1))); given(fileService.generateUrl(anyString(), any())) .willReturn(new URL("http://example.com/serverFilePath")); @@ -954,13 +1010,16 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV // given final Member creator = 크리에이터를_생성한다(); final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - when(goalRoomRepository.findByIdWithNodes(1L)) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findGoalRoomMember(anyLong(), any())) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findByIdWithNodes(1L)) + .willReturn(Optional.of(goalRoom)); + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomMemberRepository.findByGoalRoomIdAndMemberId(anyLong(), any())) + .willReturn(Optional.empty()); // expected assertThatThrownBy(() -> goalRoomReadService.findAllGoalRoomNodes(1L, "identifier")) @@ -970,206 +1029,14 @@ THIRTY_DAY_LATER, new MemberResponse(creator.getId(), creator.getNickname().getV @Test void 골룸의_노드_조회시_존재하지_않는_골룸이면_예외가_발생한다() { // given - when(goalRoomRepository.findByIdWithNodes(1L)) - .thenReturn(Optional.empty()); + given(goalRoomRepository.findByIdWithNodes(1L)) + .willReturn(Optional.empty()); // expected assertThatThrownBy(() -> goalRoomReadService.findAllGoalRoomNodes(1L, "identifier")) .isInstanceOf(NotFoundException.class); } - @Test - void 진행중인_골룸의_인증피드를_전체_조회한다() throws MalformedURLException { - // given - final Member creator = 크리에이터를_생성한다(); - final Member follower = 사용자를_생성한다(2L); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - goalRoom.start(); - - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - follower); - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - - final CheckFeed checkFeed1 = 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, - goalRoomMember1); - final CheckFeed checkFeed2 = 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, - goalRoomMember1); - final CheckFeed checkFeed3 = 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, - goalRoomMember2); - - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willReturn(Optional.of(goalRoom)); - given(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .willReturn(Optional.of(goalRoomMember1)); - given(checkFeedRepository.findByRunningGoalRoomRoadmapNodeWithMemberAndMemberImage(any())) - .willReturn(List.of(checkFeed3, checkFeed2, checkFeed1)); - given(fileService.generateUrl(anyString(), any())) - .willReturn(new URL("http://example.com/serverFilePath")); - - // when - final List responses = goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L); - - // then - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( - new MemberResponse(1L, "코끼리", "http://example.com/serverFilePath"), - new CheckFeedResponse(1L, "http://example.com/serverFilePath", "description1", LocalDate.now())); - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( - new MemberResponse(1L, "코끼리", "http://example.com/serverFilePath"), - new CheckFeedResponse(2L, "http://example.com/serverFilePath", "description2", LocalDate.now())); - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse3 = new GoalRoomCheckFeedResponse( - new MemberResponse(2L, "name1", "http://example.com/serverFilePath"), - new co.kirikiri.service.dto.goalroom.response.CheckFeedResponse(3L, "http://example.com/serverFilePath", "description3", LocalDate.now())); - final List expected = List.of(goalRoomCheckFeedResponse3, - goalRoomCheckFeedResponse2, goalRoomCheckFeedResponse1); - - assertThat(responses).usingRecursiveComparison() - .ignoringFields("checkFeed.id", "checkFeed.createdAt") - .isEqualTo(expected); - } - - @Test - void 모집중인_골룸의_인증피드를_조회시_빈_값을_반환한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willReturn(Optional.of(goalRoom)); - given(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .willReturn(Optional.of(goalRoomMember)); - - // when - final List responses = goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L); - - // then - final List expected = Collections.emptyList(); - - assertThat(responses).isEqualTo(expected); - } - - @Test - void 종료된_골룸의_인증피드를_전체_조회시_모든_기간의_인증피드를_대상으로_반환한다() throws MalformedURLException { - // given - final Member creator = 크리에이터를_생성한다(); - final Member follower = 사용자를_생성한다(2L); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - goalRoom.complete(); - - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - follower); - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - - final CheckFeed checkFeed1 = 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, - goalRoomMember1); - final CheckFeed checkFeed2 = 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, - goalRoomMember1); - final CheckFeed checkFeed3 = 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, - goalRoomMember2); - - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willReturn(Optional.of(goalRoom)); - given(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .willReturn(Optional.of(goalRoomMember1)); - given(checkFeedRepository.findByGoalRoomWithMemberAndMemberImage(any())) - .willReturn(List.of(checkFeed3, checkFeed2, checkFeed1)); - given(fileService.generateUrl(anyString(), any())) - .willReturn(new URL("http://example.com/serverFilePath")); - - // when - final List responses = goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L); - - // then - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse1 = new GoalRoomCheckFeedResponse( - new MemberResponse(1L, "코끼리", "http://example.com/serverFilePath"), - new CheckFeedResponse(1L, "http://example.com/serverFilePath", "description1", LocalDate.now())); - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse2 = new GoalRoomCheckFeedResponse( - new MemberResponse(1L, "코끼리", "http://example.com/serverFilePath"), - new CheckFeedResponse(2L, "http://example.com/serverFilePath", "description2", LocalDate.now())); - final GoalRoomCheckFeedResponse goalRoomCheckFeedResponse3 = new GoalRoomCheckFeedResponse( - new MemberResponse(2L, "name1", "http://example.com/serverFilePath"), - new co.kirikiri.service.dto.goalroom.response.CheckFeedResponse(3L, "http://example.com/serverFilePath", "description3", LocalDate.now())); - final List expected = List.of(goalRoomCheckFeedResponse3, - goalRoomCheckFeedResponse2, goalRoomCheckFeedResponse1); - - assertThat(responses).usingRecursiveComparison() - .ignoringFields("checkFeed.id", "checkFeed.createdAt") - .isEqualTo(expected); - } - - @Test - void 골룸의_인증피드를_전체_조회시_현재_진행중인_노드가_없으면_빈_리스트를_반환한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final Member follower = 사용자를_생성한다(2L); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 진행중인_노드가_없는_골룸을_생성한다(creator, roadmapContent); - - final GoalRoomMember goalRoomMember1 = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, - creator); - final GoalRoomMember goalRoomMember2 = new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), goalRoom, - follower); - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - - 인증피드를_생성한다("serverFilePath1", "description1", goalRoomRoadmapNode, goalRoomMember1); - 인증피드를_생성한다("serverFilePath2", "description2", goalRoomRoadmapNode, goalRoomMember1); - 인증피드를_생성한다("serverFilePath3", "description3", goalRoomRoadmapNode, goalRoomMember2); - - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willReturn(Optional.of(goalRoom)); - given(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .willReturn(Optional.of(goalRoomMember1)); - - // when - final List responses = goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L); - - // then - assertThat(responses).isEmpty(); - } - - @Test - void 골룸의_인증피드를_전체_조회할_때_존재하지_않는_골룸이면_예외가_발생한다() { - // given - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")); - - // when - // then - assertThatThrownBy(() -> goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸의_인증피드를_전체_조회할_때_골룸에_참여하지_않은_회원이면_예외가_발생한다() { - // given - final Member creator = 크리에이터를_생성한다(); - final Roadmap roadmap = 로드맵을_생성한다(creator); - final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId()); - final GoalRoom goalRoom = 골룸을_생성한다(creator, roadmapContent); - - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willReturn(Optional.of(goalRoom)); - given(goalRoomRepository.findByIdWithNodes(anyLong())) - .willThrow(new ForbiddenException("골룸에 참여하지 않은 회원입니다.")); - - // when - // then - assertThatThrownBy(() -> goalRoomReadService.findGoalRoomCheckFeeds("cokirikiri", 1L)) - .isInstanceOf(ForbiddenException.class); - } - private Member 크리에이터를_생성한다() { final MemberImage memberImage = new MemberImage("originalFileName", "default-member-image", ImageContentType.JPG); @@ -1191,15 +1058,10 @@ null, new EncryptedPassword(new Password("password1")), new Nickname("name1"), RoadmapStatus.CREATED, creator.getId(), category, new RoadmapTags(new ArrayList<>())); } - private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId) { - final List roadmapNodes = 로드맵_노드들을_생성한다(); - return new RoadmapContent("로드맵 본문", roadmapId, new RoadmapNodes(roadmapNodes)); - } - private List 로드맵_노드들을_생성한다() { - final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "로드맵 1주차", "로드맵 1주차 내용"); roadmapNode1.addImages(new RoadmapNodeImages(노드_이미지들을_생성한다())); - final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); + final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "로드맵 2주차", "로드맵 2주차 내용"); return List.of(roadmapNode1, roadmapNode2); } @@ -1210,68 +1072,53 @@ null, new EncryptedPassword(new Password("password1")), new Nickname("name1"), ); } + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + return new RoadmapContent("로드맵 본문", roadmapId, new RoadmapNodes(roadmapNodes)); + } + private GoalRoom 골룸을_생성한다(final Member member, final RoadmapContent roadmapContent) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), - roadmapContent, member); final List roadmapNodes = roadmapContent.getNodes().getValues(); final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - 1L, new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode); + 1L, new Period(TODAY, TEN_DAY_LATER), 10, firstRoadmapNode.getId()); final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - 2L, new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), 2, secondRoadmapNode); + 2L, new Period(TWENTY_DAY_LAYER, THIRTY_DAY_LATER), 2, secondRoadmapNode.getId()); final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoom; - } - private GoalRoom 진행중인_노드가_없는_골룸을_생성한다(final Member member, final RoadmapContent roadmapContent) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), - roadmapContent, member); - final List roadmapNodes = roadmapContent.getNodes().getValues(); - - final RoadmapNode firstRoadmapNode = roadmapNodes.get(0); - final GoalRoomRoadmapNode firstGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - 1L, new Period(TEN_DAY_LATER, TWENTY_DAY_LAYER), 10, firstRoadmapNode); - - final RoadmapNode secondRoadmapNode = roadmapNodes.get(1); - final GoalRoomRoadmapNode secondGoalRoomRoadmapNode = new GoalRoomRoadmapNode( - 2L, new Period(THIRTY_DAY_LATER, THIRTY_DAY_LATER.plusDays(10)), 2, secondRoadmapNode); + return new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(10), roadmapContent.getId(), + goalRoomRoadmapNodes); + } - final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( - List.of(firstGoalRoomRoadmapNode, secondGoalRoomRoadmapNode)); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoom; + private static GoalRoomResponse 예상하는_골룸_응답을_생성한다() { + final List goalRoomNodeResponses = List.of( + new GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", TODAY, TEN_DAY_LATER, 10), + new GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", TWENTY_DAY_LAYER, THIRTY_DAY_LATER, 2)); + return new GoalRoomResponse("골룸", 1, 10, goalRoomNodeResponses, 31); } - private CheckFeed 인증피드를_생성한다(final String serverFilePath, final String description, - final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember goalRoomMember) { - return new CheckFeed(serverFilePath, ImageContentType.PNG, "fileName", description, goalRoomRoadmapNode, - goalRoomMember, LocalDateTime.now()); + private static GoalRoomCertifiedResponse 예상하는_로그인된_사용자의_골룸_응답을_생성한다(final Boolean isJoined, + final int currentMemberCount) { + final List goalRoomNodeResponses = List.of( + new GoalRoomRoadmapNodeResponse(1L, "로드맵 1주차", TODAY, TEN_DAY_LATER, 10), + new GoalRoomRoadmapNodeResponse(2L, "로드맵 2주차", TWENTY_DAY_LAYER, THIRTY_DAY_LATER, 2)); + return new GoalRoomCertifiedResponse("골룸", currentMemberCount, 10, goalRoomNodeResponses, 31, isJoined); } - private List 인증_피드_목록을_생성한다(final GoalRoomRoadmapNode node, final Member member, - final GoalRoom goalRoom) { + private List 대시보드_인증_피드_목록_응답을_생성한다() { return List.of( - new CheckFeed("filePath1", ImageContentType.JPEG, "originalFileName1", "인증 피드 설명", node, - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member), - LocalDateTime.now()), - new CheckFeed("filePath2", ImageContentType.JPEG, "originalFileName2", "인증 피드 설명", node, - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member), - LocalDateTime.now()), - new CheckFeed("filePath3", ImageContentType.JPEG, "originalFileName3", "인증 피드 설명", node, - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member), - LocalDateTime.now()), - new CheckFeed("filePath4", ImageContentType.JPEG, "originalFileName4", "인증 피드 설명", node, - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member), - LocalDateTime.now()), - new CheckFeed("filePath5", ImageContentType.JPEG, "originalFileName5", "인증 피드 설명", node, - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, member), - LocalDateTime.now()) + new DashBoardCheckFeedResponse(1L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(2L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(3L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()), + new DashBoardCheckFeedResponse(4L, "http://example.com/serverFilePath", "인증 피드 설명", + LocalDate.now()) ); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomSchedulerTest.java b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomSchedulerTest.java similarity index 80% rename from backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomSchedulerTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomSchedulerTest.java index 8311fc144..5b1a4eb37 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomSchedulerTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/goalroom/service/GoalRoomSchedulerTest.java @@ -1,21 +1,32 @@ -package co.kirikiri.service; +package co.kirikiri.goalroom.service; -import static co.kirikiri.domain.goalroom.GoalRoomStatus.RECRUITING; -import static co.kirikiri.domain.goalroom.GoalRoomStatus.RUNNING; +import static co.kirikiri.goalroom.domain.GoalRoomStatus.RECRUITING; +import static co.kirikiri.goalroom.domain.GoalRoomStatus.RUNNING; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomPendingMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomPendingMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.scheduler.GoalRoomScheduler; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -23,9 +34,6 @@ import co.kirikiri.member.domain.vo.Identifier; import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -35,17 +43,12 @@ import co.kirikiri.roadmap.domain.RoadmapNodeImages; import co.kirikiri.roadmap.domain.RoadmapNodes; import co.kirikiri.roadmap.domain.RoadmapTags; -import co.kirikiri.service.scheduler.GoalRoomScheduler; 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 java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; @ExtendWith(MockitoExtension.class) class GoalRoomSchedulerTest { @@ -58,10 +61,10 @@ class GoalRoomSchedulerTest { private GoalRoomRepository goalRoomRepository; @Mock - private GoalRoomMemberRepository goalRoomMemberRepository; + private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; @Mock - private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository; + private GoalRoomMemberRepository goalRoomMemberRepository; @InjectMocks private GoalRoomScheduler goalRoomScheduler; @@ -80,14 +83,11 @@ class GoalRoomSchedulerTest { final Member follower2 = 사용자를_생성한다(3L, "identifier2", "password3!", "name2", "kirikiri@email.com"); final Member follower3 = 사용자를_생성한다(4L, "identifier3", "password4!", "name3", "kirikiri@email.com"); - 골룸_대기자를_생성한다(goalRoom2, creator, GoalRoomRole.FOLLOWER); + 골룸_대기자를_생성한다(goalRoom1, creator, GoalRoomRole.LEADER); + 골룸_대기자를_생성한다(goalRoom2, creator, GoalRoomRole.LEADER); 골룸_대기자를_생성한다(goalRoom1, follower1, GoalRoomRole.FOLLOWER); 골룸_대기자를_생성한다(goalRoom1, follower2, GoalRoomRole.FOLLOWER); - goalRoom1.join(follower1); - goalRoom1.join(follower2); - goalRoom2.join(follower3); - when(goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan(LocalDate.now())) .thenReturn(List.of(goalRoom1)); @@ -95,6 +95,9 @@ class GoalRoomSchedulerTest { goalRoomScheduler.startGoalRooms(); // then + verify(goalRoomMemberRepository, times(1)).saveAllInBatch(any()); + verify(goalRoomPendingMemberRepository, times(1)).deleteAllInBatch(any()); + assertAll( () -> assertThat(goalRoom1.getStatus()).isEqualTo(RUNNING), () -> assertThat(goalRoom2.getStatus()).isEqualTo(RECRUITING) @@ -115,10 +118,6 @@ class GoalRoomSchedulerTest { final Member follower2 = 사용자를_생성한다(3L, "identifier2", "password3!", "name2", "kirikiri@email.com"); final Member follower3 = 사용자를_생성한다(4L, "identifier3", "password4!", "name3", "kirikiri@email.com"); - goalRoom1.join(follower1); - goalRoom1.join(follower2); - goalRoom2.join(follower3); - when(goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan(LocalDate.now())) .thenReturn(Collections.emptyList()); @@ -126,7 +125,8 @@ class GoalRoomSchedulerTest { goalRoomScheduler.startGoalRooms(); // then - verify(goalRoomPendingMemberRepository, never()).deleteAllByIdIn(anyList()); + verify(goalRoomMemberRepository, never()).saveAllInBatch(any()); + verify(goalRoomPendingMemberRepository, never()).deleteAllByIdIn(any()); assertAll( () -> assertThat(goalRoom1.getStatus()).isEqualTo(RECRUITING), @@ -169,22 +169,20 @@ class GoalRoomSchedulerTest { private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final RoadmapContent roadmapContent, final Integer limitedMemberCount) { - final GoalRoom goalRoom = new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), - new LimitedMemberCount(limitedMemberCount), roadmapContent, creator); - goalRoom.addAllGoalRoomRoadmapNodes(골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); - return goalRoom; + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), 골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); } private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다(final RoadmapNodes roadmapNodes) { return new GoalRoomRoadmapNodes(List.of( - new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0)), + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0).getId()), new GoalRoomRoadmapNode(new Period(TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER), 5, - roadmapNodes.getValues().get(1))) + roadmapNodes.getValues().get(1).getId())) ); } private GoalRoomPendingMember 골룸_대기자를_생성한다(final GoalRoom goalRoom, final Member follower, final GoalRoomRole role) { - return new GoalRoomPendingMember(role, LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, follower); + return new GoalRoomPendingMember(1L, role, LocalDateTime.of(2023, 7, 19, 12, 0, 0), goalRoom, follower.getId()); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/infra/AmazonS3FileServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/infra/AmazonS3FileServiceTest.java index d0ee3c329..25324e4c3 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/infra/AmazonS3FileServiceTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/infra/AmazonS3FileServiceTest.java @@ -10,11 +10,14 @@ import co.kirikiri.common.exception.ServerException; import co.kirikiri.common.infra.AmazonS3FileService; import co.kirikiri.common.infra.CloudFrontService; -import co.kirikiri.service.dto.FileInformation; +import co.kirikiri.common.service.dto.FileInformation; import com.amazonaws.AmazonServiceException; import com.amazonaws.Protocol; import com.amazonaws.SdkClientException; import com.amazonaws.services.s3.AmazonS3; +import java.io.FileInputStream; +import java.net.MalformedURLException; +import java.net.URL; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -22,9 +25,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod; -import java.io.FileInputStream; -import java.net.MalformedURLException; -import java.net.URL; @ExtendWith(MockitoExtension.class) class AmazonS3FileServiceTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/AuthenticationIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/AuthenticationIntegrationTest.java index 44b57c71f..08ba12f04 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/AuthenticationIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/AuthenticationIntegrationTest.java @@ -9,15 +9,15 @@ import co.kirikiri.auth.service.dto.request.LoginRequest; import co.kirikiri.auth.service.dto.request.ReissueTokenRequest; import co.kirikiri.auth.service.dto.response.AuthenticationResponse; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.integration.helper.InitIntegrationTest; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import java.util.List; class AuthenticationIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCheckFeedIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCheckFeedIntegrationTest.java new file mode 100644 index 000000000..16662d621 --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCheckFeedIntegrationTest.java @@ -0,0 +1,270 @@ +package co.kirikiri.integration; + +import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; +import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_참가_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸의_사용자_정보를_정렬_기준없이_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.기본_골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.인증_피드_등록; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.인증_피드_전체_조회_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import co.kirikiri.auth.service.dto.request.LoginRequest; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.checkfeed.service.dto.response.GoalRoomCheckFeedResponse; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.integration.helper.InitIntegrationTest; +import co.kirikiri.member.service.dto.request.GenderType; +import co.kirikiri.member.service.dto.request.MemberJoinRequest; +import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import io.restassured.common.mapper.TypeRef; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockMultipartFile; + +class GoalRoomCheckFeedIntegrationTest extends InitIntegrationTest { + + @Test + void 인증_피드_등록을_요청한다() throws IOException { + //given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, + 골룸_노드_별_기간_요청); + final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/jpeg", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); + + //when + final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); + + //then + assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + } + + @Test + void 인증용_사진이_없는_경우_인증_피드_등록이_실패한다() throws IOException { + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, + 골룸_노드_별_기간_요청); + final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + + final MockMultipartFile 빈_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/jpeg", "".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(빈_이미지_객체, "image description"); + + //when + final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 빈_이미지_객체, + 인증_피드_등록_요청, 기본_로그인_토큰); + final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); + + //then + assertAll( + () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), + () -> assertThat(예외_메세지.message()).isEqualTo("인증 피드 등록 시 이미지가 반드시 포함되어야 합니다.") + ); + } + + @Test + void 인증용_사진의_확장자가_허용되지_않는_경우_인증_피드_등록이_실패한다() throws IOException { + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, + 골룸_노드_별_기간_요청); + final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.gif", + "image/gif", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); + + //when + final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, + 인증_피드_등록_요청, 기본_로그인_토큰); + + final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); + + //then + assertAll( + () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), + () -> assertThat(예외_메세지.message()).isEqualTo("허용되지 않는 확장자입니다.") + ); + } + + @Test + void 하루에_두_번_이상_인증_피드_등록을_요청하는_경우_실패한다() throws IOException { + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, + 골룸_노드_별_기간_요청); + final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/webp", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); + 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); + + //when + final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); + final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); + + //then + final List 골룸_사용자_응답 = 골룸의_사용자_정보를_정렬_기준없이_조회(골룸_아이디, 기본_로그인_토큰).as(new TypeRef<>() { + }); + + assertAll( + () -> assertThat(인증_피드_등록_응답.statusCode()) + .isEqualTo(HttpStatus.BAD_REQUEST.value()), + () -> assertThat(예외_메세지.message()).isEqualTo("이미 오늘 인증 피드를 등록하였습니다."), + () -> assertThat(골룸_사용자_응답.get(0).accomplishmentRate()) + .isEqualTo(100 / (double) 정상적인_골룸_노드_인증_횟수) + ); + } + + @Test + void 진행_중인_노드의_허용된_인증_횟수_이상_요청할_경우_실패한다() throws IOException { + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 1, 오늘, 십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, + 골룸_노드_별_기간_요청); + final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/webp", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); + 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); + + //when + final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); + + final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); + + //then + assertAll( + () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), + () -> assertThat(예외_메세지.message()).isEqualTo("이번 노드에는 최대 " + 1 + "번만 인증 피드를 등록할 수 있습니다.") + ); + } + + @Test + void 골룸의_인증피드를_전체_조회한다() throws IOException { + // given + final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", + "follower", GenderType.FEMALE, DEFAULT_EMAIL); + final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); + 회원가입(팔로워_회원_가입_요청); + final String 팔로워_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(팔로워_로그인_요청).accessToken()); + + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); + + 골룸_참가_요청(기본_골룸_아이디, 팔로워_액세스_토큰); + 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/jpeg", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청1 = new CheckFeedRequest(가짜_이미지_객체, "image description1"); + final CheckFeedRequest 인증_피드_등록_요청2 = new CheckFeedRequest(가짜_이미지_객체, "image description2"); + + 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청1, 기본_로그인_토큰); + 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청2, 팔로워_액세스_토큰); + + //when + final List 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(팔로워_액세스_토큰, 기본_골룸_아이디) + .as(new TypeRef<>() { + }); + + // then + assertThat(인증_피드_전체_조회_요청에_대한_응답.get(0).checkFeed().description()).isEqualTo(인증_피드_등록_요청2.description()); + assertThat(인증_피드_전체_조회_요청에_대한_응답.get(1).checkFeed().description()).isEqualTo(인증_피드_등록_요청1.description()); + } + + @Test + void 골룸의_인증피드를_전체_조회시_존재하지_않는_골룸인_경우_예외가_발생한다() throws IOException { + // given + //when + final Long 존재하지_않는_골룸_아이디 = 1L; + final ExtractableResponse 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(기본_로그인_토큰, 존재하지_않는_골룸_아이디); + + // then + final ErrorResponse 인증_피드_전체_조회_응답_바디 = jsonToClass(인증_피드_전체_조회_요청에_대한_응답.asString(), new TypeReference<>() { + }); + assertThat(인증_피드_전체_조회_요청에_대한_응답.statusCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); + assertThat(인증_피드_전체_조회_응답_바디).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸의_인증피드를_전체_조회시_골룸에_참여하지_않은_사용자면_예외가_발생한다() throws IOException { + // given + final MemberJoinRequest 다른_회원_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", + "follower", GenderType.FEMALE, DEFAULT_EMAIL); + final LoginRequest 다른_회원_로그인_요청 = new LoginRequest(다른_회원_회원_가입_요청.identifier(), 다른_회원_회원_가입_요청.password()); + 회원가입(다른_회원_회원_가입_요청); + final String 다른_회원_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(다른_회원_로그인_요청).accessToken()); + + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); + 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); + + final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", + "image/jpeg", "tempImage".getBytes()); + final CheckFeedRequest 인증_피드_등록_요청1 = new CheckFeedRequest(가짜_이미지_객체, "image description1"); + + 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청1, 기본_로그인_토큰); + + //when + final ExtractableResponse 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(다른_회원_액세스_토큰, 기본_골룸_아이디); + + // then + final ErrorResponse 인증_피드_전체_조회_응답_바디 = jsonToClass(인증_피드_전체_조회_요청에_대한_응답.asString(), new TypeReference<>() { + }); + assertThat(인증_피드_전체_조회_요청에_대한_응답.statusCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); + assertThat(인증_피드_전체_조회_응답_바디).isEqualTo(new ErrorResponse("골룸에 참여하지 않은 회원입니다.")); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCreateIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCreateIntegrationTest.java index aec2d0ebc..cf4323876 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCreateIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomCreateIntegrationTest.java @@ -2,41 +2,51 @@ import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; -import static co.kirikiri.integration.fixture.MemberAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_나가기_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_목록_조회_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_참가_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸의_사용자_정보를_정렬_기준없이_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.사용자의_특정_골룸_정보를_조회한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.이십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_사용자_자신의_정보_조회_요청; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import co.kirikiri.auth.service.dto.request.LoginRequest; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.service.dto.GoalRoomFilterTypeDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; import co.kirikiri.member.service.dto.response.MemberInformationResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.ErrorResponse; -import co.kirikiri.service.dto.goalroom.GoalRoomFilterTypeDto; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; import com.fasterxml.jackson.core.JsonProcessingException; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.mock.web.MockMultipartFile; import java.io.IOException; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; class GoalRoomCreateIntegrationTest extends InitIntegrationTest { @@ -269,150 +279,6 @@ class GoalRoomCreateIntegrationTest extends InitIntegrationTest { ); } - @Test - void 인증_피드_등록을_요청한다() throws IOException { - //given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, - 골룸_노드_별_기간_요청); - final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/jpeg", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); - - //when - final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); - - //then - assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.CREATED.value()); - } - - @Test - void 인증용_사진이_없는_경우_인증_피드_등록이_실패한다() throws IOException { - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, - 골룸_노드_별_기간_요청); - final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - - final MockMultipartFile 빈_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/jpeg", "".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(빈_이미지_객체, "image description"); - - //when - final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 빈_이미지_객체, - 인증_피드_등록_요청, 기본_로그인_토큰); - final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); - - //then - assertAll( - () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), - () -> assertThat(예외_메세지.message()).isEqualTo("인증 피드 등록 시 이미지가 반드시 포함되어야 합니다.") - ); - } - - @Test - void 인증용_사진의_확장자가_허용되지_않는_경우_인증_피드_등록이_실패한다() throws IOException { - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, - 골룸_노드_별_기간_요청); - final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.gif", - "image/gif", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); - - //when - final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, - 인증_피드_등록_요청, 기본_로그인_토큰); - - final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); - - //then - assertAll( - () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), - () -> assertThat(예외_메세지.message()).isEqualTo("허용되지 않는 확장자입니다.") - ); - } - - @Test - void 하루에_두_번_이상_인증_피드_등록을_요청하는_경우_실패한다() throws IOException { - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 오늘, 십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, - 골룸_노드_별_기간_요청); - final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/webp", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); - 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); - - //when - final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); - final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); - - //then - final List 골룸_사용자_응답 = 골룸의_사용자_정보를_정렬_기준없이_조회(골룸_아이디, 기본_로그인_토큰).as(new TypeRef<>() { - }); - - assertAll( - () -> assertThat(인증_피드_등록_응답.statusCode()) - .isEqualTo(HttpStatus.BAD_REQUEST.value()), - () -> assertThat(예외_메세지.message()).isEqualTo("이미 오늘 인증 피드를 등록하였습니다."), - () -> assertThat(골룸_사용자_응답.get(0).accomplishmentRate()) - .isEqualTo(100 / (double) 정상적인_골룸_노드_인증_횟수) - ); - } - - @Test - void 진행_중인_노드의_허용된_인증_횟수_이상_요청할_경우_실패한다() throws IOException { - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 1, 오늘, 십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(기본_로드맵_아이디, 정상적인_골룸_이름, 정상적인_골룸_제한_인원, - 골룸_노드_별_기간_요청); - final Long 골룸_아이디 = 골룸을_생성하고_아이디를_반환한다(골룸_생성_요청, 기본_로그인_토큰); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/webp", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청 = new CheckFeedRequest(가짜_이미지_객체, "image description"); - 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); - - //when - final ExtractableResponse 인증_피드_등록_응답 = 인증_피드_등록(골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청, 기본_로그인_토큰); - - final ErrorResponse 예외_메세지 = 인증_피드_등록_응답.as(ErrorResponse.class); - - //then - assertAll( - () -> assertThat(인증_피드_등록_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()), - () -> assertThat(예외_메세지.message()).isEqualTo("이번 노드에는 최대 " + 1 + "번만 인증 피드를 등록할 수 있습니다.") - ); - } - @Test void 이미_참여한_골룸에_참가_요청을_보내면_예외가_발생한다() throws IOException { //given @@ -438,158 +304,6 @@ class GoalRoomCreateIntegrationTest extends InitIntegrationTest { ); } - @Test - void 정상적으로_골룸에_투두리스트를_추가한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); - - final GoalRoomTodoRequest 골룸_투두리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); - - // when - final ExtractableResponse 골룸_투두리스트_추가 = 골룸_투두리스트_추가(기본_로그인_토큰, 골룸_아이디, 골룸_투두리스트_추가_요청); - - // then - assertThat(골룸_투두리스트_추가.statusCode()).isEqualTo(HttpStatus.CREATED.value()); - final String header = 골룸_투두리스트_추가.response() - .header(HttpHeaders.LOCATION); - assertThat(header).contains("/api/goal-rooms/1/todos/" + header.substring(24)); - } - - @Test - void 골룸에_팔로워가_투두_리스트를_추가할때_예외를_던진다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final MemberJoinRequest 팔로워_회원가입_요청 = new MemberJoinRequest("identifier2", "password12!@#$%", "follower", - GenderType.MALE, DEFAULT_EMAIL); - 회원가입(팔로워_회원가입_요청); - final String 팔로워_로그인_토큰 = String.format(BEARER_TOKEN_FORMAT, - 로그인(new LoginRequest(팔로워_회원가입_요청.identifier(), 팔로워_회원가입_요청.password())).accessToken()); - - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 로드맵_응답.roadmapId(), 로드맵_응답.content().nodes().get(0).id()); - - final GoalRoomTodoRequest 골룸_투두리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); - - // when - final ExtractableResponse 골룸_투두리스트_추가 = 골룸_투두리스트_추가(팔로워_로그인_토큰, 골룸_아이디, 골룸_투두리스트_추가_요청); - - // then - final ErrorResponse 골룸_투두리스트_추가_바디 = 골룸_투두리스트_추가.as(new TypeRef<>() { - }); - - assertThat(골룸_투두리스트_추가.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); - assertThat(골룸_투두리스트_추가_바디).isEqualTo(new ErrorResponse("골룸의 리더만 투두리스트를 추가할 수 있습니다.")); - } - - @Test - void 종료된_골룸에_투두_리스트를_추가할때_예외를_던진다() throws IOException { - //given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); - testTransactionService.골룸을_완료시킨다(골룸_아이디); - - final GoalRoomTodoRequest 골룸_투두_리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); - - //when - final ExtractableResponse 골룸_추가_응답 = 골룸_투두리스트_추가(기본_로그인_토큰, 골룸_아이디, 골룸_투두_리스트_추가_요청); - - //then - final ErrorResponse 골룸_추가_응답_바디 = 골룸_추가_응답.as(new TypeRef<>() { - }); - - assertThat(골룸_추가_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); - assertThat(골룸_추가_응답_바디).isEqualTo(new ErrorResponse("이미 종료된 골룸입니다.")); - } - - @Test - void 골룸_투두_리스트를_체크한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); - - // when - final GoalRoomToDoCheckResponse 골룸_투두리스트_체크_응답값 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디) - .as(new TypeRef<>() { - }); - - // then - final GoalRoomToDoCheckResponse 예상하는_골룸_투두리스트_체크_응답값 = new GoalRoomToDoCheckResponse(true); - assertThat(골룸_투두리스트_체크_응답값) - .isEqualTo(예상하는_골룸_투두리스트_체크_응답값); - } - - @Test - void 골룸_투두리스트_체크를_해제한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); - - 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디); - - // when - final GoalRoomToDoCheckResponse 두번째_골룸_투두리스트_체크_응답값 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디) - .as(new TypeRef<>() { - }); - - // then - final GoalRoomToDoCheckResponse 예상하는_골룸_투두리스트_체크_응답값 = new GoalRoomToDoCheckResponse(false); - assertThat(두번째_골룸_투두리스트_체크_응답값) - .isEqualTo(예상하는_골룸_투두리스트_체크_응답값); - } - - @Test - void 골룸_투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() { - // given - // when - final ErrorResponse 에러_응답 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 1L, 1L) - .as(new TypeRef<>() { - }); - - // then - assertThat(에러_응답) - .isEqualTo(new ErrorResponse("골룸이 존재하지 않습니다. goalRoomId = 1")); - } - - @Test - void 골룸_투두리스트_체크시_사용자가_없으면_예외가_발생한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); - 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); - final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); - - final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); - 회원가입(팔로워_회원_가입_요청); - final String 팔로워_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(팔로워_로그인_요청).accessToken()); - - // when - final ErrorResponse 에러_응답 = 골룸_투두리스트를_체크한다(팔로워_액세스_토큰, 골룸_아이디, 투두_아이디) - .as(new TypeRef<>() { - }); - - // then - assertThat(에러_응답) - .isEqualTo(new ErrorResponse("골룸에 사용자가 존재하지 않습니다. goalRoomId = " + 골룸_아이디 + - " memberIdentifier = " + 팔로워_회원_가입_요청.identifier())); - } - @Test void 정상적으로_모집중인_골룸을_나간다() throws IOException { //given diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomReadIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomReadIntegrationTest.java index 0ab954bb0..1ba4351fd 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomReadIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomReadIntegrationTest.java @@ -2,7 +2,28 @@ import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; +import static co.kirikiri.integration.fixture.CommonFixture.LOCATION; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_노드_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_아이디로_골룸을_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_아이디와_토큰으로_골룸_정보를_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_참가_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸의_사용자_정보를_전체_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸의_사용자_정보를_정렬_기준없이_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.기본_골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.로드맵_아이디로_골룸_목록_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.사용자가_참여한_골룸_중_골룸_진행_상태에_따라_목록을_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.사용자의_모든_골룸_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.사용자의_특정_골룸_정보를_조회한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.이십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.인증_피드_등록; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; @@ -10,42 +31,35 @@ import static org.assertj.core.api.Assertions.assertThat; import co.kirikiri.auth.service.dto.request.LoginRequest; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.persistence.dto.RoadmapGoalRoomsOrderType; +import co.kirikiri.goalroom.service.dto.GoalRoomMemberSortTypeDto; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.goalroom.service.dto.response.DashBoardCheckFeedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomCertifiedResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomMemberResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeDetailResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodeResponse; +import co.kirikiri.goalroom.service.dto.response.GoalRoomRoadmapNodesResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomForListResponse; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; -import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsOrderType; import co.kirikiri.roadmap.service.dto.request.RoadmapDifficultyType; import co.kirikiri.roadmap.service.dto.request.RoadmapNodeSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import co.kirikiri.roadmap.service.dto.response.RoadmapGoalRoomResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.ErrorResponse; -import co.kirikiri.service.dto.goalroom.GoalRoomMemberSortTypeDto; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.CheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCertifiedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomCheckFeedResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomMemberResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeDetailResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodeResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomRoadmapNodesResponse; -import co.kirikiri.service.dto.goalroom.response.GoalRoomTodoResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomForListResponse; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import com.fasterxml.jackson.core.type.TypeReference; import io.restassured.common.mapper.TypeRef; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.mock.web.MockMultipartFile; import java.io.IOException; import java.time.LocalDate; import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockMultipartFile; class GoalRoomReadIntegrationTest extends InitIntegrationTest { @@ -83,72 +97,6 @@ class GoalRoomReadIntegrationTest extends InitIntegrationTest { assertThat(골룸_응답값.name()).isEqualTo(정상적인_골룸_이름); } - @Test - void 골룸_투두리스트를_조회한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - - final GoalRoomTodoRequest 골룸_투두_생성_요청 = new GoalRoomTodoRequest("content", 이십일_후, 삼십일_후); - 골룸_투두리스트_추가(기본_로그인_토큰, 기본_골룸_아이디, 골룸_투두_생성_요청); - - 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); - - // when - final List 골룸_투두리스트_응답값 = 골룸_투두리스트_조회(기본_골룸_아이디, 기본_로그인_토큰) - .as(new TypeRef<>() { - }); - - // then - assertThat(골룸_투두리스트_응답값.get(0).startDate()) - .isEqualTo(이십일_후); - } - - @Test - void 골룸_투두리스트_조회시_존재하지_않은_골룸일_경우() { - // given - // when - final ErrorResponse 예외_응답 = 골룸_투두리스트_조회(1L, 기본_로그인_토큰) - .as(new TypeRef<>() { - }); - - // then - assertThat(예외_응답) - .isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); - } - - @Test - void 골룸_투두리스트_조회시_참여하지_않은_사용자일_경우() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - - final GoalRoomTodoRequest 골룸_투두_생성_요청 = new GoalRoomTodoRequest("content", 이십일_후, 삼십일_후); - 골룸_투두리스트_추가(기본_로그인_토큰, 기본_골룸_아이디, 골룸_투두_생성_요청); - - final MemberJoinRequest 다른_사용자_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 다른_사용자_로그인_요청 = new LoginRequest(다른_사용자_회원_가입_요청.identifier(), 다른_사용자_회원_가입_요청.password()); - 회원가입(다른_사용자_회원_가입_요청); - final String 다른_사용자_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(다른_사용자_로그인_요청).accessToken()); - - 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); - - // when - final ErrorResponse 예외_응답 = 골룸_투두리스트_조회(기본_골룸_아이디, 다른_사용자_액세스_토큰) - .as(new TypeRef<>() { - }); - - // then - assertThat(예외_응답) - .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = " + 기본_골룸_아이디 + - " memberIdentifier = identifier2")); - } - @Test void 진행중인_사용자_단일_골룸을_조회한다() throws IOException { // given @@ -182,8 +130,9 @@ class GoalRoomReadIntegrationTest extends InitIntegrationTest { List.of(new GoalRoomRoadmapNodeResponse(로드맵_응답.content().nodes().get(0).id(), "roadmap 1st week", 오늘, 십일_후, 정상적인_골룸_노드_인증_횟수))), List.of(), - List.of(new CheckFeedResponse(2L, "default-image-path", "image description", LocalDate.now()), - new CheckFeedResponse(1L, "default-image-path", "image description", LocalDate.now()))); + List.of(new DashBoardCheckFeedResponse(2L, "default-image-path", "image description", LocalDate.now()), + new DashBoardCheckFeedResponse(1L, "default-image-path", "image description", + LocalDate.now()))); assertThat(요청_응답값) .usingRecursiveComparison() @@ -206,6 +155,32 @@ class GoalRoomReadIntegrationTest extends InitIntegrationTest { assertThat(요청_응답값.checkFeeds()).isEmpty(); } + @Test + void 진행_중인_사용자_단일_골룸을_조회할_때_진행_중인_노드_기간이_아니면_빈_인증피드를_반환한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 기본_골룸_아이디 = 십일_후에_시작하는_골룸_생성(기본_로그인_토큰, 로드맵_응답); + + final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", + "follower", GenderType.FEMALE, DEFAULT_EMAIL); + final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); + 회원가입(팔로워_회원_가입_요청); + final String 팔로워_액세스_토큰 = 로그인(팔로워_로그인_요청).accessToken(); + + 골룸_참가_요청(기본_골룸_아이디, 팔로워_액세스_토큰); + + testTransactionService.골룸의_시작날짜를_변경한다(기본_골룸_아이디, 오늘); + 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); + + // when + final MemberGoalRoomResponse 요청_응답값 = 사용자의_특정_골룸_정보를_조회한다(기본_로그인_토큰, 기본_골룸_아이디); + + // then + assertThat(요청_응답값.checkFeeds()).isEmpty(); + } + @Test void 사용자의_모든_골룸_목록을_조회한다() throws IOException { // given @@ -355,86 +330,6 @@ class GoalRoomReadIntegrationTest extends InitIntegrationTest { .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier2")); } - @Test - void 골룸의_인증피드를_전체_조회한다() throws IOException { - // given - final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); - 회원가입(팔로워_회원_가입_요청); - final String 팔로워_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(팔로워_로그인_요청).accessToken()); - - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - - 골룸_참가_요청(기본_골룸_아이디, 팔로워_액세스_토큰); - 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/jpeg", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청1 = new CheckFeedRequest(가짜_이미지_객체, "image description1"); - final CheckFeedRequest 인증_피드_등록_요청2 = new CheckFeedRequest(가짜_이미지_객체, "image description2"); - - 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청1, 기본_로그인_토큰); - 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청2, 팔로워_액세스_토큰); - - //when - final List 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(팔로워_액세스_토큰, 기본_골룸_아이디) - .as(new TypeRef<>() { - }); - - // then - assertThat(인증_피드_전체_조회_요청에_대한_응답.get(0).checkFeed().description()).isEqualTo(인증_피드_등록_요청2.description()); - assertThat(인증_피드_전체_조회_요청에_대한_응답.get(1).checkFeed().description()).isEqualTo(인증_피드_등록_요청1.description()); - } - - @Test - void 골룸의_인증피드를_전체_조회시_존재하지_않는_골룸인_경우_예외가_발생한다() throws IOException { - // given - //when - final Long 존재하지_않는_골룸_아이디 = 1L; - final ExtractableResponse 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(기본_로그인_토큰, 존재하지_않는_골룸_아이디); - - // then - final ErrorResponse 인증_피드_전체_조회_응답_바디 = jsonToClass(인증_피드_전체_조회_요청에_대한_응답.asString(), new TypeReference<>() { - }); - assertThat(인증_피드_전체_조회_요청에_대한_응답.statusCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); - assertThat(인증_피드_전체_조회_응답_바디).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); - } - - @Test - void 골룸의_인증피드를_전체_조회시_골룸에_참여하지_않은_사용자면_예외가_발생한다() throws IOException { - // given - final MemberJoinRequest 다른_회원_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 다른_회원_로그인_요청 = new LoginRequest(다른_회원_회원_가입_요청.identifier(), 다른_회원_회원_가입_요청.password()); - 회원가입(다른_회원_회원_가입_요청); - final String 다른_회원_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(다른_회원_로그인_요청).accessToken()); - - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); - - final MockMultipartFile 가짜_이미지_객체 = new MockMultipartFile("image", "originalFileName.jpeg", - "image/jpeg", "tempImage".getBytes()); - final CheckFeedRequest 인증_피드_등록_요청1 = new CheckFeedRequest(가짜_이미지_객체, "image description1"); - - 인증_피드_등록(기본_골룸_아이디, 가짜_이미지_객체, 인증_피드_등록_요청1, 기본_로그인_토큰); - - //when - final ExtractableResponse 인증_피드_전체_조회_요청에_대한_응답 = 인증_피드_전체_조회_요청(다른_회원_액세스_토큰, 기본_골룸_아이디); - - // then - final ErrorResponse 인증_피드_전체_조회_응답_바디 = jsonToClass(인증_피드_전체_조회_요청에_대한_응답.asString(), new TypeReference<>() { - }); - assertThat(인증_피드_전체_조회_요청에_대한_응답.statusCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); - assertThat(인증_피드_전체_조회_응답_바디).isEqualTo(new ErrorResponse("골룸에 참여하지 않은 회원입니다.")); - } - @Test void 골룸의_사용자_정보를_달성률순으로_전체_조회한다() throws IOException { // given @@ -618,4 +513,13 @@ class GoalRoomReadIntegrationTest extends InitIntegrationTest { assertThat(로드맵_아이디로_골룸_목록_조회_응답1.responses().get(0).goalRoomId()).isEqualTo(기본_골룸_아이디); assertThat(로드맵_아이디로_골룸_목록_조회_응답1.responses().get(1).goalRoomId()).isEqualTo(두번째_골룸_아이디); } + + private Long 십일_후에_시작하는_골룸_생성(final String 액세스_토큰, final RoadmapResponse 로드맵_응답) { + final List 골룸_노드_별_기간_요청 = List.of( + new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 십일_후, 이십일_후)); + final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(로드맵_응답.content().id(), 정상적인_골룸_이름, + 정상적인_골룸_제한_인원, 골룸_노드_별_기간_요청); + final String Location_헤더 = 골룸_생성(골룸_생성_요청, 액세스_토큰).response().header(LOCATION); + return Long.parseLong(Location_헤더.substring(16)); + } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomSchedulerIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomSchedulerIntegrationTest.java index 7c8b9d99d..78c19ff84 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomSchedulerIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomSchedulerIntegrationTest.java @@ -2,8 +2,11 @@ import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; -import static co.kirikiri.integration.fixture.CommonFixture.LOCATION; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_참가_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.기본_골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.사용자의_특정_골룸_정보를_조회한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; @@ -12,21 +15,23 @@ import static org.junit.jupiter.api.Assertions.assertAll; import co.kirikiri.auth.service.dto.request.LoginRequest; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomStatus; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomPendingMemberRepository; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; +import co.kirikiri.goalroom.service.scheduler.GoalRoomScheduler; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; -import co.kirikiri.service.scheduler.GoalRoomScheduler; -import org.junit.jupiter.api.Test; import java.io.IOException; +import java.time.LocalDate; import java.util.List; +import org.junit.jupiter.api.Test; class GoalRoomSchedulerIntegrationTest extends InitIntegrationTest { @@ -49,7 +54,7 @@ public GoalRoomSchedulerIntegrationTest(final GoalRoomScheduler goalRoomSchedule final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, null); + final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, 골룸_로드맵_노드들을_생성한다()); final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", "follower", GenderType.FEMALE, DEFAULT_EMAIL); @@ -76,7 +81,7 @@ public GoalRoomSchedulerIntegrationTest(final GoalRoomScheduler goalRoomSchedule final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, null); + final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, 골룸_로드맵_노드들을_생성한다()); final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", "follower", GenderType.FEMALE, DEFAULT_EMAIL); @@ -104,7 +109,7 @@ public GoalRoomSchedulerIntegrationTest(final GoalRoomScheduler goalRoomSchedule final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, null); + final GoalRoom 골룸 = new GoalRoom(기본_골룸_아이디, null, null, null, 골룸_로드맵_노드들을_생성한다()); final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", "follower", GenderType.FEMALE, DEFAULT_EMAIL); @@ -176,61 +181,9 @@ public GoalRoomSchedulerIntegrationTest(final GoalRoomScheduler goalRoomSchedule assertThat(요청_응답값.status()).isEqualTo(GoalRoomStatus.RUNNING.name()); } - @Test - void 진행_중인_사용자_단일_골룸을_조회할_때_진행_중인_노드_기간이_아니면_빈_인증피드를_반환한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 십일_후에_시작하는_골룸_생성(기본_로그인_토큰, 로드맵_응답); - - final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); - 회원가입(팔로워_회원_가입_요청); - final String 팔로워_액세스_토큰 = 로그인(팔로워_로그인_요청).accessToken(); - - 골룸_참가_요청(기본_골룸_아이디, 팔로워_액세스_토큰); - - testTransactionService.골룸의_시작날짜를_변경한다(기본_골룸_아이디, 오늘); - 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); - - // when - final MemberGoalRoomResponse 요청_응답값 = 사용자의_특정_골룸_정보를_조회한다(기본_로그인_토큰, 기본_골룸_아이디); - - // then - assertThat(요청_응답값.checkFeeds()).isEmpty(); - } - - @Test - void 모집_중인_사용자_단일_골룸_조회_시_인증_피드가_빈_응답을_반환한다() throws IOException { - // given - final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); - final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); - - final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); - - final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", - "follower", GenderType.FEMALE, DEFAULT_EMAIL); - final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); - 회원가입(팔로워_회원_가입_요청); - final String 팔로워_액세스_토큰 = 로그인(팔로워_로그인_요청).accessToken(); - - 골룸_참가_요청(기본_골룸_아이디, 팔로워_액세스_토큰); - - //when - final MemberGoalRoomResponse 요청_응답값 = 사용자의_특정_골룸_정보를_조회한다(기본_로그인_토큰, 기본_골룸_아이디); - - //then - assertThat(요청_응답값.checkFeeds()).isEmpty(); - } - - private Long 십일_후에_시작하는_골룸_생성(final String 액세스_토큰, final RoadmapResponse 로드맵_응답) { - final List 골룸_노드_별_기간_요청 = List.of( - new GoalRoomRoadmapNodeRequest(로드맵_응답.content().nodes().get(0).id(), 정상적인_골룸_노드_인증_횟수, 십일_후, 이십일_후)); - final GoalRoomCreateRequest 골룸_생성_요청 = new GoalRoomCreateRequest(로드맵_응답.content().id(), 정상적인_골룸_이름, - 정상적인_골룸_제한_인원, 골룸_노드_별_기간_요청); - final String Location_헤더 = 골룸_생성(골룸_생성_요청, 액세스_토큰).response().header(LOCATION); - return Long.parseLong(Location_헤더.substring(16)); + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다() { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(LocalDate.now(), LocalDate.now().plusDays(5)), 5, 1L)) + ); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomToDoIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomToDoIntegrationTest.java new file mode 100644 index 000000000..c40cf88eb --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/GoalRoomToDoIntegrationTest.java @@ -0,0 +1,260 @@ +package co.kirikiri.integration; + +import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; +import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_투두리스트_조회; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_투두리스트_추가; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_투두리스트_추가후_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_투두리스트를_체크한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.기본_골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.삼십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.이십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_생성; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_투두_컨텐츠; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; +import static org.assertj.core.api.Assertions.assertThat; + +import co.kirikiri.auth.service.dto.request.LoginRequest; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.integration.helper.InitIntegrationTest; +import co.kirikiri.member.service.dto.request.GenderType; +import co.kirikiri.member.service.dto.request.MemberJoinRequest; +import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import io.restassured.common.mapper.TypeRef; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; + +class GoalRoomToDoIntegrationTest extends InitIntegrationTest { + + @Test + void 정상적으로_골룸에_투두리스트를_추가한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + final GoalRoomTodoRequest 골룸_투두리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); + + // when + final ExtractableResponse 골룸_투두리스트_추가 = 골룸_투두리스트_추가(기본_로그인_토큰, 골룸_아이디, 골룸_투두리스트_추가_요청); + + // then + assertThat(골룸_투두리스트_추가.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + final String header = 골룸_투두리스트_추가.response() + .header(HttpHeaders.LOCATION); + assertThat(header).contains("/api/goal-rooms/1/todos/" + header.substring(24)); + } + + @Test + void 골룸에_팔로워가_투두_리스트를_추가할때_예외를_던진다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final MemberJoinRequest 팔로워_회원가입_요청 = new MemberJoinRequest("identifier2", "password12!@#$%", "follower", + GenderType.MALE, DEFAULT_EMAIL); + 회원가입(팔로워_회원가입_요청); + final String 팔로워_로그인_토큰 = String.format(BEARER_TOKEN_FORMAT, + 로그인(new LoginRequest(팔로워_회원가입_요청.identifier(), 팔로워_회원가입_요청.password())).accessToken()); + + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 로드맵_응답.roadmapId(), 로드맵_응답.content().nodes().get(0).id()); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + final GoalRoomTodoRequest 골룸_투두리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); + + // when + final ExtractableResponse 골룸_투두리스트_추가 = 골룸_투두리스트_추가(팔로워_로그인_토큰, 골룸_아이디, 골룸_투두리스트_추가_요청); + + // then + final ErrorResponse 골룸_투두리스트_추가_바디 = 골룸_투두리스트_추가.as(new TypeRef<>() { + }); + + assertThat(골룸_투두리스트_추가.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); + assertThat(골룸_투두리스트_추가_바디).isEqualTo(new ErrorResponse("골룸의 리더만 투두리스트를 추가할 수 있습니다.")); + } + + @Test + void 종료된_골룸에_투두_리스트를_추가할때_예외를_던진다() throws IOException { + //given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); + testTransactionService.골룸을_완료시킨다(골룸_아이디); + + final GoalRoomTodoRequest 골룸_투두_리스트_추가_요청 = new GoalRoomTodoRequest(정상적인_골룸_투두_컨텐츠, 오늘, 십일_후); + + //when + final ExtractableResponse 골룸_추가_응답 = 골룸_투두리스트_추가(기본_로그인_토큰, 골룸_아이디, 골룸_투두_리스트_추가_요청); + + //then + final ErrorResponse 골룸_추가_응답_바디 = 골룸_추가_응답.as(new TypeRef<>() { + }); + + assertThat(골룸_추가_응답.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); + assertThat(골룸_추가_응답_바디).isEqualTo(new ErrorResponse("이미 종료된 골룸입니다.")); + } + + @Test + void 골룸_투두_리스트를_체크한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); + + // when + final GoalRoomToDoCheckResponse 골룸_투두리스트_체크_응답값 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디) + .as(new TypeRef<>() { + }); + + // then + final GoalRoomToDoCheckResponse 예상하는_골룸_투두리스트_체크_응답값 = new GoalRoomToDoCheckResponse(true); + assertThat(골룸_투두리스트_체크_응답값) + .isEqualTo(예상하는_골룸_투두리스트_체크_응답값); + } + + @Test + void 골룸_투두리스트_체크를_해제한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); + + 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디); + + // when + final GoalRoomToDoCheckResponse 두번째_골룸_투두리스트_체크_응답값 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 골룸_아이디, 투두_아이디) + .as(new TypeRef<>() { + }); + + // then + final GoalRoomToDoCheckResponse 예상하는_골룸_투두리스트_체크_응답값 = new GoalRoomToDoCheckResponse(false); + assertThat(두번째_골룸_투두리스트_체크_응답값) + .isEqualTo(예상하는_골룸_투두리스트_체크_응답값); + } + + @Test + void 골룸_투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() { + // given + // when + final ErrorResponse 에러_응답 = 골룸_투두리스트를_체크한다(기본_로그인_토큰, 1L, 1L) + .as(new TypeRef<>() { + }); + + // then + assertThat(에러_응답) + .isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸_투두리스트_체크시_사용자가_없으면_예외가_발생한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 골룸_아이디 = 정상적인_골룸_생성(기본_로그인_토큰, 기본_로드맵_아이디, 로드맵_응답.content().nodes().get(0).id()); + 골룸을_시작한다(기본_로그인_토큰, 골룸_아이디); + final Long 투두_아이디 = 골룸_투두리스트_추가후_아이디를_반환한다(기본_로그인_토큰, 골룸_아이디); + + final MemberJoinRequest 팔로워_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", + "follower", GenderType.FEMALE, DEFAULT_EMAIL); + final LoginRequest 팔로워_로그인_요청 = new LoginRequest(팔로워_회원_가입_요청.identifier(), 팔로워_회원_가입_요청.password()); + 회원가입(팔로워_회원_가입_요청); + final String 팔로워_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(팔로워_로그인_요청).accessToken()); + + // when + final ErrorResponse 에러_응답 = 골룸_투두리스트를_체크한다(팔로워_액세스_토큰, 골룸_아이디, 투두_아이디) + .as(new TypeRef<>() { + }); + + // then + assertThat(에러_응답) + .isEqualTo(new ErrorResponse("골룸에 사용자가 존재하지 않습니다. goalRoomId = " + 골룸_아이디 + + " memberIdentifier = " + 팔로워_회원_가입_요청.identifier())); + } + + @Test + void 골룸_투두리스트를_조회한다() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); + + final GoalRoomTodoRequest 골룸_투두_생성_요청 = new GoalRoomTodoRequest("content", 이십일_후, 삼십일_후); + + 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); + 골룸_투두리스트_추가(기본_로그인_토큰, 기본_골룸_아이디, 골룸_투두_생성_요청); + + // when + final List 골룸_투두리스트_응답값 = 골룸_투두리스트_조회(기본_골룸_아이디, 기본_로그인_토큰) + .as(new TypeRef<>() { + }); + + // then + assertThat(골룸_투두리스트_응답값.get(0).startDate()) + .isEqualTo(이십일_후); + } + + @Test + void 골룸_투두리스트_조회시_존재하지_않은_골룸일_경우() { + // given + // when + final ErrorResponse 예외_응답 = 골룸_투두리스트_조회(1L, 기본_로그인_토큰) + .as(new TypeRef<>() { + }); + + // then + assertThat(예외_응답) + .isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸_투두리스트_조회시_참여하지_않은_사용자일_경우() throws IOException { + // given + final Long 기본_로드맵_아이디 = 로드맵_생성(기본_로드맵_생성_요청, 기본_로그인_토큰); + final RoadmapResponse 로드맵_응답 = 로드맵을_아이디로_조회하고_응답객체를_반환한다(기본_로드맵_아이디); + + final Long 기본_골룸_아이디 = 기본_골룸_생성(기본_로그인_토큰, 로드맵_응답); + + final GoalRoomTodoRequest 골룸_투두_생성_요청 = new GoalRoomTodoRequest("content", 이십일_후, 삼십일_후); + 골룸_투두리스트_추가(기본_로그인_토큰, 기본_골룸_아이디, 골룸_투두_생성_요청); + + final MemberJoinRequest 다른_사용자_회원_가입_요청 = new MemberJoinRequest("identifier2", "paswword2@", + "follower", GenderType.FEMALE, DEFAULT_EMAIL); + final LoginRequest 다른_사용자_로그인_요청 = new LoginRequest(다른_사용자_회원_가입_요청.identifier(), 다른_사용자_회원_가입_요청.password()); + 회원가입(다른_사용자_회원_가입_요청); + final String 다른_사용자_액세스_토큰 = String.format(BEARER_TOKEN_FORMAT, 로그인(다른_사용자_로그인_요청).accessToken()); + + 골룸을_시작한다(기본_로그인_토큰, 기본_골룸_아이디); + + // when + final ErrorResponse 예외_응답 = 골룸_투두리스트_조회(기본_골룸_아이디, 다른_사용자_액세스_토큰) + .as(new TypeRef<>() { + }); + + // then + assertThat(예외_응답) + .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = " + 기본_골룸_아이디 + + " memberIdentifier = identifier2")); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberCreateIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberCreateIntegrationTest.java index 44910b45f..33ce378e0 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberCreateIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberCreateIntegrationTest.java @@ -4,10 +4,10 @@ import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_회원가입; import static org.assertj.core.api.Assertions.assertThat; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; -import co.kirikiri.service.dto.ErrorResponse; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; @@ -21,7 +21,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @Test void 정상적으로_회원가입을_성공한다() { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!@#$%", "hello", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!@#$%", "hello", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -34,7 +35,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"abc", "abcdefghijklmnopqrstuabcdefghijklmnopqrst"}) void 아이디_길이가_틀린_경우_회원가입에_실패한다(final String 회원_아이디) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest(회원_아이디, "password12!", "nickname", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest(회원_아이디, "password12!", "nickname", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -50,7 +52,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"Abcd", "abcdefghijklmnopqrsT", "가나다라"}) void 아이디에_허용되지_않은_문자가_들어온_경우_회원가입에_실패한다(final String 회원_아이디) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest(회원_아이디, "password12!", "nickname", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest(회원_아이디, "password12!", "nickname", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -64,7 +67,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @Test void 아이디가_중복된_경우_회원가입에_실패한다() { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!", "hello", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!", "hello", GenderType.MALE, + DEFAULT_EMAIL); 요청을_받는_회원가입(회원가입_요청); //when @@ -80,7 +84,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"abcde1!", "abcdefghijklmn12"}) void 비밀번호_길이가_틀린_경우_회원가입에_실패한다(final String password) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -95,7 +100,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"abcdef1/", "abcdefghij1₩", "abcdefgH1!"}) void 비밀번호에_허용되지_않은_문자가_들어온_경우_회원가입에_실패한다(final String password) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -110,7 +116,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"abcdefgh", "abcdefghijkl"}) void 비밀번호에_영소문자만_들어온_경우_회원가입에_실패한다(final String password) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", password, "nickname", GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); @@ -141,7 +148,8 @@ class MemberCreateIntegrationTest extends InitIntegrationTest { @ValueSource(strings = {"a", "123456789012345678901"}) void 닉네임_길이가_틀린_경우_회원가입에_실패한다(final String nickname) { //given - final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!@#$%", nickname, GenderType.MALE, DEFAULT_EMAIL); + final MemberJoinRequest 회원가입_요청 = new MemberJoinRequest("ab12", "password12!@#$%", nickname, GenderType.MALE, + DEFAULT_EMAIL); //when final ExtractableResponse 회원가입_응답 = 요청을_받는_회원가입(회원가입_요청); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberReadIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberReadIntegrationTest.java index c97688a92..bf3ecf85f 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberReadIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/MemberReadIntegrationTest.java @@ -1,14 +1,20 @@ package co.kirikiri.integration; -import static co.kirikiri.integration.fixture.MemberAPIFixture.*; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_GENDER_TYPE; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_IDENTIFIER; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_NICKNAME; +import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_사용자_자신의_정보_조회_요청; +import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_특정_사용자의_정보_조회; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; import static org.assertj.core.api.Assertions.assertThat; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.service.dto.request.MemberJoinRequest; import co.kirikiri.member.service.dto.response.MemberInformationForPublicResponse; import co.kirikiri.member.service.dto.response.MemberInformationResponse; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import io.restassured.response.ExtractableResponse; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapCreateIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapCreateIntegrationTest.java index ae22e8dd4..97cb4703d 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapCreateIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapCreateIntegrationTest.java @@ -1,13 +1,19 @@ package co.kirikiri.integration; import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; -import static co.kirikiri.integration.fixture.CommonFixture.*; +import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; +import static co.kirikiri.integration.fixture.CommonFixture.아이디를_반환한다; +import static co.kirikiri.integration.fixture.CommonFixture.응답_상태_코드_검증; import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_삭제; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_카테고리를_생성한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.요청을_받는_이미지가_포함된_로드맵_생성; import static org.assertj.core.api.Assertions.assertThat; import co.kirikiri.auth.service.dto.request.LoginRequest; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; @@ -16,17 +22,16 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapNodeSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapTagSaveRequest; -import co.kirikiri.service.dto.ErrorResponse; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.io.IOException; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.http.HttpStatus; -import java.io.IOException; -import java.util.Collections; -import java.util.List; class RoadmapCreateIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadIntegrationTest.java index 1ae51d154..a583439c8 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadIntegrationTest.java @@ -1,9 +1,19 @@ package co.kirikiri.integration; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로그인한_사용자가_생성한_로드맵을_이전에_받은_로드맵의_제일마지막_아이디_이후의_조건으로_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로그인한_사용자가_생성한_로드맵을_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.모든_카테고리를_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.사이즈_없이_로드맵을_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.사이즈별로_로드맵을_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.정렬된_카테고리별_로드맵_리스트_조회; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.카테고리_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.카테고리들_생성; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.persistence.dto.RoadmapOrderType; @@ -16,14 +26,13 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapForListResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapForListResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.ErrorResponse; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import java.io.IOException; import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; class RoadmapReadIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadOrderIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadOrderIntegrationTest.java index 8511d4a18..b4398f553 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadOrderIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReadOrderIntegrationTest.java @@ -1,12 +1,26 @@ package co.kirikiri.integration; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸_참가_요청; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_시작한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; import static co.kirikiri.integration.fixture.MemberAPIFixture.사용자를_추가하고_토큰을_조회한다; import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_사용자_자신의_정보_조회_요청; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.리뷰를_생성한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.정렬된_로드맵_리스트_조회; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.정렬된_카테고리별_로드맵_리스트_조회; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.카테고리_생성; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.domain.goalroom.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.response.MemberInformationResponse; import co.kirikiri.roadmap.domain.RoadmapCategory; @@ -18,12 +32,10 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapTagSaveRequest; import co.kirikiri.roadmap.service.dto.response.RoadmapForListResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; import io.restassured.common.mapper.TypeRef; -import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.List; +import org.junit.jupiter.api.Test; class RoadmapReadOrderIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewCreateIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewCreateIntegrationTest.java index 91eeefa44..847a265f6 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewCreateIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewCreateIntegrationTest.java @@ -2,29 +2,38 @@ import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; -import static co.kirikiri.integration.fixture.MemberAPIFixture.*; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_사용자_자신의_정보_조회_요청; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.리뷰를_생성한다; import static org.assertj.core.api.Assertions.assertThat; import co.kirikiri.auth.service.dto.request.LoginRequest; -import co.kirikiri.domain.goalroom.GoalRoom; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; import co.kirikiri.member.service.dto.response.MemberInformationResponse; import co.kirikiri.roadmap.service.dto.request.RoadmapReviewSaveRequest; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.ErrorResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import java.io.IOException; import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; class RoadmapReviewCreateIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewReadIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewReadIntegrationTest.java index d7c8643ff..4c0d59d36 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewReadIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapReviewReadIntegrationTest.java @@ -2,13 +2,20 @@ import static co.kirikiri.integration.fixture.AuthenticationAPIFixture.로그인; import static co.kirikiri.integration.fixture.CommonFixture.BEARER_TOKEN_FORMAT; -import static co.kirikiri.integration.fixture.MemberAPIFixture.*; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_EMAIL; +import static co.kirikiri.integration.fixture.MemberAPIFixture.요청을_받는_사용자_자신의_정보_조회_요청; +import static co.kirikiri.integration.fixture.MemberAPIFixture.회원가입; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_리뷰를_조회한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.리뷰를_생성한다; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import co.kirikiri.auth.service.dto.request.LoginRequest; -import co.kirikiri.domain.goalroom.GoalRoom; +import co.kirikiri.common.service.dto.CustomScrollRequest; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.domain.GoalRoom; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; @@ -17,18 +24,16 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapReviewSaveRequest; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; -import co.kirikiri.service.dto.CustomScrollRequest; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import java.io.IOException; import java.time.LocalDateTime; import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; class RoadmapReviewReadIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapSchedulerIntegrationTest.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapSchedulerIntegrationTest.java index c0e8eb0f2..fecf22296 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapSchedulerIntegrationTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/RoadmapSchedulerIntegrationTest.java @@ -1,20 +1,27 @@ package co.kirikiri.integration; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; -import static co.kirikiri.integration.fixture.RoadmapAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_삭제; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵_생성; +import static co.kirikiri.integration.fixture.RoadmapAPIFixture.로드맵을_아이디로_조회하고_응답객체를_반환한다; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.domain.goalroom.GoalRoomStatus; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; import co.kirikiri.integration.helper.InitIntegrationTest; import co.kirikiri.roadmap.persistence.RoadmapRepository; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.scheduler.RoadmapScheduler; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import org.junit.jupiter.api.Test; import java.io.IOException; import java.time.LocalDate; import java.util.List; +import org.junit.jupiter.api.Test; class RoadmapSchedulerIntegrationTest extends InitIntegrationTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/GoalRoomAPIFixture.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/GoalRoomAPIFixture.java index c59f78b8d..7dee91904 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/GoalRoomAPIFixture.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/GoalRoomAPIFixture.java @@ -1,26 +1,28 @@ package co.kirikiri.integration.fixture; -import static co.kirikiri.integration.fixture.CommonFixture.*; +import static co.kirikiri.integration.fixture.CommonFixture.API_PREFIX; +import static co.kirikiri.integration.fixture.CommonFixture.AUTHORIZATION; +import static co.kirikiri.integration.fixture.CommonFixture.LOCATION; import static io.restassured.RestAssured.given; +import co.kirikiri.checkfeed.service.dto.request.CheckFeedRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; +import co.kirikiri.goalroom.service.dto.response.MemberGoalRoomResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.MemberGoalRoomResponse; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; import io.restassured.common.mapper.TypeRef; import io.restassured.http.Header; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; public class GoalRoomAPIFixture { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/RoadmapAPIFixture.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/RoadmapAPIFixture.java index d8966e793..7a783246b 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/RoadmapAPIFixture.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/fixture/RoadmapAPIFixture.java @@ -4,6 +4,7 @@ import static co.kirikiri.integration.fixture.CommonFixture.AUTHORIZATION; import static io.restassured.RestAssured.given; +import co.kirikiri.common.service.dto.CustomScrollRequest; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.persistence.dto.RoadmapOrderType; import co.kirikiri.roadmap.service.dto.request.RoadmapCategorySaveRequest; @@ -11,18 +12,17 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapReviewSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.CustomScrollRequest; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import io.restassured.specification.RequestSpecification; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; public class RoadmapAPIFixture { @@ -215,7 +215,8 @@ private static RequestSpecification makeRequestSpecification(final RoadmapSaveRe .extract(); } - public static ExtractableResponse 로드맵_카테고리를_생성한다(final String 로그인_토큰_정보, final RoadmapCategorySaveRequest 카테고리_생성_요청) { + public static ExtractableResponse 로드맵_카테고리를_생성한다(final String 로그인_토큰_정보, + final RoadmapCategorySaveRequest 카테고리_생성_요청) { return given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) .when() diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestConfig.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestConfig.java index 8fc5c56f0..8aec60c77 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestConfig.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestConfig.java @@ -2,8 +2,8 @@ import co.kirikiri.auth.persistence.RefreshTokenRepository; import co.kirikiri.common.service.FileService; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestFileService.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestFileService.java index 264538fee..122ced8fa 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestFileService.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestFileService.java @@ -1,10 +1,10 @@ package co.kirikiri.integration.helper; import co.kirikiri.common.service.FileService; -import co.kirikiri.service.dto.FileInformation; -import org.springframework.http.HttpMethod; +import co.kirikiri.common.service.dto.FileInformation; import java.net.MalformedURLException; import java.net.URL; +import org.springframework.http.HttpMethod; public class TestFileService implements FileService { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestTransactionService.java b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestTransactionService.java index f63bc8d47..c305b76fa 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestTransactionService.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/integration/helper/TestTransactionService.java @@ -1,13 +1,22 @@ package co.kirikiri.integration.helper; -import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.*; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.골룸을_생성하고_아이디를_반환한다; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.십일_후; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.오늘; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_노드_인증_횟수; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_이름; +import static co.kirikiri.integration.fixture.GoalRoomAPIFixture.정상적인_골룸_제한_인원; import static co.kirikiri.integration.fixture.MemberAPIFixture.DEFAULT_PASSWORD; import static co.kirikiri.integration.helper.InitIntegrationTest.기본_로그인_토큰; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.GoalRoomStatus; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.GoalRoomStatus; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.goalroom.service.dto.request.GoalRoomCreateRequest; +import co.kirikiri.goalroom.service.dto.request.GoalRoomRoadmapNodeRequest; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -16,18 +25,14 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.service.dto.response.MemberInformationResponse; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import org.springframework.transaction.annotation.Transactional; @Transactional public class TestTransactionService { @@ -62,14 +67,14 @@ public TestTransactionService(final GoalRoomRepository goalRoomRepository, final MemberInformationResponse... 팔로워들_정보) { final Member 리더 = 사용자_정보에서_사용자를_생성한다(리더_정보); final GoalRoomMember 골룸_멤버_리더 = new GoalRoomMember(GoalRoomRole.LEADER, - LocalDateTime.of(2023, 7, 1, 12, 0), 골룸, 리더); + LocalDateTime.of(2023, 7, 1, 12, 0), 골룸, 리더.getId()); final List 골룸_멤버_리스트 = new ArrayList<>(); 골룸_멤버_리스트.add(골룸_멤버_리더); for (final MemberInformationResponse 팔로워_정보 : 팔로워들_정보) { final Member 팔로워 = 사용자_정보에서_사용자를_생성한다(팔로워_정보); final GoalRoomMember 골룸_멤버_팔로워 = new GoalRoomMember(GoalRoomRole.FOLLOWER, - LocalDateTime.of(2023, 7, 5, 18, 0), 골룸, 팔로워); + LocalDateTime.of(2023, 7, 5, 18, 0), 골룸, 팔로워.getId()); 골룸_멤버_리스트.add(골룸_멤버_팔로워); } 골룸_멤버를_저장한다(골룸_멤버_리스트); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberCreateApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberCreateApiTest.java index d854cbeb6..5ff3fbdb9 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberCreateApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberCreateApiTest.java @@ -13,13 +13,14 @@ import co.kirikiri.common.exception.BadRequestException; import co.kirikiri.common.exception.ConflictException; -import co.kirikiri.controller.helper.ControllerTestHelper; -import co.kirikiri.controller.helper.FieldDescriptionHelper.FieldDescription; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.helper.FieldDescriptionHelper.FieldDescription; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.member.service.MemberService; import co.kirikiri.member.service.dto.request.GenderType; import co.kirikiri.member.service.dto.request.MemberJoinRequest; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.type.TypeReference; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -28,7 +29,6 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; -import java.util.List; @WebMvcTest(MemberController.class) class MemberCreateApiTest extends ControllerTestHelper { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberReadApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberReadApiTest.java index 2c3c2bb01..ca3c9865e 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberReadApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/member/controller/MemberReadApiTest.java @@ -13,12 +13,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.controller.helper.ControllerTestHelper; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.service.MemberService; import co.kirikiri.member.service.dto.response.MemberInformationForPublicResponse; import co.kirikiri.member.service.dto.response.MemberInformationResponse; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.type.TypeReference; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/member/persistence/MemberRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/member/persistence/MemberRepositoryTest.java index d0b59d02b..d2d0a47dd 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/member/persistence/MemberRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/member/persistence/MemberRepositoryTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; @@ -12,10 +13,9 @@ import co.kirikiri.member.domain.vo.Identifier; import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.persistence.helper.RepositoryTest; +import java.util.Optional; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.util.Optional; @RepositoryTest class MemberRepositoryTest { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/service/dto/member/request/GenderTypeTest.java b/backend/kirikiri/src/test/java/co/kirikiri/member/service/dto/GenderTypeTest.java similarity index 95% rename from backend/kirikiri/src/test/java/co/kirikiri/service/dto/member/request/GenderTypeTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/member/service/dto/GenderTypeTest.java index 8e3de826a..2fceffcb4 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/service/dto/member/request/GenderTypeTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/member/service/dto/GenderTypeTest.java @@ -1,4 +1,4 @@ -package co.kirikiri.service.dto.member.request; +package co.kirikiri.member.service.dto; import static org.assertj.core.api.Assertions.assertThat; diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapCreateApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapCreateApiTest.java index c491db44f..6cde54c09 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapCreateApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapCreateApiTest.java @@ -1,14 +1,26 @@ package co.kirikiri.roadmap.controller; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; -import static org.springframework.restdocs.headers.HeaderDocumentation.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.partWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParts; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -17,7 +29,8 @@ import co.kirikiri.common.exception.ConflictException; import co.kirikiri.common.exception.ForbiddenException; import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.controller.helper.ControllerTestHelper; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.service.dto.ErrorResponse; import co.kirikiri.roadmap.service.RoadmapCreateService; import co.kirikiri.roadmap.service.RoadmapReadService; import co.kirikiri.roadmap.service.dto.request.RoadmapCategorySaveRequest; @@ -26,8 +39,9 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapReviewSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapTagSaveRequest; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.type.TypeReference; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -41,8 +55,6 @@ import org.springframework.restdocs.snippet.Attributes.Attribute; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; -import java.util.ArrayList; -import java.util.List; @WebMvcTest(RoadmapController.class) class RoadmapCreateApiTest extends ControllerTestHelper { diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapReadApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapReadApiTest.java index 69f322ef2..c3d9f5b85 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapReadApiTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/controller/RoadmapReadApiTest.java @@ -10,18 +10,23 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.controller.helper.ControllerTestHelper; -import co.kirikiri.domain.goalroom.GoalRoomStatus; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.service.dto.CustomScrollRequest; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.goalroom.domain.GoalRoomStatus; import co.kirikiri.member.service.dto.response.MemberResponse; import co.kirikiri.roadmap.domain.RoadmapDifficulty; import co.kirikiri.roadmap.service.RoadmapCreateService; import co.kirikiri.roadmap.service.RoadmapReadService; import co.kirikiri.roadmap.service.dto.request.RoadmapOrderTypeRequest; +import co.kirikiri.roadmap.service.dto.response.MemberRoadmapGoalRoomResponse; import co.kirikiri.roadmap.service.dto.response.MemberRoadmapResponse; import co.kirikiri.roadmap.service.dto.response.MemberRoadmapResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapCategoryResponse; @@ -34,9 +39,11 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapTagResponse; -import co.kirikiri.service.dto.CustomScrollRequest; -import co.kirikiri.service.dto.ErrorResponse; import com.fasterxml.jackson.core.type.TypeReference; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -44,10 +51,6 @@ import org.springframework.http.MediaType; import org.springframework.restdocs.snippet.Attributes; import org.springframework.test.web.servlet.MvcResult; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; @WebMvcTest(RoadmapController.class) class RoadmapReadApiTest extends ControllerTestHelper { @@ -690,12 +693,12 @@ class RoadmapReadApiTest extends ControllerTestHelper { GoalRoomStatus.RECRUITING.name(), 3, 6, LocalDateTime.of(2023, 7, 20, 13, 0, 0), LocalDate.now(), LocalDate.now().plusDays(100), - new MemberResponse(1L, "황시진", "default-member-image")); + new MemberRoadmapGoalRoomResponse(1L, "황시진", "default-member-image")); final RoadmapGoalRoomResponse roadmapGoalRoomResponse2 = new RoadmapGoalRoomResponse(2L, "골룸 이름2", GoalRoomStatus.RECRUITING.name(), 4, 10, LocalDateTime.of(2023, 7, 10, 13, 0, 0), LocalDate.now(), LocalDate.now().plusDays(100), - new MemberResponse(2L, "시진이", "default-member-image")); + new MemberRoadmapGoalRoomResponse(2L, "시진이", "default-member-image")); final List responses = List.of(roadmapGoalRoomResponse1, roadmapGoalRoomResponse2); return new RoadmapGoalRoomResponses(responses, false); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapContentRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapContentRepositoryTest.java index fdfd2c8d6..851df11b8 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapContentRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapContentRepositoryTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -11,15 +12,14 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; import co.kirikiri.roadmap.domain.RoadmapDifficulty; import co.kirikiri.roadmap.domain.RoadmapTags; -import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.Test; @RepositoryTest class RoadmapContentRepositoryTest { @@ -82,7 +82,8 @@ public RoadmapContentRepositoryTest(final MemberRepository memberRepository, private Roadmap 로드맵을_저장한다() { final Member creator = 사용자를_생성한다(); final RoadmapCategory category = 로드맵_카테고리를_생성한다(); - final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.NORMAL, creator.getId(), category, new RoadmapTags(new ArrayList<>())); + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.NORMAL, creator.getId(), + category, new RoadmapTags(new ArrayList<>())); return roadmapRepository.save(roadmap); } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapRepositoryTest.java index d81d43f62..67275181d 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapRepositoryTest.java @@ -3,15 +3,18 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -21,9 +24,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -269,13 +269,14 @@ public RoadmapRepositoryTest(final MemberRepository memberRepository, // gameRoadmap1 : 참가인원 0명 // gameRoadmap2 : 참가인원 1명 final List gameRoadmap2GoalRoomMembers = List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), gameRoadmap2GoalRoom, creator)); + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), gameRoadmap2GoalRoom, creator.getId())); goalRoomMemberRepository.saveAllInBatch(gameRoadmap2GoalRoomMembers); // travelRoadmap : 참가인원 2명 final List travelRoadmapGoalRoomMembers = List.of( - new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), travelRoadmapGoalRoom, creator), - new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), travelRoadmapGoalRoom, follower)); + new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), travelRoadmapGoalRoom, creator.getId()), + new GoalRoomMember(GoalRoomRole.FOLLOWER, LocalDateTime.now(), travelRoadmapGoalRoom, + follower.getId())); goalRoomMemberRepository.saveAllInBatch(travelRoadmapGoalRoomMembers); final RoadmapCategory category = null; @@ -609,14 +610,13 @@ public RoadmapRepositoryTest(final MemberRepository memberRepository, final GoalRoomRoadmapNodes goalRoomRoadmapNodes = new GoalRoomRoadmapNodes( List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)); - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(5), roadmapContent, - member); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); + final GoalRoom goalRoom = new GoalRoom(new GoalRoomName("골룸"), new LimitedMemberCount(5), + roadmapContent.getId(), goalRoomRoadmapNodes); return goalRoomRepository.save(goalRoom); } private GoalRoomRoadmapNode 골룸_로드맵_노드를_생성한다(final LocalDate startDate, final LocalDate endDate, final RoadmapNode roadmapNode) { - return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode); + return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode.getId()); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapReviewRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapReviewRepositoryTest.java index cf9cc5e30..ff6cf8bfb 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapReviewRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/persistence/RoadmapReviewRepositoryTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import co.kirikiri.common.helper.RepositoryTest; import co.kirikiri.common.type.ImageContentType; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; @@ -13,12 +14,11 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapReview; -import org.junit.jupiter.api.Test; import java.util.List; import java.util.Optional; +import org.junit.jupiter.api.Test; @RepositoryTest class RoadmapReviewRepositoryTest { @@ -48,7 +48,8 @@ public RoadmapReviewRepositoryTest(final MemberRepository memberRepository, roadmapReviewRepository.save(roadmapReview); // when - final RoadmapReview findRoadmapReview = roadmapReviewRepository.findByRoadmapIdAndMemberId(roadmapId, member.getId()) + final RoadmapReview findRoadmapReview = roadmapReviewRepository.findByRoadmapIdAndMemberId(roadmapId, + member.getId()) .get(); // then diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateEventListenerTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateEventListenerTest.java index 12779de3e..a73936ced 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateEventListenerTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateEventListenerTest.java @@ -2,12 +2,15 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import co.kirikiri.common.exception.BadRequestException; import co.kirikiri.common.exception.ServerException; import co.kirikiri.common.service.FilePathGenerator; import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.FileInformation; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -22,7 +25,10 @@ import co.kirikiri.roadmap.service.dto.RoadmapTagSaveDto; import co.kirikiri.roadmap.service.dto.request.RoadmapDifficultyType; import co.kirikiri.roadmap.service.event.RoadmapCreateEvent; -import co.kirikiri.service.dto.FileInformation; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -30,10 +36,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; @ExtendWith(MockitoExtension.class) class RoadmapCreateEventListenerTest { @@ -55,9 +57,11 @@ class RoadmapCreateEventListenerTest { @Test void 정상적으로_로드맵_노드_이미지를_저장한다() throws IOException { final Roadmap roadmap = new Roadmap(1L, "roadmapTitle", "introduction", 10, - RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, memberId, new RoadmapCategory("category"), new RoadmapTags(new ArrayList<>())); + RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, memberId, new RoadmapCategory("category"), + new RoadmapTags(new ArrayList<>())); final RoadmapNode roadmapNode = new RoadmapNode("roadmapNodeTitle", "roadmapNodeContent"); - final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), new RoadmapNodes(List.of(roadmapNode))); + final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), + new RoadmapNodes(List.of(roadmapNode))); final MultipartFile imageFile = new MockMultipartFile(roadmapNode.getTitle(), "originalFileName.jpeg", "image/jpeg", "tempImage".getBytes()); @@ -114,7 +118,8 @@ class RoadmapCreateEventListenerTest { final Roadmap roadmap = new Roadmap(1L, "roadmapTitle", "introduction", 10, RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, memberId, new RoadmapCategory("category"), new RoadmapTags(new ArrayList<>())); final RoadmapNode roadmapNode = new RoadmapNode("roadmapNodeTitle", "roadmapNodeContent"); - final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), new RoadmapNodes(List.of(roadmapNode))); + final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), + new RoadmapNodes(List.of(roadmapNode))); final MultipartFile imageFile = new MockMultipartFile(roadmapNode.getTitle(), "originalFileName.jpeg", "image/jpeg", "tempImage".getBytes()); @@ -144,13 +149,15 @@ class RoadmapCreateEventListenerTest { final Roadmap roadmap = new Roadmap(1L, "roadmapTitle", "inroduction", 10, RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, memberId, new RoadmapCategory("category"), new RoadmapTags(new ArrayList<>())); final RoadmapNode roadmapNode = new RoadmapNode("roadmapNodeTitle", "roadmapNodeContent"); - final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), new RoadmapNodes(List.of(roadmapNode))); + final RoadmapContent roadmapContent = new RoadmapContent("roadmapContent", roadmap.getId(), + new RoadmapNodes(List.of(roadmapNode))); final MultipartFile imageFile = new MockMultipartFile(roadmapNode.getTitle(), null, "image/jpeg", "tempImage".getBytes()); final FileInformation fileInformation = new FileInformation(null, imageFile.getSize(), imageFile.getContentType(), imageFile.getInputStream()); - final RoadmapNodeSaveDto roadmapNodeSaveDto = new RoadmapNodeSaveDto("roadmapNodeTitle", roadmapNode.getContent(), + final RoadmapNodeSaveDto roadmapNodeSaveDto = new RoadmapNodeSaveDto("roadmapNodeTitle", + roadmapNode.getContent(), List.of(fileInformation)); final RoadmapSaveDto roadmapSaveDto = new RoadmapSaveDto(1L, roadmap.getTitle(), roadmap.getIntroduction(), roadmapContent.getContent(), RoadmapDifficultyType.DIFFICULT, 10, List.of(roadmapNodeSaveDto), diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateServiceTest.java index 96e15b60d..d1b374bfe 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateServiceTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapCreateServiceTest.java @@ -3,9 +3,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import co.kirikiri.common.exception.AuthenticationException; import co.kirikiri.common.exception.BadRequestException; @@ -39,24 +44,23 @@ import co.kirikiri.roadmap.service.dto.request.RoadmapReviewSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSaveRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapTagSaveRequest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; 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 org.springframework.context.ApplicationEventPublisher; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; @ExtendWith(MockitoExtension.class) class RoadmapCreateServiceTest { private static final Member MEMBER = new Member(1L, new Identifier("identifier1"), null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임"), - null, - new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); + null, new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); @Mock private MemberRepository memberRepository; @@ -103,8 +107,10 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 given(roadmapCategoryRepository.findById(any())) .willReturn(Optional.of(category)); given(roadmapRepository.save(any())) - .willReturn(new Roadmap(1L, roadmapTitle, roadmapIntroduction, requiredPeriod, RoadmapDifficulty.valueOf(difficulty.name()), - RoadmapStatus.CREATED, MEMBER.getId(), category, new RoadmapTags(List.of(new RoadmapTag(new RoadmapTagName("태그 1")))))); + .willReturn(new Roadmap(1L, roadmapTitle, roadmapIntroduction, requiredPeriod, + RoadmapDifficulty.valueOf(difficulty.name()), + RoadmapStatus.CREATED, MEMBER.getId(), category, + new RoadmapTags(List.of(new RoadmapTag(new RoadmapTagName("태그 1")))))); given(roadmapContentRepository.save(any())) .willReturn(new RoadmapContent(roadmapContent, 1L, null)); @@ -159,8 +165,8 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 when(roadmapRepository.findById(anyLong())) .thenReturn(Optional.of(roadmap)); - when(roadmapGoalRoomService.findCompletedGoalRoomMember(anyLong(), any())) - .thenReturn(follower); + when(roadmapGoalRoomService.findCompletedGoalRoomMemberId(anyLong(), any())) + .thenReturn(follower.getId()); when(roadmapReviewRepository.findByRoadmapIdAndMemberId(any(), anyLong())) .thenReturn(Optional.empty()); @@ -193,7 +199,7 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 when(roadmapRepository.findById(anyLong())) .thenReturn(Optional.of(roadmap)); - when(roadmapGoalRoomService.findCompletedGoalRoomMember(anyLong(), any())) + when(roadmapGoalRoomService.findCompletedGoalRoomMemberId(anyLong(), any())) .thenThrow(new BadRequestException("Error Message")); final RoadmapReviewSaveRequest roadmapReviewSaveRequest = new RoadmapReviewSaveRequest("리뷰 내용", null); @@ -217,8 +223,8 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 when(roadmapRepository.findById(anyLong())) .thenReturn(Optional.of(roadmap)); - when(roadmapGoalRoomService.findCompletedGoalRoomMember(anyLong(), any())) - .thenReturn(follower); + when(roadmapGoalRoomService.findCompletedGoalRoomMemberId(anyLong(), any())) + .thenReturn(follower.getId()); when(roadmapReviewRepository.findByRoadmapIdAndMemberId(any(), anyLong())) .thenReturn(Optional.of(new RoadmapReview("로드맵 짱!", 5.0, MEMBER.getId(), roadmap.getId()))); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapReadServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapReadServiceTest.java index 66c15c0ef..c8368f514 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapReadServiceTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/roadmap/service/RoadmapReadServiceTest.java @@ -2,14 +2,18 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.when; import co.kirikiri.common.exception.NotFoundException; import co.kirikiri.common.service.FileService; +import co.kirikiri.common.service.dto.CustomScrollRequest; import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.GoalRoomStatus; +import co.kirikiri.goalroom.domain.GoalRoomStatus; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -39,6 +43,7 @@ import co.kirikiri.roadmap.service.dto.RoadmapGoalRoomsOrderTypeDto; import co.kirikiri.roadmap.service.dto.request.RoadmapOrderTypeRequest; import co.kirikiri.roadmap.service.dto.request.RoadmapSearchRequest; +import co.kirikiri.roadmap.service.dto.response.MemberRoadmapGoalRoomResponse; import co.kirikiri.roadmap.service.dto.response.MemberRoadmapResponse; import co.kirikiri.roadmap.service.dto.response.MemberRoadmapResponses; import co.kirikiri.roadmap.service.dto.response.RoadmapCategoryResponse; @@ -51,12 +56,6 @@ import co.kirikiri.roadmap.service.dto.response.RoadmapResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapReviewResponse; import co.kirikiri.roadmap.service.dto.response.RoadmapTagResponse; -import co.kirikiri.service.dto.CustomScrollRequest; -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 java.net.MalformedURLException; import java.net.URL; import java.time.LocalDate; @@ -65,6 +64,11 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +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; @ExtendWith(MockitoExtension.class) class RoadmapReadServiceTest { @@ -460,7 +464,8 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 void 로드맵의_골룸_목록을_최신순으로_조회한다() { // given final Member member1 = 사용자를_생성한다(1L, "identifier1", "name1"); - final Roadmap roadmap = new Roadmap(1L, "로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, + final Roadmap roadmap = new Roadmap(1L, "로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.DIFFICULT, + RoadmapStatus.CREATED, member1.getId(), new RoadmapCategory("it"), new RoadmapTags(new ArrayList<>())); final Member member2 = 사용자를_생성한다(2L, "identifier2", "name2"); @@ -471,12 +476,14 @@ null, new EncryptedPassword(new Password("password1!")), new Nickname("닉네임 new RoadmapGoalRoomResponse(2L, "goalroom2", GoalRoomStatus.RECRUITING.name(), 1, 10, LocalDateTime.now(), TODAY, TODAY.plusDays(20), - new MemberResponse(member3.getId(), member3.getNickname().getValue(), + new MemberRoadmapGoalRoomResponse(member3.getId(), + member3.getNickname().getValue(), "http://example.com/serverFilePath")), new RoadmapGoalRoomResponse(1L, "goalroom1", GoalRoomStatus.RECRUITING.name(), 1, 10, LocalDateTime.now(), TODAY, TODAY.plusDays(20), - new MemberResponse(member2.getId(), member2.getNickname().getValue(), + new MemberRoadmapGoalRoomResponse(member2.getId(), + member2.getNickname().getValue(), "http://example.com/serverFilePath"))), false); given(roadmapRepository.findById(anyLong())) @@ -514,7 +521,8 @@ RoadmapGoalRoomsOrderTypeDto.LATEST, new CustomScrollRequest(null, 10))) // given final Member member1 = 사용자를_생성한다(1L, "identifier1", "리뷰어1"); final Member member2 = 사용자를_생성한다(2L, "identifier2", "리뷰어2"); - final Roadmap roadmap = new Roadmap(1L, "로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.DIFFICULT, RoadmapStatus.CREATED, + final Roadmap roadmap = new Roadmap(1L, "로드맵 제목", "로드맵 설명", 100, RoadmapDifficulty.DIFFICULT, + RoadmapStatus.CREATED, member1.getId(), new RoadmapCategory("it"), new RoadmapTags(new ArrayList<>())); final RoadmapReview roadmapReview1 = new RoadmapReview("리뷰 내용", 5.0, member1.getId(), roadmap.getId()); diff --git a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomCreateServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomCreateServiceTest.java deleted file mode 100644 index 2d475c48e..000000000 --- a/backend/kirikiri/src/test/java/co/kirikiri/service/GoalRoomCreateServiceTest.java +++ /dev/null @@ -1,939 +0,0 @@ -package co.kirikiri.service; - -import static co.kirikiri.domain.goalroom.GoalRoomStatus.RUNNING; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; - -import co.kirikiri.common.exception.BadRequestException; -import co.kirikiri.common.exception.NotFoundException; -import co.kirikiri.common.exception.domain.ImageExtensionException; -import co.kirikiri.common.service.FilePathGenerator; -import co.kirikiri.common.service.FileService; -import co.kirikiri.common.type.ImageContentType; -import co.kirikiri.domain.goalroom.CheckFeed; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; -import co.kirikiri.member.domain.EncryptedPassword; -import co.kirikiri.member.domain.Gender; -import co.kirikiri.member.domain.Member; -import co.kirikiri.member.domain.MemberProfile; -import co.kirikiri.member.domain.vo.Identifier; -import co.kirikiri.member.domain.vo.Nickname; -import co.kirikiri.member.domain.vo.Password; -import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.goalroom.CheckFeedRepository; -import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository; -import co.kirikiri.persistence.goalroom.GoalRoomRepository; -import co.kirikiri.persistence.goalroom.GoalRoomToDoCheckRepository; -import co.kirikiri.roadmap.domain.Roadmap; -import co.kirikiri.roadmap.domain.RoadmapCategory; -import co.kirikiri.roadmap.domain.RoadmapContent; -import co.kirikiri.roadmap.domain.RoadmapDifficulty; -import co.kirikiri.roadmap.domain.RoadmapNode; -import co.kirikiri.roadmap.domain.RoadmapNodes; -import co.kirikiri.roadmap.domain.RoadmapStatus; -import co.kirikiri.roadmap.domain.RoadmapTags; -import co.kirikiri.roadmap.persistence.RoadmapContentRepository; -import co.kirikiri.roadmap.persistence.RoadmapRepository; -import co.kirikiri.service.dto.goalroom.request.CheckFeedRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomCreateRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomRoadmapNodeRequest; -import co.kirikiri.service.dto.goalroom.request.GoalRoomTodoRequest; -import co.kirikiri.service.dto.goalroom.response.GoalRoomToDoCheckResponse; -import co.kirikiri.service.goalroom.GoalRoomCreateService; -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 org.springframework.mock.web.MockMultipartFile; -import java.net.MalformedURLException; -import java.net.URL; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@ExtendWith(MockitoExtension.class) -class GoalRoomCreateServiceTest { - - private static final LocalDate TODAY = LocalDate.now(); - private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); - private static final LocalDate TWENTY_DAY_LATER = TODAY.plusDays(20); - - private static final Member MEMBER = new Member(new Identifier("identifier1"), - new EncryptedPassword(new Password("password1!")), - new Nickname("name1"), null, - new MemberProfile(Gender.FEMALE, "kirikiri@email.com")); - - private static final Roadmap ROADMAP = new Roadmap(1L, "roadmap", "introduction", 30, RoadmapDifficulty.DIFFICULT, - RoadmapStatus.CREATED, MEMBER.getId(), new RoadmapCategory("IT"), new RoadmapTags(new ArrayList<>())); - private static final Roadmap DELETED_ROADMAP = new Roadmap(2L, "roadmap", "introduction", 30, RoadmapDifficulty.DIFFICULT, - RoadmapStatus.DELETED, MEMBER.getId(), new RoadmapCategory("IT"), new RoadmapTags(new ArrayList<>())); - private static final RoadmapNode roadmapNode1 = new RoadmapNode(1L, "title1", "content1"); - private static final RoadmapNode roadmapNode2 = new RoadmapNode(2L, "title2", "content2"); - private static final RoadmapContent DELETED_ROADMAP_CONTENT = new RoadmapContent(2L, "content2", DELETED_ROADMAP.getId(), null); - private static final RoadmapContent ROADMAP_CONTENT = new RoadmapContent(1L, "content", ROADMAP.getId(), new RoadmapNodes(List.of(roadmapNode1, roadmapNode2))); - @Mock - private GoalRoomRepository goalRoomRepository; - - @Mock - private GoalRoomMemberRepository goalRoomMemberRepository; - - @Mock - private RoadmapRepository roadmapRepository; - - @Mock - private RoadmapContentRepository roadmapContentRepository; - - @Mock - private MemberRepository memberRepository; - - @Mock - private GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; - - @Mock - private CheckFeedRepository checkFeedRepository; - - @Mock - private FileService fileService; - - @Mock - private FilePathGenerator filePathGenerator; - - @InjectMocks - private GoalRoomCreateService goalRoomCreateService; - - @Test - void 정상적으로_골룸을_생성한다() { - //given - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", 20, - List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER), - new GoalRoomRoadmapNodeRequest(2L, 5, TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER))); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP_CONTENT)); - given(roadmapRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP)); - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.save(any())) - .willReturn(new GoalRoom(1L, null, null, null, null)); - - //when - assertDoesNotThrow(() -> goalRoomCreateService.create(request, MEMBER.getIdentifier().getValue())); - } - - @Test - void 골룸_생성_시_삭제된_로드맵이면_예외를_던진다() { - //given - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", - 20, new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)))); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.of(DELETED_ROADMAP_CONTENT)); - given(roadmapRepository.findById(anyLong())) - .willReturn(Optional.of(DELETED_ROADMAP)); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.create(request, MEMBER.getIdentifier().getValue())) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸_생성_시_존재하지_않은_로드맵_컨텐츠가_들어올때_예외를_던진다() { - //given - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", - 20, new ArrayList<>(List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)))); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.empty()); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.create(request, MEMBER.getIdentifier().getValue())) - .isInstanceOf(NotFoundException.class) - .hasMessage("존재하지 않는 로드맵 컨텐츠입니다."); - } - - @Test - void 골룸_생성_시_로드맵_컨텐츠의_노드사이즈와_요청의_노드사이즈가_다를때_예외를_던진다() { - //given - final List wrongSizeGoalRoomRoadmapNodeRequest = List.of( - new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER)); - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", - 20, wrongSizeGoalRoomRoadmapNodeRequest); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP_CONTENT)); - given(roadmapRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP)); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.create(request, MEMBER.getIdentifier().getValue())) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸_생성_시_로드맵에_존재하지_않는_노드가_요청으로_들어올때_예외를_던진다() { - //given - final long wrongRoadmapNodId = 3L; - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", 20, - List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER), - new GoalRoomRoadmapNodeRequest(wrongRoadmapNodId, 5, TEN_DAY_LATER, TWENTY_DAY_LATER))); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP_CONTENT)); - given(roadmapRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP)); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.create(request, MEMBER.getIdentifier().getValue())) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸_생성_시_존재하지_않은_회원의_Identifier가_들어올때_예외를_던진다() { - //given - final GoalRoomCreateRequest request = new GoalRoomCreateRequest(1L, "name", 20, - List.of(new GoalRoomRoadmapNodeRequest(1L, 10, TODAY, TEN_DAY_LATER), - new GoalRoomRoadmapNodeRequest(2L, 5, TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER))); - - given(roadmapContentRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP_CONTENT)); - given(roadmapRepository.findById(anyLong())) - .willReturn(Optional.of(ROADMAP)); - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.empty()); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.create(request, "identifier")) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸에_참가한다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final Member follower = 사용자를_생성한다(2L, "identifier2", "password2@", "팔로워", "kirikiri1@email"); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(follower)); - when(goalRoomRepository.findGoalRoomByIdWithPessimisticLock(anyLong())) - .thenReturn(Optional.of(goalRoom)); - - //when - goalRoomCreateService.join(follower.getIdentifier().getValue(), goalRoom.getId()); - - //then - assertThat(goalRoom.getCurrentMemberCount()) - .isEqualTo(2); - } - - @Test - void 골룸_참가_요청시_유효한_사용자_아이디가_아니면_예외가_발생한다() { - //given - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.empty()); - - //when, then - assertThatThrownBy(() -> goalRoomCreateService.join("identifier", 1L)) - .isInstanceOf(NotFoundException.class) - .hasMessage("존재하지 않는 회원입니다."); - } - - @Test - void 골룸_참가_요청시_유효한_골룸_아이디가_아니면_예외가_발생한다() { - //given - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(MEMBER)); - when(goalRoomRepository.findGoalRoomByIdWithPessimisticLock(anyLong())) - .thenReturn(Optional.empty()); - - //when, then - assertThatThrownBy(() -> goalRoomCreateService.join("identifier1", 1L)) - .isInstanceOf(NotFoundException.class) - .hasMessage("존재하지 않는 골룸입니다. goalRoomId = 1"); - } - - @Test - void 골룸_참가_요청시_제한_인원이_가득_찼을_경우_예외가_발생한다() { - //given - final int limitedMemberCount = 1; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final Member follower = 사용자를_생성한다(1L, "identifier2", "password1!", "팔로워", "kirikiri1@email"); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(follower)); - when(goalRoomRepository.findGoalRoomByIdWithPessimisticLock(anyLong())) - .thenReturn(Optional.of(goalRoom)); - - //when, then - assertThatThrownBy(() -> goalRoomCreateService.join(follower.getIdentifier().getValue(), goalRoom.getId())) - .isInstanceOf(GoalRoomException.class) - .hasMessage("제한 인원이 꽉 찬 골룸에는 참여할 수 없습니다."); - } - - @Test - void 골룸_참가_요청시_모집_중이_아닌_경우_예외가_발생한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - goalRoom.start(); - - final Member follower = 사용자를_생성한다(2L, "identifier2", "password2!", "팔로워", "kirikiri1@email"); - - // when - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(follower)); - when(goalRoomRepository.findGoalRoomByIdWithPessimisticLock(anyLong())) - .thenReturn(Optional.of(goalRoom)); - - // then - assertThatThrownBy(() -> goalRoomCreateService.join(follower.getIdentifier().getValue(), 1L)) - .isInstanceOf(GoalRoomException.class) - .hasMessage("모집 중이지 않은 골룸에는 참여할 수 없습니다."); - } - - @Test - void 정상적으로_골룸에_투두리스트를_추가한다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - goalRoom.addGoalRoomTodo( - new GoalRoomToDo(new GoalRoomTodoContent("goalRoomTodoContent"), new Period(TODAY, TEN_DAY_LATER))); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, - TEN_DAY_LATER); - - //when - //then - assertDoesNotThrow(() -> goalRoomCreateService.addGoalRoomTodo(1L, MEMBER.getIdentifier().getValue(), goalRoomTodoRequest)); - } - - @Test - void 골룸에_투두리스트_추가시_회원을_찾지_못할_경우_예외를_던진다() { - //given - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.empty()); - - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, - TEN_DAY_LATER); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.addGoalRoomTodo(1L, "identifier", goalRoomTodoRequest)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸에_투두리스트_추가시_골룸을_찾지_못할_경우_예외를_던진다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - goalRoom.addGoalRoomTodo( - new GoalRoomToDo(new GoalRoomTodoContent("goalRoomTodoContent"), new Period(TODAY, TEN_DAY_LATER))); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.empty()); - - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, - TEN_DAY_LATER); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.addGoalRoomTodo(2L, MEMBER.getIdentifier().getValue(), goalRoomTodoRequest)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸에_투두리스트_추가시_종료된_골룸일_경우_예외를_던진다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - goalRoom.addGoalRoomTodo( - new GoalRoomToDo(new GoalRoomTodoContent("goalRoomTodoContent"), new Period(TODAY, TEN_DAY_LATER))); - goalRoom.complete(); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, - TEN_DAY_LATER); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.addGoalRoomTodo(1L, MEMBER.getIdentifier().getValue(), goalRoomTodoRequest)) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸에_투두리스트_추가시_리더가_아닐_경우_예외를_던진다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final Member follower = 사용자를_생성한다(2L, "identifier2", "password2@", "팔로워", "kirikiri1@email"); - - goalRoom.addGoalRoomTodo( - new GoalRoomToDo(new GoalRoomTodoContent("goalRoomTodoContent"), new Period(TODAY, TEN_DAY_LATER))); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(follower)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, - TEN_DAY_LATER); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.addGoalRoomTodo(1L, follower.getIdentifier().getValue(), goalRoomTodoRequest)) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸에_투두리스트_추가시_골룸_컨텐츠가_250글자가_넘을때_예외를_던진다() { - //given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - goalRoom.addGoalRoomTodo( - new GoalRoomToDo(new GoalRoomTodoContent("goalRoomTodoContent"), new Period(TODAY, TEN_DAY_LATER))); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - final String goalRoomTodoContent = "a".repeat(251); - final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest(goalRoomTodoContent, TODAY, - TEN_DAY_LATER); - - //when - //then - assertThatThrownBy(() -> goalRoomCreateService.addGoalRoomTodo(1L, MEMBER.getIdentifier().getValue(), goalRoomTodoRequest)) - .isInstanceOf(GoalRoomException.class); - } - - @Test - void 골룸을_시작한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(MEMBER)); - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - - // when - goalRoomCreateService.startGoalRoom(MEMBER.getIdentifier().getValue(), 1L); - - // then - assertThat(goalRoom.getStatus()).isEqualTo(RUNNING); - } - - @Test - void 골룸_시작시_존재하지_않는_사용자면_예외가_발생한다() { - // given - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("identifier", 1L)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸_시작시_존재하지_않는_골룸이면_예외가_발생한다() { - // given - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(MEMBER)); - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom("identifier", 1L)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸을_시작하는_사용자가_골룸의_리더가_아니면_예외가_발생한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final Member follower = 사용자를_생성한다(2L, "kirikirico", "password2!", "끼리코", "kirikiri1@email"); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(follower)); - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom(follower.getIdentifier().getValue(), 1L)) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸_시작시_시작날짜가_아직_지나지_않았으면_예외가_발생한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 시작_날짜가_미래인_골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - when(memberRepository.findByIdentifier(any())) - .thenReturn(Optional.of(MEMBER)); - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.startGoalRoom(MEMBER.getIdentifier().getValue(), 1L)) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 인증_피드_등록을_요청한다() { - // given - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - final CheckFeed checkFeed = 인증_피드를_생성한다(goalRoomRoadmapNode, goalRoomLeader); - - when(goalRoomRepository.findById(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomLeader)); - when(checkFeedRepository.findByGoalRoomMemberAndDateTime(any(), any(), any())) - .thenReturn(Optional.empty()); - when(checkFeedRepository.countByGoalRoomMemberAndGoalRoomRoadmapNode(any(), any())) - .thenReturn(0); - when(checkFeedRepository.save(any())) - .thenReturn(checkFeed); - when(filePathGenerator.makeFilePath(any(), any())) - .thenReturn("originalFileName.jpeg"); - when(fileService.generateUrl(anyString(), any())) - .thenReturn(makeUrl("originalFileName.jpeg")); - - // when - final String response = goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), 1L, request); - - // then - assertAll( - () -> assertThat(goalRoomLeader.getAccomplishmentRate()).isEqualTo(100 / (double) 10), - () -> assertThat(response).contains("originalFileName") - ); - } - - @Test - void 인증_피드_등록시_노드_기간에_해당하지_않으면_예외가_발생한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 시작_날짜가_미래인_골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - - when(goalRoomRepository.findById(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomLeader)); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), goalRoom.getId(), request)) - .isInstanceOf(BadRequestException.class) - .hasMessage("인증 피드는 노드 기간 내에만 작성할 수 있습니다."); - } - - @Test - void 하루에_두_번_이상_인증_피드_등록_요청_시_예외를_반환한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - final CheckFeed checkFeed = 인증_피드를_생성한다(goalRoomRoadmapNode, goalRoomLeader); - - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomLeader)); - when(checkFeedRepository.findByGoalRoomMemberAndDateTime(any(), any(), any())) - .thenReturn(Optional.of(checkFeed)); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), goalRoom.getId(), request)) - .isInstanceOf(BadRequestException.class) - .hasMessage("이미 오늘 인증 피드를 등록하였습니다."); - } - - @Test - void 골룸_노드에서_허가된_인증_횟수보다_많은_인증_피드_등록_요청_시_예외를_반환한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - - goalRoomMemberRepository.save(goalRoomLeader); - final GoalRoomRoadmapNode goalRoomRoadmapNode = goalRoom.getGoalRoomRoadmapNodes().getValues().get(0); - - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomLeader)); - when(checkFeedRepository.countByGoalRoomMemberAndGoalRoomRoadmapNode(any(), any())) - .thenReturn(goalRoomRoadmapNode.getCheckCount()); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), goalRoom.getId(), request)) - .isInstanceOf(BadRequestException.class) - .hasMessage("이번 노드에는 최대 " + goalRoomRoadmapNode.getCheckCount() + "번만 인증 피드를 등록할 수 있습니다."); - } - - @Test - void 인증_피드_등록_요청_시_허용되지_않는_확장자_형식이라면_예외를_반환한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomLeader)); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/gif"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), goalRoom.getId(), request)) - .isInstanceOf(ImageExtensionException.class) - .hasMessage("허용되지 않는 확장자입니다."); - } - - @Test - void 인증_피드_등록_요청_시_존재하지_않는_골룸이라면_예외를_반환한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.empty()); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed(MEMBER.getIdentifier().getValue(), goalRoom.getId(), request)) - .isInstanceOf(NotFoundException.class) - .hasMessage("존재하지 않는 골룸입니다. goalRoomId = 1"); - } - - @Test - void 인증_피드_등록_요청_시_사용자가_참여하지_않은_골룸이라면_예외를_반환한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomMember goalRoomLeader = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - goalRoomMemberRepository.save(goalRoomLeader); - - when(goalRoomRepository.findById(any())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.empty()); - - // when - final CheckFeedRequest request = 인증_피드_요청_DTO를_생성한다("image/jpeg"); - - // then - assertThatThrownBy( - () -> goalRoomCreateService.createCheckFeed("identifier", goalRoom.getId(), request)) - .isInstanceOf(NotFoundException.class) - .hasMessage("골룸에 해당 사용자가 존재하지 않습니다. 사용자 아이디 = " + "identifier"); - } - - @Test - void 투두리스트를_체크한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - goalRoom.addGoalRoomTodo(new GoalRoomToDo( - 1L, new GoalRoomTodoContent("투두 1"), new Period(TODAY, TODAY.plusDays(3)))); - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - - when(goalRoomRepository.findByIdWithTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomMember)); - when(goalRoomToDoCheckRepository.findByGoalRoomIdAndTodoAndMemberIdentifier(any(), any(), any())) - .thenReturn(Optional.empty()); - - // when - final GoalRoomToDoCheckResponse checkResponse = goalRoomCreateService.checkGoalRoomTodo(goalRoom.getId(), 1L, MEMBER.getIdentifier().getValue()); - - // then - assertThat(checkResponse) - .isEqualTo(new GoalRoomToDoCheckResponse(true)); - } - - @Test - void 투두리스트_체크시_체크_이력이_있으면_제거한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - final GoalRoomToDo goalRoomToDo = new GoalRoomToDo( - 1L, new GoalRoomTodoContent("투두 1"), new Period(TODAY, TODAY.plusDays(3))); - goalRoom.addGoalRoomTodo(goalRoomToDo); - - final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, MEMBER); - final GoalRoomToDoCheck goalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, goalRoomToDo); - - when(goalRoomRepository.findByIdWithTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.of(goalRoomMember)); - when(goalRoomToDoCheckRepository.findByGoalRoomIdAndTodoAndMemberIdentifier(any(), any(), any())) - .thenReturn(Optional.of(goalRoomToDoCheck)); - - // when - final GoalRoomToDoCheckResponse checkResponse = goalRoomCreateService.checkGoalRoomTodo(goalRoom.getId(), 1L, MEMBER.getIdentifier().getValue()); - - // then - assertThat(checkResponse) - .isEqualTo(new GoalRoomToDoCheckResponse(false)); - } - - @Test - void 투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() { - // given - when(goalRoomRepository.findByIdWithTodos(anyLong())) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.checkGoalRoomTodo(1L, 1L, "cokirikiri")) - .isInstanceOf(NotFoundException.class) - .hasMessage("골룸이 존재하지 않습니다. goalRoomId = 1"); - } - - @Test - void 투두리스트_체크시_해당_투두가_존재하지_않으면_예외가_발생한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - goalRoom.addGoalRoomTodo(new GoalRoomToDo( - 1L, new GoalRoomTodoContent("투두 1"), new Period(TODAY, TODAY.plusDays(3)))); - - when(goalRoomRepository.findByIdWithTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.checkGoalRoomTodo(goalRoom.getId(), 2L, MEMBER.getIdentifier().getValue())) - .isInstanceOf(NotFoundException.class) - .hasMessage("존재하지 않는 투두입니다. todoId = 2"); - } - - @Test - void 투두리스트_체크시_골룸에_사용자가_없으면_예외가_발생한다() { - // given - final int limitedMemberCount = 10; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - goalRoom.addGoalRoomTodo(new GoalRoomToDo( - 1L, new GoalRoomTodoContent("투두 1"), new Period(TODAY, TODAY.plusDays(3)))); - - when(goalRoomRepository.findByIdWithTodos(anyLong())) - .thenReturn(Optional.of(goalRoom)); - when(goalRoomMemberRepository.findByGoalRoomAndMemberIdentifier(any(), any())) - .thenReturn(Optional.empty()); - - // expected - assertThatThrownBy(() -> goalRoomCreateService.checkGoalRoomTodo(goalRoom.getId(), 1L, "cokirikiri")) - .isInstanceOf(NotFoundException.class) - .hasMessage("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri"); - } - - @Test - void 골룸을_나간다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - // when - // then - assertDoesNotThrow(() -> goalRoomCreateService.leave(MEMBER.getIdentifier().getValue(), goalRoom.getId())); - - } - - @Test - void 골룸을_나갈때_존재하지_않는_회원일_경우_예외가_발생한다() { - // given - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.empty()); - - // when - // then - assertThatThrownBy(() -> goalRoomCreateService.leave("identifier2", 1L)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸을_나갈때_존재하지_않는_골룸일_경우_예외가_발생한다() { - // given - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.empty()); - - // when - // then - assertThatThrownBy(() -> goalRoomCreateService.leave(MEMBER.getIdentifier().getValue(), 1L)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void 골룸을_나갈때_골룸이_진행중이면_예외가_발생한다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - // when - goalRoom.start(); - - // then - assertThatThrownBy(() -> goalRoomCreateService.leave(MEMBER.getIdentifier().getValue(), goalRoom.getId())) - .isInstanceOf(BadRequestException.class); - } - - @Test - void 골룸을_나갈때_골룸에_남아있는_사용자가_없으면_골룸이_삭제된다() { - // given - final int limitedMemberCount = 20; - final GoalRoom goalRoom = 골룸을_생성한다(1L, MEMBER, ROADMAP_CONTENT, limitedMemberCount); - - given(memberRepository.findByIdentifier(any())) - .willReturn(Optional.of(MEMBER)); - given(goalRoomRepository.findById(anyLong())) - .willReturn(Optional.of(goalRoom)); - - // when - goalRoomCreateService.leave(MEMBER.getIdentifier().getValue(), goalRoom.getId()); - - // then - verify(goalRoomRepository, times(1)).delete(goalRoom); - } - - private Member 사용자를_생성한다(final Long memberId, final String identifier, final String password, final String nickname, - final String email) { - final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); - return new Member(memberId, new Identifier(identifier), null, new EncryptedPassword(new Password(password)), - new Nickname(nickname), null, memberProfile); - } - - private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final RoadmapContent roadmapContent, - final Integer limitedMemberCount) { - final GoalRoom goalRoom = new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), - new LimitedMemberCount(limitedMemberCount), roadmapContent, creator); - goalRoom.addAllGoalRoomRoadmapNodes(골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); - return goalRoom; - } - - private GoalRoom 시작_날짜가_미래인_골룸을_생성한다(final Long goalRoomId, final Member creator, - final RoadmapContent roadmapContent, final Integer limitedMemberCount) { - final GoalRoom goalRoom = new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), - new LimitedMemberCount(limitedMemberCount), roadmapContent, creator); - final GoalRoomRoadmapNode goalRoomRoadmapNode = new GoalRoomRoadmapNode( - new Period(TEN_DAY_LATER, TWENTY_DAY_LATER), 5, roadmapContent.getNodes().getValues().get(0)); - goalRoom.addAllGoalRoomRoadmapNodes( - new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode))); - return goalRoom; - } - - private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다(final RoadmapNodes roadmapNodes) { - return new GoalRoomRoadmapNodes(List.of( - new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0)), - new GoalRoomRoadmapNode(new Period(TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER), 5, - roadmapNodes.getValues().get(1))) - ); - } - - private CheckFeedRequest 인증_피드_요청_DTO를_생성한다(final String contentType) { - return new CheckFeedRequest( - new MockMultipartFile("image", "originalFileName.jpeg", contentType, - "test image".getBytes()), "인증 피드 설명"); - } - - private CheckFeed 인증_피드를_생성한다(final GoalRoomRoadmapNode goalRoomRoadmapNode, final GoalRoomMember joinedMember) { - return new CheckFeed("src/test/resources/testImage/originalFileName.jpeg", ImageContentType.JPEG, - "originalFileName.jpeg", "인증 피드 설명", goalRoomRoadmapNode, joinedMember); - } - - private URL makeUrl(final String path) { - try { - return new URL("http://example.com/" + path); - } catch (final MalformedURLException e) { - throw new RuntimeException(e); - } - } -} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/todo/controller/GoalRoomToDoApiTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/controller/GoalRoomToDoApiTest.java new file mode 100644 index 000000000..d0e9e34aa --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/controller/GoalRoomToDoApiTest.java @@ -0,0 +1,494 @@ +package co.kirikiri.todo.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.ForbiddenException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.helper.ControllerTestHelper; +import co.kirikiri.common.helper.FieldDescriptionHelper.FieldDescription; +import co.kirikiri.common.service.dto.ErrorResponse; +import co.kirikiri.todo.service.GoalRoomToDoService; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; + +@WebMvcTest(GoalRoomToDoController.class) +class GoalRoomToDoApiTest extends ControllerTestHelper { + + private static final LocalDate TODAY = LocalDate.now(); + private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); + + @MockBean + private GoalRoomToDoService goalRoomToDoService; + + @Test + void 정상적으로_골룸에_투두리스트를_추가한다() throws Exception { + //given + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + given(goalRoomToDoService.addGoalRoomTodo(anyLong(), anyString(), any())) + .willReturn(1L); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isCreated()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰")), + responseHeaders(headerWithName(HttpHeaders.LOCATION).description("골룸 투두 단일 조회 api 경로")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")))) + .andReturn(); + + //then + assertThat(mvcResult.getResponse().getHeader(HttpHeaders.LOCATION)).isEqualTo( + API_PREFIX + "/goal-rooms/1/todos/1"); + } + + @Test + void 골룸_투두_추가시_존재하지_않는_회원일_경우() throws Exception { + //given + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + doThrow(new NotFoundException("존재하지 않는 회원입니다.")) + .when(goalRoomToDoService) + .addGoalRoomTodo(anyLong(), anyString(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("존재하지 않는 회원입니다.")); + } + + @Test + void 골룸_투두_추가시_존재하지_않는_골룸일_경우() throws Exception { + //given + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) + .when(goalRoomToDoService) + .addGoalRoomTodo(anyLong(), anyString(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸_투두_추가시_이미_종료된_골룸일_경우() throws Exception { + //given + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + doThrow(new BadRequestException("이미 종료된 골룸입니다.")) + .when(goalRoomToDoService) + .addGoalRoomTodo(anyLong(), anyString(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("이미 종료된 골룸입니다.")); + } + + @Test + void 골룸_투두_추가시_리더가_아닌_경우() throws Exception { + //given + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("content", TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + doThrow(new BadRequestException("골룸의 리더만 투두리스트를 추가할 수 있습니다.")) + .when(goalRoomToDoService) + .addGoalRoomTodo(anyLong(), anyString(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("골룸의 리더만 투두리스트를 추가할 수 있습니다.")); + } + + @Test + void 골룸_투두_추가시_컨텐츠가_250글자가_넘을_경우() throws Exception { + //given + final String content = "a".repeat(251); + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest(content, TODAY, TEN_DAY_LATER); + final String jsonRequest = objectMapper.writeValueAsString(goalRoomTodoRequest); + doThrow(new BadRequestException("투두 컨텐츠의 길이가 적절하지 않습니다.")) + .when(goalRoomToDoService) + .addGoalRoomTodo(anyLong(), anyString(), any()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestFields(makeFieldDescriptor(makeAddTodoSuccessRequestFieldDescription())), + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters(parameterWithName("goalRoomId").description("골룸 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("투두 컨텐츠의 길이가 적절하지 않습니다.")); + } + + @Test + void 골룸_투두리스트에_대해_체크한다() throws Exception { + // given + final GoalRoomToDoCheckResponse expected = new GoalRoomToDoCheckResponse(true); + when(goalRoomToDoService.checkGoalRoomTodo(anyLong(), anyLong(), anyString())) + .thenReturn(expected); + + // when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isOk()) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디"), + parameterWithName("todoId").description("골룸 투두 아이디")), + responseFields( + fieldWithPath("isChecked").description( + "투두 체크 현황 (true: 체크됨, false: 체크되지 않음)")))) + .andReturn(); + + // then + final GoalRoomToDoCheckResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(response) + .isEqualTo(expected); + } + + @Test + void 골룸_투두리스트_체크시_체크_이력이_있으면_제거한다() throws Exception { + // given + final GoalRoomToDoCheckResponse expected = new GoalRoomToDoCheckResponse(false); + when(goalRoomToDoService.checkGoalRoomTodo(anyLong(), anyLong(), anyString())) + .thenReturn(expected); + + // when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isOk()) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디"), + parameterWithName("todoId").description("골룸 투두 아이디")), + responseFields( + fieldWithPath("isChecked").description( + "투두 체크 현황 (true: 체크됨, false: 체크되지 않음)")))) + .andReturn(); + + // then + final GoalRoomToDoCheckResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(response) + .isEqualTo(expected); + } + + @Test + void 골룸_투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() throws Exception { + //given + doThrow(new NotFoundException("골룸이 존재하지 않습니다. goalRoomId = 1")) + .when(goalRoomToDoService) + .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestHeaders(headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디"), + parameterWithName("todoId").description("골룸 투두 아이디")), + responseFields(fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response).isEqualTo(new ErrorResponse("골룸이 존재하지 않습니다. goalRoomId = 1")); + } + + @Test + void 골룸_투두리스트_체크시_해당_투두가_존재하지_않으면_예외가_발생한다() throws Exception { + //given + doThrow(new NotFoundException("존재하지 않는 투두입니다. todoId = 1")) + .when(goalRoomToDoService) + .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디"), + parameterWithName("todoId").description("골룸 투두 아이디")), + responseFields( + fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response) + .isEqualTo(new ErrorResponse("존재하지 않는 투두입니다. todoId = 1")); + } + + @Test + void 골룸_투두리스트_체크시_사용자가_없으면_예외가_발생한다() throws Exception { + //given + doThrow(new NotFoundException("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri")) + .when(goalRoomToDoService) + .checkGoalRoomTodo(anyLong(), anyLong(), anyString()); + + //when + final MvcResult mvcResult = mockMvc.perform(post(API_PREFIX + "/goal-rooms/{goalRoomId}/todos/{todoId}", 1L, 1L) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + .contentType(MediaType.APPLICATION_JSON) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andDo(documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰")), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디"), + parameterWithName("todoId").description("골룸 투두 아이디")), + responseFields( + fieldWithPath("message").description("예외 메세지")))) + .andReturn(); + + //then + final ErrorResponse response = jsonToClass(mvcResult, new TypeReference<>() { + }); + assertThat(response) + .isEqualTo(new ErrorResponse("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri")); + } + + @Test + void 골룸의_투두리스트를_조회한다() throws Exception { + // given + final LocalDate today = LocalDate.now(); + final List goalRoomTodoResponses = List.of( + new GoalRoomTodoResponse(1L, "투두 1", today, today.plusDays(10), new GoalRoomToDoCheckResponse(true)), + new GoalRoomTodoResponse(2L, "투두 2", today.plusDays(20), today.plusDays(30), + new GoalRoomToDoCheckResponse(false))); + + when(goalRoomToDoService.findAllGoalRoomTodo(any(), any())) + .thenReturn(goalRoomTodoResponses); + + // when + final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isOk()) + .andDo( + documentationResultHandler.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰") + ), + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("[0].id").description("투두 아이디"), + fieldWithPath("[0].content").description("투두 내용"), + fieldWithPath("[0].startDate").description("투두 시작 날짜"), + fieldWithPath("[0].endDate").description("투두 종료 날짜"), + fieldWithPath("[0].check.isChecked").description("투두 체크 여부") + ))) + .andReturn(); + + // then + final List response = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(response) + .isEqualTo(goalRoomTodoResponses); + } + + @Test + void 골룸_투두리스트_조회시_존재하지_않은_골룸일_경우() throws Exception { + // given + doThrow(new NotFoundException("존재하지 않는 골룸입니다. goalRoomId = 1")) + .when(goalRoomToDoService) + .findAllGoalRoomTodo(any(), any()); + + // when + final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isNotFound()) + .andDo( + documentationResultHandler.document( + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))) + .andReturn(); + + // then + final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(responses) + .isEqualTo(new ErrorResponse("존재하지 않는 골룸입니다. goalRoomId = 1")); + } + + @Test + void 골룸_투두리스트_조회시_참여하지_않은_사용자일_경우() throws Exception { + // given + doThrow(new ForbiddenException("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier")) + .when(goalRoomToDoService) + .findAllGoalRoomTodo(any(), any()); + + // when + final MvcResult mvcResult = mockMvc.perform(get(API_PREFIX + "/goal-rooms/{goalRoomId}/todos", 1L) + .header(AUTHORIZATION, String.format(BEARER_TOKEN_FORMAT, "test-token")) + .contextPath(API_PREFIX)) + .andExpect(status().isForbidden()) + .andDo( + documentationResultHandler.document( + pathParameters( + parameterWithName("goalRoomId").description("골룸 아이디") + ), + responseFields( + fieldWithPath("message").description("예외 메세지") + ))) + .andReturn(); + + // then + final ErrorResponse responses = jsonToClass(mvcResult, new TypeReference<>() { + }); + + assertThat(responses) + .isEqualTo(new ErrorResponse("골룸에 참여하지 않은 사용자입니다. goalRoomId = 1 memberIdentifier = identifier")); + } + + private List makeAddTodoSuccessRequestFieldDescription() { + return List.of( + new FieldDescription("content", "골룸 투두 컨텐츠", "- 길이 : 1 ~ 250"), + new FieldDescription("startDate", "골룸 투두 시작일", "- yyyyMMdd 형식"), + new FieldDescription("endDate", "골룸 투두 종료일", "- yyyyMMdd 형식") + ); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDoTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDoTest.java similarity index 64% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDoTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDoTest.java index 0e2a2fcb7..f23f6991a 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDoTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDoTest.java @@ -1,14 +1,14 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.todo.domain; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.todo.domain.exception.GoalRoomToDoException; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; +import java.time.LocalDate; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.time.LocalDate; class GoalRoomToDoTest { @@ -21,7 +21,8 @@ class GoalRoomToDoTest { //when //then - assertDoesNotThrow(() -> new GoalRoomToDo(new GoalRoomTodoContent("content"), new Period(startDate, endDate))); + assertDoesNotThrow( + () -> new GoalRoomToDo(null, new GoalRoomTodoContent("content"), new ToDoPeriod(startDate, endDate))); } @ParameterizedTest @@ -33,8 +34,9 @@ class GoalRoomToDoTest { //when //then - assertThatThrownBy(() -> new GoalRoomToDo(new GoalRoomTodoContent("content"), new Period(startDate, endDate))) - .isInstanceOf(GoalRoomException.class); + assertThatThrownBy( + () -> new GoalRoomToDo(null, new GoalRoomTodoContent("content"), new ToDoPeriod(startDate, endDate))) + .isInstanceOf(GoalRoomToDoException.class); } @ParameterizedTest @@ -46,7 +48,8 @@ class GoalRoomToDoTest { //when //then - assertThatThrownBy(() -> new GoalRoomToDo(new GoalRoomTodoContent("content"), new Period(startDate, endDate))) - .isInstanceOf(GoalRoomException.class); + assertThatThrownBy( + () -> new GoalRoomToDo(null, new GoalRoomTodoContent("content"), new ToDoPeriod(startDate, endDate))) + .isInstanceOf(GoalRoomToDoException.class); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDosTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDosTest.java similarity index 51% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDosTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDosTest.java index 3de322e61..5bdaebb80 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/GoalRoomToDosTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/GoalRoomToDosTest.java @@ -1,22 +1,22 @@ -package co.kirikiri.domain.goalroom; +package co.kirikiri.todo.domain; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.Period; -import org.junit.jupiter.api.Test; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; import java.time.LocalDate; import java.util.List; +import org.junit.jupiter.api.Test; class GoalRoomToDosTest { @Test void 아이디로_투두를_조회한다() { // given - final GoalRoomToDo firstTodo = new GoalRoomToDo(1L, new GoalRoomTodoContent("투두1"), - new Period(LocalDate.now(), LocalDate.now().plusDays(3))); - final GoalRoomToDo secondTodo = new GoalRoomToDo(2L, new GoalRoomTodoContent("투두2"), - new Period(LocalDate.now(), LocalDate.now().plusDays(5))); + final GoalRoomToDo firstTodo = new GoalRoomToDo(1L, null, new GoalRoomTodoContent("투두1"), + new ToDoPeriod(LocalDate.now(), LocalDate.now().plusDays(3))); + final GoalRoomToDo secondTodo = new GoalRoomToDo(2L, null, new GoalRoomTodoContent("투두2"), + new ToDoPeriod(LocalDate.now(), LocalDate.now().plusDays(5))); final GoalRoomToDos goalRoomToDos = new GoalRoomToDos(List.of( firstTodo, secondTodo @@ -33,10 +33,10 @@ class GoalRoomToDosTest { @Test void 아이디로_투두_조회시_없으면_빈값을_반환한다() { // given - final GoalRoomToDo firstTodo = new GoalRoomToDo(1L, new GoalRoomTodoContent("투두1"), - new Period(LocalDate.now(), LocalDate.now().plusDays(3))); - final GoalRoomToDo secondTodo = new GoalRoomToDo(2L, new GoalRoomTodoContent("투두2"), - new Period(LocalDate.now(), LocalDate.now().plusDays(5))); + final GoalRoomToDo firstTodo = new GoalRoomToDo(1L, null, new GoalRoomTodoContent("투두1"), + new ToDoPeriod(LocalDate.now(), LocalDate.now().plusDays(3))); + final GoalRoomToDo secondTodo = new GoalRoomToDo(2L, null, new GoalRoomTodoContent("투두2"), + new ToDoPeriod(LocalDate.now(), LocalDate.now().plusDays(5))); final GoalRoomToDos goalRoomToDos = new GoalRoomToDos(List.of( firstTodo, secondTodo diff --git a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContentTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContentTest.java similarity index 85% rename from backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContentTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContentTest.java index ef200e17c..5e104da82 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/domain/goalroom/vo/GoalRoomTodoContentTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/domain/vo/GoalRoomTodoContentTest.java @@ -1,9 +1,9 @@ -package co.kirikiri.domain.goalroom.vo; +package co.kirikiri.todo.domain.vo; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import co.kirikiri.domain.goalroom.exception.GoalRoomException; +import co.kirikiri.todo.domain.exception.GoalRoomToDoException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -29,6 +29,6 @@ class GoalRoomTodoContentTest { //when //then assertThatThrownBy(() -> new GoalRoomTodoContent(value)) - .isInstanceOf(GoalRoomException.class); + .isInstanceOf(GoalRoomToDoException.class); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepositoryTest.java similarity index 58% rename from backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepositoryTest.java rename to backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepositoryTest.java index dcb93f110..c50e98bfb 100644 --- a/backend/kirikiri/src/test/java/co/kirikiri/persistence/goalroom/GoalRoomToDoCheckRepositoryTest.java +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoCheckRepositoryTest.java @@ -1,18 +1,22 @@ -package co.kirikiri.persistence.goalroom; +package co.kirikiri.todo.persistence; import static org.assertj.core.api.Assertions.assertThat; -import co.kirikiri.domain.goalroom.GoalRoom; -import co.kirikiri.domain.goalroom.GoalRoomMember; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNode; -import co.kirikiri.domain.goalroom.GoalRoomRoadmapNodes; -import co.kirikiri.domain.goalroom.GoalRoomRole; -import co.kirikiri.domain.goalroom.GoalRoomToDo; -import co.kirikiri.domain.goalroom.GoalRoomToDoCheck; -import co.kirikiri.domain.goalroom.vo.GoalRoomName; -import co.kirikiri.domain.goalroom.vo.GoalRoomTodoContent; -import co.kirikiri.domain.goalroom.vo.LimitedMemberCount; -import co.kirikiri.domain.goalroom.vo.Period; +import co.kirikiri.common.helper.RepositoryTest; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; import co.kirikiri.member.domain.EncryptedPassword; import co.kirikiri.member.domain.Gender; import co.kirikiri.member.domain.Member; @@ -21,7 +25,6 @@ import co.kirikiri.member.domain.vo.Nickname; import co.kirikiri.member.domain.vo.Password; import co.kirikiri.member.persistence.MemberRepository; -import co.kirikiri.persistence.helper.RepositoryTest; import co.kirikiri.roadmap.domain.Roadmap; import co.kirikiri.roadmap.domain.RoadmapCategory; import co.kirikiri.roadmap.domain.RoadmapContent; @@ -49,6 +52,7 @@ class GoalRoomToDoCheckRepositoryTest { private final GoalRoomRepository goalRoomRepository; private final GoalRoomMemberRepository goalRoomMemberRepository; private final RoadmapCategoryRepository roadmapCategoryRepository; + private final GoalRoomToDoRepository goalRoomToDoRepository; private final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, @@ -57,6 +61,7 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, final GoalRoomRepository goalRoomRepository, final GoalRoomMemberRepository goalRoomMemberRepository, final RoadmapCategoryRepository roadmapCategoryRepository, + final GoalRoomToDoRepository goalRoomToDoRepository, final GoalRoomToDoCheckRepository goalRoomToDoCheckRepository) { this.memberRepository = memberRepository; this.roadmapRepository = roadmapRepository; @@ -64,6 +69,7 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, this.goalRoomRepository = goalRoomRepository; this.goalRoomMemberRepository = goalRoomMemberRepository; this.roadmapCategoryRepository = roadmapCategoryRepository; + this.goalRoomToDoRepository = goalRoomToDoRepository; this.goalRoomToDoCheckRepository = goalRoomToDoCheckRepository; } @@ -85,28 +91,30 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, final Member goalRoomPendingMember = 사용자를_생성한다("name2", "01011112222", "identifier2", "password!2"); final GoalRoom goalRoom = 골룸을_생성한다("goalroom1", 6, roadmapContent, new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)), goalRoomPendingMember); + final GoalRoom savedGoalRoom = goalRoomRepository.save(goalRoom); final LocalDate today = LocalDate.now(); final LocalDate threeDaysAfter = today.plusDays(3); - final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo( - new GoalRoomTodoContent("투두1"), new Period(today, threeDaysAfter)); - final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo( - new GoalRoomTodoContent("투두2"), new Period(today, threeDaysAfter)); - goalRoom.addGoalRoomTodo(firstGoalRoomTodo); - goalRoom.addGoalRoomTodo(secondGoalRoomTodo); - goalRoomRepository.save(goalRoom); + final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두1"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두2"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo savedFirstGoalRoomToDo = goalRoomToDoRepository.save(firstGoalRoomTodo); + final GoalRoomToDo savedSecondGoalRoomToDo = goalRoomToDoRepository.save(secondGoalRoomTodo); final GoalRoomMember goalRoomMember = new GoalRoomMember( - GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); - goalRoomMemberRepository.save(goalRoomMember); + GoalRoomRole.LEADER, LocalDateTime.now(), savedGoalRoom, creator.getId()); + final GoalRoomMember savedGoalRoomMember = goalRoomMemberRepository.save(goalRoomMember); - final GoalRoomToDoCheck firstGoalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, firstGoalRoomTodo); - final GoalRoomToDoCheck secondGoalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, secondGoalRoomTodo); + final GoalRoomToDoCheck firstGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedFirstGoalRoomToDo); + final GoalRoomToDoCheck secondGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedSecondGoalRoomToDo); goalRoomToDoCheckRepository.saveAll(List.of(firstGoalRoomToDoCheck, secondGoalRoomToDoCheck)); // when - final GoalRoomToDoCheck findGoalRoomTodoCheck = goalRoomToDoCheckRepository.findByGoalRoomIdAndTodoAndMemberIdentifier( - goalRoom.getId(), firstGoalRoomTodo, new Identifier("identifier1")).get(); + final GoalRoomToDoCheck findGoalRoomTodoCheck = goalRoomToDoCheckRepository.findByGoalRoomToDoAndGoalRoomMemberId( + savedFirstGoalRoomToDo, savedGoalRoomMember.getId()).get(); // then assertThat(findGoalRoomTodoCheck) @@ -132,28 +140,30 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, final Member goalRoomPendingMember = 사용자를_생성한다("name2", "01011112222", "identifier2", "password!2"); final GoalRoom goalRoom = 골룸을_생성한다("goalroom1", 6, roadmapContent, new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)), goalRoomPendingMember); + final GoalRoom savedGoalRoom = goalRoomRepository.save(goalRoom); final LocalDate today = LocalDate.now(); final LocalDate threeDaysAfter = today.plusDays(3); - final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo( - new GoalRoomTodoContent("투두1"), new Period(today, threeDaysAfter)); - final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo( - new GoalRoomTodoContent("투두2"), new Period(today, threeDaysAfter)); - goalRoom.addGoalRoomTodo(firstGoalRoomTodo); - goalRoom.addGoalRoomTodo(secondGoalRoomTodo); - goalRoomRepository.save(goalRoom); + final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두1"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두2"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo savedFirstGoalRoomToDo = goalRoomToDoRepository.save(firstGoalRoomTodo); + final GoalRoomToDo savedSecondGoalRoomToDo = goalRoomToDoRepository.save(secondGoalRoomTodo); final GoalRoomMember goalRoomMember = new GoalRoomMember( - GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, creator); - goalRoomMemberRepository.save(goalRoomMember); + GoalRoomRole.LEADER, LocalDateTime.now(), savedGoalRoom, creator.getId()); + final GoalRoomMember savedGoalRoomMember = goalRoomMemberRepository.save(goalRoomMember); - final GoalRoomToDoCheck firstGoalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, firstGoalRoomTodo); - final GoalRoomToDoCheck secondGoalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember, secondGoalRoomTodo); + final GoalRoomToDoCheck firstGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedFirstGoalRoomToDo); + final GoalRoomToDoCheck secondGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedSecondGoalRoomToDo); goalRoomToDoCheckRepository.saveAll(List.of(firstGoalRoomToDoCheck, secondGoalRoomToDoCheck)); // when - final List findGoalRoomTodoCheck = goalRoomToDoCheckRepository.findByGoalRoomIdAndMemberIdentifier( - goalRoom.getId(), new Identifier("identifier1")); + final List findGoalRoomTodoCheck = goalRoomToDoCheckRepository.findByGoalRoomIdAndGoalRoomMemberId( + savedGoalRoom.getId(), savedGoalRoomMember.getId()); // then assertThat(findGoalRoomTodoCheck) @@ -161,6 +171,57 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, .isEqualTo(List.of(firstGoalRoomToDoCheck, secondGoalRoomToDoCheck)); } + @Test + void 골룸_멤버와_투두_아이디로_골룸_투두_체크를_삭제한다() { + // given + final Member creator = 사용자를_생성한다("name1", "01011111111", "identifier1", "password!1"); + final RoadmapCategory category = 카테고리를_저장한다("여가"); + final Roadmap roadmap = 로드맵을_저장한다(creator, category); + final RoadmapNode roadmapNode1 = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode2 = 로드맵_노드를_생성한다("로드맵 2주차", "로드맵 2주차 내용"); + final RoadmapContent roadmapContent = 로드맵_본문을_저장한다(List.of(roadmapNode1, roadmapNode2), roadmap.getId()); + + final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), + roadmapNode1); + final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY.plusDays(11), TODAY.plusDays(20), + roadmapNode2); + final Member goalRoomPendingMember = 사용자를_생성한다("name2", "01011112222", "identifier2", "password!2"); + final GoalRoom goalRoom = 골룸을_생성한다("goalroom1", 6, roadmapContent, + new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)), goalRoomPendingMember); + final GoalRoom savedGoalRoom = goalRoomRepository.save(goalRoom); + + final LocalDate today = LocalDate.now(); + final LocalDate threeDaysAfter = today.plusDays(3); + final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두1"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두2"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo savedFirstGoalRoomToDo = goalRoomToDoRepository.save(firstGoalRoomTodo); + final GoalRoomToDo savedSecondGoalRoomToDo = goalRoomToDoRepository.save(secondGoalRoomTodo); + + final GoalRoomMember goalRoomMember = new GoalRoomMember( + GoalRoomRole.LEADER, LocalDateTime.now(), savedGoalRoom, creator.getId()); + final GoalRoomMember savedGoalRoomMember = goalRoomMemberRepository.save(goalRoomMember); + + final GoalRoomToDoCheck firstGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedFirstGoalRoomToDo); + final GoalRoomToDoCheck secondGoalRoomToDoCheck = new GoalRoomToDoCheck(savedGoalRoomMember.getId(), + savedSecondGoalRoomToDo); + goalRoomToDoCheckRepository.saveAll(List.of(firstGoalRoomToDoCheck, secondGoalRoomToDoCheck)); + + // when + goalRoomToDoCheckRepository.deleteByGoalRoomMemberIdAndGoalRoomToDoId(savedGoalRoomMember.getId(), + savedFirstGoalRoomToDo.getId()); + + // then + final List findGoalRoomToDoChecks = goalRoomToDoCheckRepository.findByGoalRoomIdAndGoalRoomMemberId( + savedGoalRoom.getId(), savedGoalRoomMember.getId()); + assertThat(findGoalRoomToDoChecks) + .usingRecursiveComparison() + .ignoringFields("id") + .isEqualTo(List.of(secondGoalRoomToDoCheck)); + } + private RoadmapCategory 카테고리를_저장한다(final String name) { final RoadmapCategory roadmapCategory = new RoadmapCategory(name); return roadmapCategoryRepository.save(roadmapCategory); @@ -191,14 +252,12 @@ public GoalRoomToDoCheckRepositoryTest(final MemberRepository memberRepository, private GoalRoomRoadmapNode 골룸_로드맵_노드를_생성한다(final LocalDate startDate, final LocalDate endDate, final RoadmapNode roadmapNode) { - return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode); + return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode.getId()); } private GoalRoom 골룸을_생성한다(final String name, final Integer limitedMemberCount, final RoadmapContent roadmapContent, final GoalRoomRoadmapNodes goalRoomRoadmapNodes, final Member member) { - final GoalRoom goalRoom = new GoalRoom(new GoalRoomName(name), new LimitedMemberCount(limitedMemberCount), - roadmapContent, member); - goalRoom.addAllGoalRoomRoadmapNodes(goalRoomRoadmapNodes); - return goalRoom; + return new GoalRoom(new GoalRoomName(name), new LimitedMemberCount(limitedMemberCount), roadmapContent.getId(), + goalRoomRoadmapNodes); } } diff --git a/backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoRepositoryTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoRepositoryTest.java new file mode 100644 index 000000000..01296ad9b --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/persistence/GoalRoomToDoRepositoryTest.java @@ -0,0 +1,146 @@ +package co.kirikiri.todo.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import co.kirikiri.common.helper.RepositoryTest; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapCategory; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapDifficulty; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodes; +import co.kirikiri.roadmap.persistence.RoadmapCategoryRepository; +import co.kirikiri.roadmap.persistence.RoadmapRepository; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; + +@RepositoryTest +class GoalRoomToDoRepositoryTest { + + private static final LocalDate TODAY = LocalDate.now(); + + private final MemberRepository memberRepository; + private final RoadmapRepository roadmapRepository; + private final GoalRoomRepository goalRoomRepository; + private final GoalRoomMemberRepository goalRoomMemberRepository; + private final RoadmapCategoryRepository roadmapCategoryRepository; + private final GoalRoomToDoRepository goalRoomToDoRepository; + + public GoalRoomToDoRepositoryTest(final MemberRepository memberRepository, + final RoadmapRepository roadmapRepository, + final GoalRoomRepository goalRoomRepository, + final GoalRoomMemberRepository goalRoomMemberRepository, + final RoadmapCategoryRepository roadmapCategoryRepository, + final GoalRoomToDoRepository goalRoomToDoRepository) { + this.memberRepository = memberRepository; + this.roadmapRepository = roadmapRepository; + this.goalRoomRepository = goalRoomRepository; + this.goalRoomMemberRepository = goalRoomMemberRepository; + this.roadmapCategoryRepository = roadmapCategoryRepository; + this.goalRoomToDoRepository = goalRoomToDoRepository; + } + + @Test + void 골룸_아이디와_사용자_아이디로_골룸_투두_체크_현황을_반환한다() { + // given + final Member creator = 사용자를_생성한다("name1", "01011111111", "identifier1", "password!1"); + final RoadmapCategory category = 카테고리를_저장한다("여가"); + final RoadmapNode roadmapNode1 = 로드맵_노드를_생성한다("로드맵 1주차", "로드맵 1주차 내용"); + final RoadmapNode roadmapNode2 = 로드맵_노드를_생성한다("로드맵 2주차", "로드맵 2주차 내용"); + final Roadmap roadmap = 로드맵을_생성한다(creator, category); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), List.of(roadmapNode1, roadmapNode2)); + + final GoalRoomRoadmapNode goalRoomRoadmapNode1 = 골룸_로드맵_노드를_생성한다(TODAY, TODAY.plusDays(10), + roadmapNode1); + final GoalRoomRoadmapNode goalRoomRoadmapNode2 = 골룸_로드맵_노드를_생성한다(TODAY.plusDays(11), TODAY.plusDays(20), + roadmapNode2); + final Member goalRoomPendingMember = 사용자를_생성한다("name2", "01011112222", "identifier2", "password!2"); + final GoalRoom goalRoom = 골룸을_생성한다("goalroom1", 6, roadmapContent, + new GoalRoomRoadmapNodes(List.of(goalRoomRoadmapNode1, goalRoomRoadmapNode2)), goalRoomPendingMember); + final GoalRoom savedGoalRoom = goalRoomRepository.save(goalRoom); + + final LocalDate today = LocalDate.now(); + final LocalDate threeDaysAfter = today.plusDays(3); + final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두1"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(null, savedGoalRoom.getId(), + new GoalRoomTodoContent("투두2"), new ToDoPeriod(today, threeDaysAfter)); + final GoalRoomToDo savedFirstGoalRoomToDo = goalRoomToDoRepository.save(firstGoalRoomTodo); + final GoalRoomToDo savedSecondGoalRoomToDo = goalRoomToDoRepository.save(secondGoalRoomTodo); + + final GoalRoomMember goalRoomMember = new GoalRoomMember( + GoalRoomRole.LEADER, LocalDateTime.now(), savedGoalRoom, creator.getId()); + final GoalRoomMember savedGoalRoomMember = goalRoomMemberRepository.save(goalRoomMember); + + // when + final List findGoalRoomToDos = goalRoomToDoRepository.findGoalRoomToDosByGoalRoomId( + savedGoalRoom.getId()); + + // then + assertThat(findGoalRoomToDos) + .usingRecursiveComparison() + .isEqualTo(List.of(savedFirstGoalRoomToDo, savedSecondGoalRoomToDo)); + } + + private RoadmapCategory 카테고리를_저장한다(final String name) { + final RoadmapCategory roadmapCategory = new RoadmapCategory(name); + return roadmapCategoryRepository.save(roadmapCategory); + } + + private Member 사용자를_생성한다(final String nickname, final String phoneNumber, final String identifier, + final String password) { + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, "kirikiri1@email.com"); + final Member creator = new Member(new Identifier(identifier), + new EncryptedPassword(new Password(password)), new Nickname(nickname), null, memberProfile); + return memberRepository.save(creator); + } + + private Roadmap 로드맵을_생성한다(final Member creator, final RoadmapCategory category) { + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 소개글", 30, RoadmapDifficulty.DIFFICULT, + creator.getId(), category, null); + return roadmapRepository.save(roadmap); + } + + private RoadmapNode 로드맵_노드를_생성한다(final String title, final String content) { + return new RoadmapNode(title, content); + } + + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", roadmapId, new RoadmapNodes(roadmapNodes)); + return roadmapContent; + } + + private GoalRoomRoadmapNode 골룸_로드맵_노드를_생성한다(final LocalDate startDate, final LocalDate endDate, + final RoadmapNode roadmapNode) { + return new GoalRoomRoadmapNode(new Period(startDate, endDate), 1, roadmapNode.getId()); + } + + private GoalRoom 골룸을_생성한다(final String name, final Integer limitedMemberCount, final RoadmapContent roadmapContent, + final GoalRoomRoadmapNodes goalRoomRoadmapNodes, final Member member) { + return new GoalRoom(new GoalRoomName(name), new LimitedMemberCount(limitedMemberCount), roadmapContent.getId(), + goalRoomRoadmapNodes); + } +} diff --git a/backend/kirikiri/src/test/java/co/kirikiri/todo/service/GoalRoomToDoServiceTest.java b/backend/kirikiri/src/test/java/co/kirikiri/todo/service/GoalRoomToDoServiceTest.java new file mode 100644 index 000000000..a47b05c1e --- /dev/null +++ b/backend/kirikiri/src/test/java/co/kirikiri/todo/service/GoalRoomToDoServiceTest.java @@ -0,0 +1,465 @@ +package co.kirikiri.todo.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; + +import co.kirikiri.common.exception.BadRequestException; +import co.kirikiri.common.exception.ForbiddenException; +import co.kirikiri.common.exception.NotFoundException; +import co.kirikiri.common.type.ImageContentType; +import co.kirikiri.goalroom.domain.GoalRoom; +import co.kirikiri.goalroom.domain.GoalRoomMember; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNode; +import co.kirikiri.goalroom.domain.GoalRoomRoadmapNodes; +import co.kirikiri.goalroom.domain.GoalRoomRole; +import co.kirikiri.goalroom.domain.vo.GoalRoomName; +import co.kirikiri.goalroom.domain.vo.LimitedMemberCount; +import co.kirikiri.goalroom.domain.vo.Period; +import co.kirikiri.goalroom.persistence.GoalRoomMemberRepository; +import co.kirikiri.goalroom.persistence.GoalRoomRepository; +import co.kirikiri.member.domain.EncryptedPassword; +import co.kirikiri.member.domain.Gender; +import co.kirikiri.member.domain.Member; +import co.kirikiri.member.domain.MemberProfile; +import co.kirikiri.member.domain.vo.Identifier; +import co.kirikiri.member.domain.vo.Nickname; +import co.kirikiri.member.domain.vo.Password; +import co.kirikiri.member.persistence.MemberRepository; +import co.kirikiri.roadmap.domain.Roadmap; +import co.kirikiri.roadmap.domain.RoadmapCategory; +import co.kirikiri.roadmap.domain.RoadmapContent; +import co.kirikiri.roadmap.domain.RoadmapDifficulty; +import co.kirikiri.roadmap.domain.RoadmapNode; +import co.kirikiri.roadmap.domain.RoadmapNodeImage; +import co.kirikiri.roadmap.domain.RoadmapNodeImages; +import co.kirikiri.roadmap.domain.RoadmapNodes; +import co.kirikiri.todo.domain.GoalRoomToDo; +import co.kirikiri.todo.domain.GoalRoomToDoCheck; +import co.kirikiri.todo.domain.vo.GoalRoomTodoContent; +import co.kirikiri.todo.domain.vo.ToDoPeriod; +import co.kirikiri.todo.persistence.GoalRoomToDoCheckRepository; +import co.kirikiri.todo.persistence.GoalRoomToDoRepository; +import co.kirikiri.todo.service.dto.request.GoalRoomTodoRequest; +import co.kirikiri.todo.service.dto.response.GoalRoomToDoCheckResponse; +import co.kirikiri.todo.service.dto.response.GoalRoomTodoResponse; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeAll; +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; + +@ExtendWith(MockitoExtension.class) +class GoalRoomToDoServiceTest { + + private static final LocalDate TODAY = LocalDate.now(); + private static final LocalDate TEN_DAY_LATER = TODAY.plusDays(10); + private static final LocalDate TWENTY_DAY_LATER = TODAY.plusDays(20); + private static final LocalDate THIRTY_DAY_LATER = TODAY.plusDays(30); + + private static Member member; + + @Mock + private GoalRoomRepository goalRoomRepository; + + @Mock + private GoalRoomMemberRepository goalRoomMemberRepository; + + @Mock + private MemberRepository memberRepository; + + @Mock + private GoalRoomToDoRepository goalRoomToDoRepository; + + @Mock + private GoalRoomToDoCheckRepository goalRoomToDoCheckRepository; + + @InjectMocks + private GoalRoomToDoService goalRoomToDoService; + + @BeforeAll + static void setUp() { + final Identifier identifier = new Identifier("identifier1"); + final Password password = new Password("password1!"); + final EncryptedPassword encryptedPassword = new EncryptedPassword(password); + final Nickname nickname = new Nickname("nickname"); + final String email = "kirikiri@email.com"; + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); + member = new Member(identifier, encryptedPassword, nickname, null, memberProfile); + } + + @Test + void 정상적으로_골룸에_투두리스트를_추가한다() { + //given + final Member creator = 사용자를_생성한다(1L, "identifier1", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + final GoalRoomToDo goalRoomTodo = new GoalRoomToDo(null, 1L, new GoalRoomTodoContent("goalRoomTodoContent"), + new ToDoPeriod(TODAY, TEN_DAY_LATER)); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findAllByGoalRoom(any())) + .willReturn(List.of(new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()))); + given(goalRoomToDoRepository.save(any())) + .willReturn(goalRoomTodo); + + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, + TEN_DAY_LATER); + + //when + //then + assertDoesNotThrow(() -> goalRoomToDoService.addGoalRoomTodo(1L, "identifier1", goalRoomTodoRequest)); + } + + @Test + void 골룸에_투두리스트_추가시_회원을_찾지_못할_경우_예외를_던진다() { + //given + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.empty()); + + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, + TEN_DAY_LATER); + + //when + //then + assertThatThrownBy(() -> goalRoomToDoService.addGoalRoomTodo(1L, "identifier1", goalRoomTodoRequest)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸에_투두리스트_추가시_골룸을_찾지_못할_경우_예외를_던진다() { + //given + final Member creator = 사용자를_생성한다(1L, "identifier1", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.empty()); + + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, + TEN_DAY_LATER); + + //when + //then + assertThatThrownBy(() -> goalRoomToDoService.addGoalRoomTodo(1L, "identifier1", goalRoomTodoRequest)) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸에_투두리스트_추가시_종료된_골룸일_경우_예외를_던진다() { + //given + final Member creator = 사용자를_생성한다(1L, "identifier1", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + goalRoom.complete(); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(creator)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, + TEN_DAY_LATER); + + //when + //then + assertThatThrownBy(() -> goalRoomToDoService.addGoalRoomTodo(1L, "identifier1", goalRoomTodoRequest)) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 골룸에_투두리스트_추가시_리더가_아닐_경우_예외를_던진다() { + //given + final Member creator = 사용자를_생성한다(1L, "identifier1", "password1!", "시진이", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final int limitedMemberCount = 20; + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, limitedMemberCount); + + given(memberRepository.findByIdentifier(any())) + .willReturn(Optional.of(member)); + given(goalRoomRepository.findById(anyLong())) + .willReturn(Optional.of(goalRoom)); + given(goalRoomMemberRepository.findAllByGoalRoom(any())) + .willReturn(List.of(new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()))); + + final GoalRoomTodoRequest goalRoomTodoRequest = new GoalRoomTodoRequest("goalRoomContent", TODAY, + TEN_DAY_LATER); + + //when + //then + assertThatThrownBy(() -> goalRoomToDoService.addGoalRoomTodo(1L, "identifier2", goalRoomTodoRequest)) + .isInstanceOf(BadRequestException.class); + } + + @Test + void 투두리스트를_체크한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + final GoalRoomToDo goalRoomToDo = new GoalRoomToDo(1L, goalRoom.getId(), new GoalRoomTodoContent("투두 1"), + new ToDoPeriod(TODAY, TODAY.plusDays(3))); + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomToDoRepository.findById(any())) + .thenReturn(Optional.of(goalRoomToDo)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember)); + when(goalRoomToDoCheckRepository.findByGoalRoomToDoAndGoalRoomMemberId(any(), any())) + .thenReturn(Optional.empty()); + + // when + final GoalRoomToDoCheckResponse checkResponse = goalRoomToDoService.checkGoalRoomTodo(goalRoom.getId(), + goalRoomToDo.getId(), "cokirikiri"); + + // then + assertThat(checkResponse) + .isEqualTo(new GoalRoomToDoCheckResponse(true)); + } + + @Test + void 투두리스트_체크시_체크_이력이_있으면_제거한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + final GoalRoomToDo goalRoomToDo = new GoalRoomToDo(1L, goalRoom.getId(), new GoalRoomTodoContent("투두 1"), + new ToDoPeriod(TODAY, TODAY.plusDays(3))); + + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + final GoalRoomToDoCheck goalRoomToDoCheck = new GoalRoomToDoCheck(goalRoomMember.getId(), goalRoomToDo); + + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomToDoRepository.findById(any())) + .thenReturn(Optional.of(goalRoomToDo)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomMember)); + when(goalRoomToDoCheckRepository.findByGoalRoomToDoAndGoalRoomMemberId(any(), any())) + .thenReturn(Optional.of(goalRoomToDoCheck)); + + // when + final GoalRoomToDoCheckResponse checkResponse = goalRoomToDoService.checkGoalRoomTodo(goalRoom.getId(), + goalRoomToDo.getId(), "cokirikiri"); + + // then + assertThat(checkResponse) + .isEqualTo(new GoalRoomToDoCheckResponse(false)); + } + + @Test + void 투두리스트_체크시_골룸이_존재하지_않으면_예외가_발생한다() { + // given + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomToDoService.checkGoalRoomTodo(1L, 1L, "cokirikiri")) + .isInstanceOf(NotFoundException.class) + .hasMessage("존재하지 않는 골룸입니다. goalRoomId = 1"); + } + + @Test + void 투두리스트_체크시_해당_투두가_존재하지_않으면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + + // expected + assertThatThrownBy(() -> goalRoomToDoService.checkGoalRoomTodo(1L, 2L, "cokirikiri")) + .isInstanceOf(NotFoundException.class) + .hasMessage("존재하지 않는 투두입니다. todoId = 2"); + } + + @Test + void 투두리스트_체크시_골룸에_사용자가_없으면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + final GoalRoomToDo goalRoomToDo = new GoalRoomToDo(1L, goalRoom.getId(), new GoalRoomTodoContent("투두 1"), + new ToDoPeriod(TODAY, TODAY.plusDays(3))); + + when(goalRoomRepository.findById(anyLong())) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomToDoRepository.findById(any())) + .thenReturn(Optional.of(goalRoomToDo)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomAndMemberId(any(), any())) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomToDoService.checkGoalRoomTodo(1L, 1L, "cokirikiri")) + .isInstanceOf(NotFoundException.class) + .hasMessage("골룸에 사용자가 존재하지 않습니다. goalRoomId = 1 memberIdentifier = cokirikiri"); + } + + @Test + void 골룸의_전체_투두리스트를_조회한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + + final GoalRoomToDo firstGoalRoomTodo = new GoalRoomToDo(1L, goalRoom.getId(), new GoalRoomTodoContent("투두 1"), + new ToDoPeriod(TODAY, TEN_DAY_LATER)); + final GoalRoomToDo secondGoalRoomTodo = new GoalRoomToDo(2L, goalRoom.getId(), new GoalRoomTodoContent("투두 2"), + new ToDoPeriod(TWENTY_DAY_LATER, THIRTY_DAY_LATER)); + + final GoalRoomMember goalRoomMember = new GoalRoomMember(GoalRoomRole.LEADER, LocalDateTime.now(), goalRoom, + creator.getId()); + when(goalRoomRepository.findById(1L)) + .thenReturn(Optional.of(goalRoom)); + when(goalRoomToDoRepository.findGoalRoomToDosByGoalRoomId(anyLong())) + .thenReturn(List.of(firstGoalRoomTodo, secondGoalRoomTodo)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomIdAndMemberId(anyLong(), any())) + .thenReturn(Optional.of(goalRoomMember)); + when(goalRoomToDoCheckRepository.findByGoalRoomIdAndGoalRoomMemberId(anyLong(), any())) + .thenReturn(List.of( + new GoalRoomToDoCheck(goalRoomMember.getId(), firstGoalRoomTodo) + )); + + // when + final List responses = goalRoomToDoService.findAllGoalRoomTodo(1L, "identifier"); + final List expected = List.of( + new GoalRoomTodoResponse(1L, "투두 1", TODAY, TEN_DAY_LATER, new GoalRoomToDoCheckResponse(true)), + new GoalRoomTodoResponse(2L, "투두 2", TWENTY_DAY_LATER, THIRTY_DAY_LATER, + new GoalRoomToDoCheckResponse(false))); + + // then + assertThat(responses) + .isEqualTo(expected); + } + + @Test + void 골룸의_투두리스트_조회시_존재하지_않는_골룸이면_예외가_발생한다() { + // given + when(goalRoomRepository.findById(1L)) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomToDoService.findAllGoalRoomTodo(1L, "identifier")) + .isInstanceOf(NotFoundException.class); + } + + @Test + void 골룸의_투두리스트_조회시_골룸에_참여하지_않은_사용자면_예외가_발생한다() { + // given + final Member creator = 사용자를_생성한다(1L, "cokirikiri", "password1!", "코끼리", "kirikiri1@email"); + final Roadmap roadmap = 로드맵을_생성한다(creator); + final List roadmapNodes = 로드맵_노드들을_생성한다(); + final RoadmapContent roadmapContent = 로드맵_본문을_생성한다(roadmap.getId(), roadmapNodes); + final GoalRoom goalRoom = 골룸을_생성한다(1L, creator, roadmapContent, 10); + + when(goalRoomRepository.findById(1L)) + .thenReturn(Optional.of(goalRoom)); + when(memberRepository.findByIdentifier(any())) + .thenReturn(Optional.of(creator)); + when(goalRoomMemberRepository.findByGoalRoomIdAndMemberId(anyLong(), any())) + .thenReturn(Optional.empty()); + + // expected + assertThatThrownBy(() -> goalRoomToDoService.findAllGoalRoomTodo(1L, "identifier")) + .isInstanceOf(ForbiddenException.class); + } + + private Member 사용자를_생성한다(final Long memberId, final String identifier, final String password, final String nickname, + final String email) { + final MemberProfile memberProfile = new MemberProfile(Gender.MALE, email); + + return new Member(memberId, new Identifier(identifier), null, new EncryptedPassword(new Password(password)), + new Nickname(nickname), null, memberProfile); + } + + private Roadmap 로드맵을_생성한다(final Member creator) { + final RoadmapCategory category = new RoadmapCategory("게임"); + final Roadmap roadmap = new Roadmap("로드맵 제목", "로드맵 소개글", 10, RoadmapDifficulty.NORMAL, creator.getId(), + category, null); + return roadmap; + } + + private List 로드맵_노드들을_생성한다() { + final RoadmapNode roadmapNode1 = new RoadmapNode("로드맵 1주차", "로드맵 1주차 내용"); + roadmapNode1.addImages(new RoadmapNodeImages(노드_이미지들을_생성한다())); + final RoadmapNode roadmapNode2 = new RoadmapNode("로드맵 2주차", "로드맵 2주차 내용"); + return List.of(roadmapNode1, roadmapNode2); + } + + private RoadmapContent 로드맵_본문을_생성한다(final Long roadmapId, final List roadmapNodes) { + final RoadmapContent roadmapContent = new RoadmapContent("로드맵 본문", roadmapId, new RoadmapNodes(roadmapNodes)); + return roadmapContent; + } + + private List 노드_이미지들을_생성한다() { + return List.of( + new RoadmapNodeImage("node-image1.png", "node-image1-save-path", ImageContentType.PNG), + new RoadmapNodeImage("node-image2.png", "node-image2-save-path", ImageContentType.PNG) + ); + } + + private GoalRoom 골룸을_생성한다(final Long goalRoomId, final Member creator, final RoadmapContent roadmapContent, + final Integer limitedMemberCount) { + return new GoalRoom(goalRoomId, new GoalRoomName("골룸 이름"), new LimitedMemberCount(limitedMemberCount), + roadmapContent.getId(), 골룸_로드맵_노드들을_생성한다(roadmapContent.getNodes())); + } + + private GoalRoomRoadmapNodes 골룸_로드맵_노드들을_생성한다(final RoadmapNodes roadmapNodes) { + return new GoalRoomRoadmapNodes(List.of( + new GoalRoomRoadmapNode(new Period(TODAY, TEN_DAY_LATER), 5, roadmapNodes.getValues().get(0).getId()), + new GoalRoomRoadmapNode(new Period(TEN_DAY_LATER.plusDays(1), TWENTY_DAY_LATER), 5, + roadmapNodes.getValues().get(1).getId())) + ); + } +}