diff --git a/src/main/java/im/toduck/domain/badge/common/mapper/UserBadgeMapper.java b/src/main/java/im/toduck/domain/badge/common/mapper/UserBadgeMapper.java index 133615cb..01eebbec 100644 --- a/src/main/java/im/toduck/domain/badge/common/mapper/UserBadgeMapper.java +++ b/src/main/java/im/toduck/domain/badge/common/mapper/UserBadgeMapper.java @@ -1,7 +1,11 @@ package im.toduck.domain.badge.common.mapper; +import java.util.List; + import im.toduck.domain.badge.persistence.entity.Badge; import im.toduck.domain.badge.persistence.entity.UserBadge; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; +import im.toduck.domain.badge.presentation.dto.response.BadgeResponse; import im.toduck.domain.user.persistence.entity.User; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -15,4 +19,19 @@ public static UserBadge toUserBadge(final User user, final Badge badge) { .badge(badge) .build(); } + + public static BadgeListResponse toBadgeListResponse(final long totalCount, final List userBadges) { + Long representativeBadgeId = userBadges.stream() + .filter(UserBadge::isRepresentative) + .findFirst() + .map(userBadge -> userBadge.getBadge().getId()) + .orElse(null); + + List ownedBadges = userBadges.stream() + .map(UserBadge::getBadge) + .map(BadgeResponse::from) + .toList(); + + return BadgeListResponse.of(totalCount, representativeBadgeId, ownedBadges); + } } diff --git a/src/main/java/im/toduck/domain/badge/domain/service/BadgeService.java b/src/main/java/im/toduck/domain/badge/domain/service/BadgeService.java index baed672d..ebdc9126 100644 --- a/src/main/java/im/toduck/domain/badge/domain/service/BadgeService.java +++ b/src/main/java/im/toduck/domain/badge/domain/service/BadgeService.java @@ -11,6 +11,7 @@ import im.toduck.domain.badge.persistence.entity.UserBadge; import im.toduck.domain.badge.persistence.repository.BadgeRepository; import im.toduck.domain.badge.persistence.repository.UserBadgeRepository; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; import im.toduck.domain.user.persistence.entity.User; import im.toduck.global.exception.CommonException; import im.toduck.global.exception.ExceptionCode; @@ -60,4 +61,39 @@ public UserBadge grantBadge(final User user, final BadgeCode badgeCode) { public List getUnseenBadges(final User user) { return userBadgeRepository.findAllByUserAndIsSeenFalse(user); } + + /** + * 사용자의 뱃지 목록 정보를 조회합니다. + * + * @param user 조회할 사용자 + * @return 뱃지 목록 정보 (전체 개수, 대표 뱃지 ID, 보유 뱃지 목록) + */ + @Transactional(readOnly = true) + public BadgeListResponse getMyBadgeList(final User user) { + long totalCount = badgeRepository.count(); + List userBadges = userBadgeRepository.findAllByUser(user); + + return UserBadgeMapper.toBadgeListResponse(totalCount, userBadges); + } + + /** + * 사용자의 대표 뱃지를 설정합니다. + * 기존 대표 뱃지가 있다면 해제하고, 새로운 뱃지를 대표로 설정합니다. + * + * @param user 뱃지를 설정할 사용자 + * @param badgeId 대표로 설정할 뱃지 ID (UserBadge PK가 아닌 Badge PK) + */ + @Transactional + public void setRepresentativeBadge(final User user, final Long badgeId) { + Badge badge = badgeRepository.findById(badgeId) + .orElseThrow(() -> CommonException.from(ExceptionCode.NOT_FOUND_BADGE)); + + UserBadge newRepresentativeBadge = userBadgeRepository.findByUserAndBadge(user, badge) + .orElseThrow(() -> CommonException.from(ExceptionCode.NOT_OWNED_BADGE)); + + userBadgeRepository.findByUserAndIsRepresentativeTrue(user) + .ifPresent(oldBadge -> oldBadge.updateRepresentativeStatus(false)); + + newRepresentativeBadge.updateRepresentativeStatus(true); + } } diff --git a/src/main/java/im/toduck/domain/badge/domain/usecase/BadgeUseCase.java b/src/main/java/im/toduck/domain/badge/domain/usecase/BadgeUseCase.java index c96953f6..36d220ec 100644 --- a/src/main/java/im/toduck/domain/badge/domain/usecase/BadgeUseCase.java +++ b/src/main/java/im/toduck/domain/badge/domain/usecase/BadgeUseCase.java @@ -6,10 +6,11 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.transaction.annotation.Transactional; -import im.toduck.domain.badge.common.dto.response.BadgeResponse; import im.toduck.domain.badge.domain.service.BadgeService; import im.toduck.domain.badge.persistence.entity.BadgeCode; import im.toduck.domain.badge.persistence.entity.UserBadge; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; +import im.toduck.domain.badge.presentation.dto.response.BadgeResponse; import im.toduck.domain.notification.domain.data.BadgeAcquiredNotificationData; import im.toduck.domain.notification.domain.event.BadgeAcquiredNotificationEvent; import im.toduck.domain.user.domain.service.UserService; @@ -66,4 +67,32 @@ public List getNewBadges(final Long userId) { return responses; } + + /** + * 사용자의 뱃지 목록 정보를 조회합니다. + * + * @param userId 조회할 사용자 ID + * @return 뱃지 목록 정보 + */ + @Transactional(readOnly = true) + public BadgeListResponse getMyBadgeList(final Long userId) { + User user = userService.getUserById(userId) + .orElseThrow(() -> CommonException.from(ExceptionCode.NOT_FOUND_USER)); + + return badgeService.getMyBadgeList(user); + } + + /** + * 사용자의 대표 뱃지를 설정합니다. + * + * @param userId 뱃지를 설정할 사용자 ID + * @param badgeId 설정할 뱃지 ID + */ + @Transactional + public void setRepresentativeBadge(final Long userId, final Long badgeId) { + User user = userService.getUserById(userId) + .orElseThrow(() -> CommonException.from(ExceptionCode.NOT_FOUND_USER)); + + badgeService.setRepresentativeBadge(user, badgeId); + } } diff --git a/src/main/java/im/toduck/domain/badge/persistence/entity/UserBadge.java b/src/main/java/im/toduck/domain/badge/persistence/entity/UserBadge.java index 13cc3448..a208854a 100644 --- a/src/main/java/im/toduck/domain/badge/persistence/entity/UserBadge.java +++ b/src/main/java/im/toduck/domain/badge/persistence/entity/UserBadge.java @@ -51,4 +51,8 @@ private UserBadge(User user, Badge badge) { public void markAsSeen() { this.isSeen = true; } + + public void updateRepresentativeStatus(boolean isRepresentative) { + this.isRepresentative = isRepresentative; + } } diff --git a/src/main/java/im/toduck/domain/badge/persistence/repository/UserBadgeRepository.java b/src/main/java/im/toduck/domain/badge/persistence/repository/UserBadgeRepository.java index 38deb37a..dda11a2b 100644 --- a/src/main/java/im/toduck/domain/badge/persistence/repository/UserBadgeRepository.java +++ b/src/main/java/im/toduck/domain/badge/persistence/repository/UserBadgeRepository.java @@ -1,6 +1,7 @@ package im.toduck.domain.badge.persistence.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -14,4 +15,8 @@ public interface UserBadgeRepository extends JpaRepository { List findAllByUser(User user); List findAllByUserAndIsSeenFalse(User user); + + Optional findByUserAndIsRepresentativeTrue(User user); + + Optional findByUserAndBadge(User user, Badge badge); } diff --git a/src/main/java/im/toduck/domain/badge/presentation/api/BadgeApi.java b/src/main/java/im/toduck/domain/badge/presentation/api/BadgeApi.java index 983b9de4..e51014c3 100644 --- a/src/main/java/im/toduck/domain/badge/presentation/api/BadgeApi.java +++ b/src/main/java/im/toduck/domain/badge/presentation/api/BadgeApi.java @@ -1,17 +1,25 @@ package im.toduck.domain.badge.presentation.api; import java.util.List; +import java.util.Map; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; -import im.toduck.domain.badge.common.dto.response.BadgeResponse; +import im.toduck.domain.badge.presentation.dto.request.RepresentativeBadgeRequest; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; +import im.toduck.domain.badge.presentation.dto.response.BadgeResponse; +import im.toduck.global.annotation.swagger.ApiErrorResponseExplanation; import im.toduck.global.annotation.swagger.ApiResponseExplanations; import im.toduck.global.annotation.swagger.ApiSuccessResponseExplanation; +import im.toduck.global.exception.ExceptionCode; import im.toduck.global.presentation.ApiResponse; import im.toduck.global.security.authentication.CustomUserDetails; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; @Tag(name = "Badges", description = "뱃지 관련 API") public interface BadgeApi { @@ -25,4 +33,30 @@ public interface BadgeApi { ResponseEntity>> getNewBadges( @AuthenticationPrincipal CustomUserDetails userDetails ); + + @Operation(summary = "내 뱃지 목록 조회", description = "보유한 뱃지 목록과 대표 뱃지 설정 정보를 조회합니다.") + @ApiResponseExplanations( + success = @ApiSuccessResponseExplanation( + description = "내 뱃지 목록 조회 성공" + ) + ) + @GetMapping + ResponseEntity> getMyBadgeList( + @AuthenticationPrincipal CustomUserDetails userDetails + ); + + @Operation(summary = "대표 뱃지 설정", description = "사용자의 대표 뱃지를 설정하거나 변경합니다.") + @ApiResponseExplanations( + success = @ApiSuccessResponseExplanation( + description = "대표 뱃지 설정 성공" + ), + errors = { + @ApiErrorResponseExplanation(exceptionCode = ExceptionCode.NOT_FOUND_BADGE), + @ApiErrorResponseExplanation(exceptionCode = ExceptionCode.NOT_OWNED_BADGE) + } + ) + ResponseEntity>> updateRepresentativeBadge( + @AuthenticationPrincipal CustomUserDetails userDetails, + @Valid @RequestBody RepresentativeBadgeRequest request + ); } diff --git a/src/main/java/im/toduck/domain/badge/presentation/controller/BadgeController.java b/src/main/java/im/toduck/domain/badge/presentation/controller/BadgeController.java index d9988c32..c3cd3fec 100644 --- a/src/main/java/im/toduck/domain/badge/presentation/controller/BadgeController.java +++ b/src/main/java/im/toduck/domain/badge/presentation/controller/BadgeController.java @@ -1,19 +1,25 @@ package im.toduck.domain.badge.presentation.controller; import java.util.List; +import java.util.Map; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import im.toduck.domain.badge.common.dto.response.BadgeResponse; import im.toduck.domain.badge.domain.usecase.BadgeUseCase; import im.toduck.domain.badge.presentation.api.BadgeApi; +import im.toduck.domain.badge.presentation.dto.request.RepresentativeBadgeRequest; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; +import im.toduck.domain.badge.presentation.dto.response.BadgeResponse; import im.toduck.global.presentation.ApiResponse; import im.toduck.global.security.authentication.CustomUserDetails; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @RestController @@ -32,4 +38,25 @@ public ResponseEntity>> getNewBadges( List responses = badgeUseCase.getNewBadges(userDetails.getUserId()); return ResponseEntity.ok(ApiResponse.createSuccess(responses)); } + + @Override + @GetMapping + @PreAuthorize("isAuthenticated()") + public ResponseEntity> getMyBadgeList( + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + BadgeListResponse response = badgeUseCase.getMyBadgeList(userDetails.getUserId()); + return ResponseEntity.ok(ApiResponse.createSuccess(response)); + } + + @Override + @PatchMapping("/representative") + @PreAuthorize("isAuthenticated()") + public ResponseEntity>> updateRepresentativeBadge( + @AuthenticationPrincipal CustomUserDetails userDetails, + @Valid @RequestBody RepresentativeBadgeRequest request + ) { + badgeUseCase.setRepresentativeBadge(userDetails.getUserId(), request.badgeId()); + return ResponseEntity.ok(ApiResponse.createSuccessWithNoContent()); + } } diff --git a/src/main/java/im/toduck/domain/badge/presentation/dto/request/RepresentativeBadgeRequest.java b/src/main/java/im/toduck/domain/badge/presentation/dto/request/RepresentativeBadgeRequest.java new file mode 100644 index 00000000..6fbd43f3 --- /dev/null +++ b/src/main/java/im/toduck/domain/badge/presentation/dto/request/RepresentativeBadgeRequest.java @@ -0,0 +1,12 @@ +package im.toduck.domain.badge.presentation.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "대표 뱃지 설정 요청") +public record RepresentativeBadgeRequest( + @Schema(description = "설정할 뱃지 ID", example = "1") + @NotNull(message = "뱃지 ID는 필수입니다.") + Long badgeId +) { +} diff --git a/src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeListResponse.java b/src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeListResponse.java new file mode 100644 index 00000000..e22ff6b8 --- /dev/null +++ b/src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeListResponse.java @@ -0,0 +1,31 @@ +package im.toduck.domain.badge.presentation.dto.response; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +@Builder +@Schema(description = "내 뱃지 목록 조회 응답") +public record BadgeListResponse( + @Schema(description = "전체 뱃지 개수") + long totalCount, + + @Schema(description = "대표 뱃지 ID (없으면 null)") + Long representativeBadgeId, + + @Schema(description = "보유한 뱃지 리스트") + List ownedBadges +) { + public static BadgeListResponse of( + final long totalCount, + final Long representativeBadgeId, + final List ownedBadges + ) { + return BadgeListResponse.builder() + .totalCount(totalCount) + .representativeBadgeId(representativeBadgeId) + .ownedBadges(ownedBadges) + .build(); + } +} diff --git a/src/main/java/im/toduck/domain/badge/common/dto/response/BadgeResponse.java b/src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeResponse.java similarity index 68% rename from src/main/java/im/toduck/domain/badge/common/dto/response/BadgeResponse.java rename to src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeResponse.java index 8cd43688..5c6dd66c 100644 --- a/src/main/java/im/toduck/domain/badge/common/dto/response/BadgeResponse.java +++ b/src/main/java/im/toduck/domain/badge/presentation/dto/response/BadgeResponse.java @@ -1,30 +1,28 @@ -package im.toduck.domain.badge.common.dto.response; +package im.toduck.domain.badge.presentation.dto.response; import im.toduck.domain.badge.persistence.entity.Badge; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; -import lombok.Getter; -@Getter @Builder @Schema(description = "뱃지 정보 응답") -public class BadgeResponse { +public record BadgeResponse( @Schema(description = "뱃지 ID") - private Long id; + Long id, @Schema(description = "뱃지 코드") - private String code; + String code, @Schema(description = "뱃지 이름") - private String name; + String name, @Schema(description = "뱃지 설명") - private String description; + String description, @Schema(description = "뱃지 이미지 URL") - private String imageUrl; - - public static BadgeResponse from(Badge badge) { + String imageUrl +) { + public static BadgeResponse from(final Badge badge) { return BadgeResponse.builder() .id(badge.getId()) .code(badge.getCode().name()) diff --git a/src/main/java/im/toduck/global/exception/ExceptionCode.java b/src/main/java/im/toduck/global/exception/ExceptionCode.java index 6233865d..0a97a163 100644 --- a/src/main/java/im/toduck/global/exception/ExceptionCode.java +++ b/src/main/java/im/toduck/global/exception/ExceptionCode.java @@ -127,6 +127,7 @@ public enum ExceptionCode { /* 413xx Badge */ NOT_FOUND_BADGE(HttpStatus.NOT_FOUND, 41301, "뱃지를 찾을 수 없습니다."), ALREADY_ACQUIRED_BADGE(HttpStatus.CONFLICT, 41302, "이미 획득한 뱃지입니다."), + NOT_OWNED_BADGE(HttpStatus.FORBIDDEN, 41303, "보유하지 않은 뱃지입니다."), /* 431xx schedule */ NOT_FOUND_SCHEDULE_RECORD(HttpStatus.NOT_FOUND, 43101, "일정 기록을 찾을 수 없습니다.", diff --git a/src/test/java/im/toduck/builder/TestFixtureBuilder.java b/src/test/java/im/toduck/builder/TestFixtureBuilder.java index bb9a7a9b..147bac3c 100644 --- a/src/test/java/im/toduck/builder/TestFixtureBuilder.java +++ b/src/test/java/im/toduck/builder/TestFixtureBuilder.java @@ -140,7 +140,12 @@ public Badge buildBadge(final Badge badge) { return bs.badgeRepository().save(badge); } - public UserBadge buildUserBadge(final UserBadge userBadge) { + public UserBadge buildUserBadge(final User user, final Badge badge, boolean isRepresentative) { + UserBadge userBadge = UserBadge.builder() + .user(user) + .badge(badge) + .build(); + ReflectionTestUtils.setField(userBadge, "isRepresentative", isRepresentative); return bs.userBadgeRepository().save(userBadge); } } diff --git a/src/test/java/im/toduck/domain/badge/domain/service/BadgeServiceTest.java b/src/test/java/im/toduck/domain/badge/domain/service/BadgeServiceTest.java index 269d0654..a6385b13 100644 --- a/src/test/java/im/toduck/domain/badge/domain/service/BadgeServiceTest.java +++ b/src/test/java/im/toduck/domain/badge/domain/service/BadgeServiceTest.java @@ -14,7 +14,9 @@ import im.toduck.ServiceTest; import im.toduck.domain.badge.persistence.entity.Badge; import im.toduck.domain.badge.persistence.entity.UserBadge; +import im.toduck.domain.badge.presentation.dto.response.BadgeListResponse; import im.toduck.domain.user.persistence.entity.User; +import im.toduck.global.exception.CommonException; @Transactional class BadgeServiceTest extends ServiceTest { @@ -22,36 +24,108 @@ class BadgeServiceTest extends ServiceTest { @Autowired BadgeService badgeService; - private User user; - private Badge badge; + private User USER; @BeforeEach void setUp() { - user = testFixtureBuilder.buildUser(GENERAL_USER()); - badge = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + USER = testFixtureBuilder.buildUser(GENERAL_USER()); } @Test @DisplayName("배지를 처음 획득하면 UserBadge를 반환한다") void grantBadge_new() { + // given + Badge BABY_DUCK_BADGE = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + // when - UserBadge result = badgeService.grantBadge(user, badge.getCode()); + UserBadge result = badgeService.grantBadge(USER, BABY_DUCK_BADGE.getCode()); // then assertThat(result).isNotNull(); - assertThat(result.getBadge()).isEqualTo(badge); - assertThat(result.getUser()).isEqualTo(user); + assertThat(result.getBadge()).isEqualTo(BABY_DUCK_BADGE); + assertThat(result.getUser()).isEqualTo(USER); } @Test @DisplayName("이미 보유한 배지를 획득하려 하면 ALREADY_ACQUIRED_BADGE 예외가 발생한다") void grantBadge_duplicate() { // given - badgeService.grantBadge(user, badge.getCode()); + Badge BABY_DUCK_BADGE = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + badgeService.grantBadge(USER, BABY_DUCK_BADGE.getCode()); // when & then - assertThatThrownBy(() -> badgeService.grantBadge(user, badge.getCode())) - .isInstanceOf(im.toduck.global.exception.CommonException.class) + assertThatThrownBy(() -> badgeService.grantBadge(USER, BABY_DUCK_BADGE.getCode())) + .isInstanceOf(CommonException.class) .hasMessageContaining(ALREADY_ACQUIRED_BADGE.getMessage()); } + + @Test + @DisplayName("보유한 배지로 대표 뱃지를 설정할 수 있다.") + void setRepresentativeBadge_success() { + // given + Badge BABY_DUCK_BADGE = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + UserBadge userBadge = badgeService.grantBadge(USER, BABY_DUCK_BADGE.getCode()); + + // when + badgeService.setRepresentativeBadge(USER, BABY_DUCK_BADGE.getId()); + + // then + assertThat(userBadge.isRepresentative()).isTrue(); + } + + @Test + @DisplayName("기존 대표 배지가 있을 경우, 새로운 배지로 대표가 변경되고 기존 배지는 해제된다") + void setRepresentativeBadge_change() { + // given + Badge oldBadge = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + // 1. 첫 번째 뱃지 획득 및 대표 설정 + UserBadge oldUserBadge = testFixtureBuilder.buildUserBadge(USER, oldBadge, true); + + // 2. 두 번째 뱃지 생성 및 획득 + Badge newBadge = testFixtureBuilder.buildBadge(PERFECTIONIST_BADGE()); + UserBadge newUserBadge = testFixtureBuilder.buildUserBadge(USER, newBadge, false); + + // when + badgeService.setRepresentativeBadge(USER, newBadge.getId()); + + // then + assertThat(oldUserBadge.isRepresentative()).isFalse(); + assertThat(newUserBadge.isRepresentative()).isTrue(); + } + + @Test + @DisplayName("보유하지 않은 배지를 대표로 설정하려 하면 NOT_OWNED_BADGE 예외가 발생한다") + void setRepresentativeBadge_notOwned() { + // given + Badge notOwnedBadge = testFixtureBuilder.buildBadge(PERFECTIONIST_BADGE()); + + // when & then + assertThatThrownBy(() -> badgeService.setRepresentativeBadge(USER, notOwnedBadge.getId())) + .isInstanceOf(CommonException.class) + .hasMessageContaining(NOT_OWNED_BADGE.getMessage()); + } + + @Test + @DisplayName("내 배지 목록을 조회하면 전체 개수, 대표 뱃지 ID, 보유 뱃지 리스트를 반환한다") + void getMyBadgeList() { + // given + // 1. 뱃지 2개 생성 (전체 뱃지 개수: 2) + Badge firstBadge = testFixtureBuilder.buildBadge(BABY_DUCK_BADGE()); + Badge secondBadge = testFixtureBuilder.buildBadge(PERFECTIONIST_BADGE()); + + // 2. 사용자에게 첫 번째 뱃지 지급 및 대표 설정 + UserBadge userBadge1 = testFixtureBuilder.buildUserBadge(USER, firstBadge, true); + // 3. 사용자에게 두 번째 뱃지 지급 (대표 아님) + UserBadge userBadge2 = testFixtureBuilder.buildUserBadge(USER, secondBadge, false); + + // when + BadgeListResponse response = badgeService.getMyBadgeList(USER); + + // then + assertThat(response.totalCount()).isEqualTo(2); // 전체 뱃지 종류 수 + assertThat(response.representativeBadgeId()).isEqualTo(firstBadge.getId()); + assertThat(response.ownedBadges()).hasSize(2) + .extracting("id") + .containsExactlyInAnyOrder(firstBadge.getId(), secondBadge.getId()); + } } diff --git a/src/test/java/im/toduck/domain/badge/domain/usecase/BadgeUseCaseTest.java b/src/test/java/im/toduck/domain/badge/domain/usecase/BadgeUseCaseTest.java index e54881e1..2997bb3a 100644 --- a/src/test/java/im/toduck/domain/badge/domain/usecase/BadgeUseCaseTest.java +++ b/src/test/java/im/toduck/domain/badge/domain/usecase/BadgeUseCaseTest.java @@ -13,10 +13,10 @@ import org.springframework.transaction.annotation.Transactional; import im.toduck.ServiceTest; -import im.toduck.domain.badge.common.dto.response.BadgeResponse; import im.toduck.domain.badge.domain.service.BadgeService; import im.toduck.domain.badge.persistence.entity.Badge; import im.toduck.domain.badge.persistence.entity.UserBadge; +import im.toduck.domain.badge.presentation.dto.response.BadgeResponse; import im.toduck.domain.user.persistence.entity.User; @Transactional @@ -48,7 +48,7 @@ void getNewBadges_success() { // then assertThat(newBadges).hasSize(1); - assertThat(newBadges.get(0).getCode()).isEqualTo(badge.getCode().name()); + assertThat(newBadges.get(0).code()).isEqualTo(badge.getCode().name()); List unseenBadges = badgeService.getUnseenBadges(user); assertThat(unseenBadges).isEmpty(); diff --git a/src/test/java/im/toduck/fixtures/badge/BadgeFixtures.java b/src/test/java/im/toduck/fixtures/badge/BadgeFixtures.java index 58ac9087..b92ac09b 100644 --- a/src/test/java/im/toduck/fixtures/badge/BadgeFixtures.java +++ b/src/test/java/im/toduck/fixtures/badge/BadgeFixtures.java @@ -12,4 +12,13 @@ public static Badge BABY_DUCK_BADGE() { .imageUrl("https://example.com/images/badges/baby-duck.png") .build(); } + + public static Badge PERFECTIONIST_BADGE() { + return Badge.builder() + .code(BadgeCode.PERFECTIONIST) + .name("완벽주의") + .description("루틴 개수가 10개 이상입니다.") + .imageUrl("https://example.com/images/badges/perfectionist.png") + .build(); + } }