diff --git a/build.gradle b/build.gradle index 5da91ed50..5ba7f9aaa 100644 --- a/build.gradle +++ b/build.gradle @@ -115,7 +115,8 @@ def jacocoExcludePatterns = [ '**/auth/**', '**/domain/*', '**/domains/*', - '**/fixture/*' + '**/fixture/*', + '**/*Factory*' ] def jacocoExcludePatternsForVerify = [ @@ -130,7 +131,8 @@ def jacocoExcludePatternsForVerify = [ '*.auth.*', '*.domain.*', '*.domains.*', - '*.fixture.*' + '*.fixture.*', + '*.*Factory*' ] jacocoTestReport { diff --git a/src/main/java/com/somemore/center/repository/CenterRepository.java b/src/main/java/com/somemore/center/repository/CenterRepository.java index 946553a08..93d501b49 100644 --- a/src/main/java/com/somemore/center/repository/CenterRepository.java +++ b/src/main/java/com/somemore/center/repository/CenterRepository.java @@ -1,8 +1,10 @@ package com.somemore.center.repository; import com.somemore.center.domain.Center; +import com.somemore.center.repository.mapper.CenterOverviewInfo; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -14,5 +16,6 @@ default boolean doesNotExistById(UUID id) { return !existsById(id); } Optional
findCenterById(UUID id); + List findCenterOverviewsByIds(List ids); void deleteAllInBatch(); } diff --git a/src/main/java/com/somemore/center/repository/CenterRepositoryImpl.java b/src/main/java/com/somemore/center/repository/CenterRepositoryImpl.java index 67224b14f..b71acda45 100644 --- a/src/main/java/com/somemore/center/repository/CenterRepositoryImpl.java +++ b/src/main/java/com/somemore/center/repository/CenterRepositoryImpl.java @@ -1,9 +1,14 @@ package com.somemore.center.repository; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; import com.somemore.center.domain.Center; +import com.somemore.center.domain.QCenter; +import com.somemore.center.repository.mapper.CenterOverviewInfo; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -11,6 +16,7 @@ @Repository public class CenterRepositoryImpl implements CenterRepository { + private final JPAQueryFactory queryFactory; private final CenterJpaRepository centerJpaRepository; @Override @@ -28,6 +34,23 @@ public Optional
findCenterById(UUID id) { return centerJpaRepository.findCenterById(id); } + @Override + public List findCenterOverviewsByIds(List ids) { + QCenter center = QCenter.center; + + return queryFactory + .select(Projections.constructor( + CenterOverviewInfo.class, + center.id, + center.name, + center.imgUrl + )) + .from(center) + .where(center.id.in(ids) + .and(center.deleted.eq(false))) + .fetch(); + } + @Override public void deleteAllInBatch() { centerJpaRepository.deleteAllInBatch(); diff --git a/src/main/java/com/somemore/center/repository/mapper/CenterOverviewInfo.java b/src/main/java/com/somemore/center/repository/mapper/CenterOverviewInfo.java new file mode 100644 index 000000000..d770935aa --- /dev/null +++ b/src/main/java/com/somemore/center/repository/mapper/CenterOverviewInfo.java @@ -0,0 +1,12 @@ +package com.somemore.center.repository.mapper; + + +import java.util.UUID; + +public record CenterOverviewInfo( + UUID centerId, + String centerName, + String imgUrl +) { + +} diff --git a/src/main/java/com/somemore/center/service/query/CenterQueryService.java b/src/main/java/com/somemore/center/service/query/CenterQueryService.java index 29f0cfc4e..81a19f21b 100644 --- a/src/main/java/com/somemore/center/service/query/CenterQueryService.java +++ b/src/main/java/com/somemore/center/service/query/CenterQueryService.java @@ -1,6 +1,7 @@ package com.somemore.center.service.query; import com.somemore.center.domain.Center; +import com.somemore.center.repository.mapper.CenterOverviewInfo; import com.somemore.center.dto.response.CenterProfileResponseDto; import com.somemore.center.dto.response.PreferItemResponseDto; import com.somemore.center.repository.CenterRepository; @@ -33,6 +34,11 @@ public CenterProfileResponseDto getCenterProfileByCenterId(UUID centerId) { return CenterProfileResponseDto.of(center, preferItemDtos); } + @Override + public List getCenterOverviewsByIds(List centerIds) { + return centerRepository.findCenterOverviewsByIds(centerIds); + } + @Override public void validateCenterExists(UUID id) { if (centerRepository.doesNotExistById(id)) { diff --git a/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java b/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java index 7c12cb28c..3f381ec3c 100644 --- a/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java +++ b/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java @@ -1,11 +1,13 @@ package com.somemore.center.usecase.query; +import com.somemore.center.repository.mapper.CenterOverviewInfo; import com.somemore.center.dto.response.CenterProfileResponseDto; +import java.util.List; import java.util.UUID; public interface CenterQueryUseCase { - CenterProfileResponseDto getCenterProfileByCenterId(UUID centerId); + List getCenterOverviewsByIds(List centerIds); void validateCenterExists(UUID centerId); } diff --git a/src/main/java/com/somemore/interestcenter/controller/InterestCenterQueryApiController.java b/src/main/java/com/somemore/interestcenter/controller/InterestCenterQueryApiController.java new file mode 100644 index 000000000..1b8cd43d2 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/controller/InterestCenterQueryApiController.java @@ -0,0 +1,32 @@ +package com.somemore.interestcenter.controller; + +import com.somemore.global.common.response.ApiResponse; +import com.somemore.interestcenter.dto.response.InterestCentersResponseDto; +import com.somemore.interestcenter.usecase.InterestCenterQueryUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.UUID; + +@RequiredArgsConstructor +@RestController +@Tag(name = "Interest Center Query API", description = "관심 기관의 조회 API를 제공합니다") +public class InterestCenterQueryApiController { + + private final InterestCenterQueryUseCase interestCenterQueryUseCase; + + @Operation(summary = "관심기관 목록 조회 API") + @GetMapping("/api/interest-centers") + public ApiResponse> getInterestCenters(@AuthenticationPrincipal String volunteerId) { + + List responseDtos = interestCenterQueryUseCase.getInterestCenters(UUID.fromString(volunteerId)); + + return ApiResponse.ok(200, responseDtos, "관심기관 조회 성공"); + } + +} diff --git a/src/main/java/com/somemore/interestcenter/dto/response/InterestCentersResponseDto.java b/src/main/java/com/somemore/interestcenter/dto/response/InterestCentersResponseDto.java new file mode 100644 index 000000000..0584550b2 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/dto/response/InterestCentersResponseDto.java @@ -0,0 +1,28 @@ +package com.somemore.interestcenter.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.center.repository.mapper.CenterOverviewInfo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Builder +public record InterestCentersResponseDto( + @Schema(description = "관심기관의 ID", example = "123e4567-e89b-12d3-a456-426614174000") + UUID centerId, + @Schema(description = "관심기관의 이름", example = "서울 도서관") + String centerName, + @Schema(description = "관심기관의 프로필 이미지 링크", example = "~~/image.jpeg") + String imgUrl +) { + public static InterestCentersResponseDto of(CenterOverviewInfo responseDto) { + return InterestCentersResponseDto.builder() + .centerId(responseDto.centerId()) + .centerName(responseDto.centerName()) + .imgUrl(responseDto.imgUrl()) + .build(); + } +} diff --git a/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java b/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java index 4a20fa957..797c485c2 100644 --- a/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java +++ b/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java @@ -17,7 +17,7 @@ public record RegisterInterestCenterResponseDto( @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") UUID volunteerId, - @Schema(description = "센터 ID", example = "123e4567-e89b-12d3-a456-426614174000") + @Schema(description = "기관 ID", example = "123e4567-e89b-12d3-a456-426614174000") UUID centerId ) { public static RegisterInterestCenterResponseDto from(InterestCenter interestCenter) { diff --git a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java index 14b07b2d2..4292d2c4f 100644 --- a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java +++ b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java @@ -3,6 +3,7 @@ import com.somemore.interestcenter.domain.InterestCenter; import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -10,5 +11,6 @@ public interface InterestCenterRepository { InterestCenter save(InterestCenter interestCenter); Optional findById(Long id); Optional findInterestCenterResponseById(Long id); + List findInterestCenterIdsByVolunteerId(UUID volunteerId); boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId); } diff --git a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java index 5667967b8..b5f10c65c 100644 --- a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java +++ b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java @@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -61,6 +62,20 @@ public Optional findInterestCenterResponseByI return Optional.ofNullable(result); } + @Override + public List findInterestCenterIdsByVolunteerId(UUID volunteerId) { + QInterestCenter interestCenter = QInterestCenter.interestCenter; + + return queryFactory + .select(interestCenter.centerId) + .from(interestCenter) + .where( + interestCenter.volunteerId.eq(volunteerId) + .and(interestCenter.deleted.eq(false)) + ) + .fetch(); + } + @Override public boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId) { QInterestCenter interestCenter = QInterestCenter.interestCenter; diff --git a/src/main/java/com/somemore/interestcenter/service/InterestCenterQueryService.java b/src/main/java/com/somemore/interestcenter/service/InterestCenterQueryService.java new file mode 100644 index 000000000..ffd8b38fc --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/service/InterestCenterQueryService.java @@ -0,0 +1,34 @@ +package com.somemore.interestcenter.service; + +import com.somemore.center.repository.mapper.CenterOverviewInfo; +import com.somemore.center.usecase.query.CenterQueryUseCase; +import com.somemore.interestcenter.dto.response.InterestCentersResponseDto; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import com.somemore.interestcenter.usecase.InterestCenterQueryUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@RequiredArgsConstructor +@Service +public class InterestCenterQueryService implements InterestCenterQueryUseCase { + + private final CenterQueryUseCase centerQueryUseCase; + private final InterestCenterRepository interestCenterRepository; + + @Override + public List getInterestCenters(UUID volunteerId) { + + List interestCenterIds = interestCenterRepository.findInterestCenterIdsByVolunteerId(volunteerId); + if (interestCenterIds.isEmpty()) { + return List.of(); + } + + List centerOverviews = centerQueryUseCase.getCenterOverviewsByIds(interestCenterIds); + return centerOverviews.stream() + .map(InterestCentersResponseDto::of) + .toList(); + } +} diff --git a/src/main/java/com/somemore/interestcenter/usecase/InterestCenterQueryUseCase.java b/src/main/java/com/somemore/interestcenter/usecase/InterestCenterQueryUseCase.java new file mode 100644 index 000000000..c3015f597 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/usecase/InterestCenterQueryUseCase.java @@ -0,0 +1,10 @@ +package com.somemore.interestcenter.usecase; + +import com.somemore.interestcenter.dto.response.InterestCentersResponseDto; + +import java.util.List; +import java.util.UUID; + +public interface InterestCenterQueryUseCase { + List getInterestCenters(UUID volunteerId); +} diff --git a/src/test/java/com/somemore/CustomSecurityContextFactory.java b/src/test/java/com/somemore/CustomSecurityContextFactory.java new file mode 100644 index 000000000..5ec7d1bd6 --- /dev/null +++ b/src/test/java/com/somemore/CustomSecurityContextFactory.java @@ -0,0 +1,26 @@ +package com.somemore; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +import java.util.Collections; + +public class CustomSecurityContextFactory implements WithSecurityContextFactory { + @Override + public SecurityContext createSecurityContext(WithMockCustomUser annotation) { + SecurityContext context = SecurityContextHolder.createEmptyContext(); + + Authentication auth = new UsernamePasswordAuthenticationToken( + annotation.username(), + "password", + Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) + ); + + context.setAuthentication(auth); + return context; + } +} diff --git a/src/test/java/com/somemore/WithMockCustomUser.java b/src/test/java/com/somemore/WithMockCustomUser.java new file mode 100644 index 000000000..da5e38c23 --- /dev/null +++ b/src/test/java/com/somemore/WithMockCustomUser.java @@ -0,0 +1,12 @@ +package com.somemore; + +import org.springframework.security.test.context.support.WithSecurityContext; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = CustomSecurityContextFactory.class) +public @interface WithMockCustomUser { + String username() default "123e4567-e89b-12d3-a456-426614174000"; +} diff --git a/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java b/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java index 2cff5e618..d21a9f8a4 100644 --- a/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java +++ b/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java @@ -3,7 +3,9 @@ import com.somemore.IntegrationTestSupport; import com.somemore.center.domain.Center; import com.somemore.center.domain.PreferItem; +import com.somemore.center.repository.mapper.CenterOverviewInfo; import com.somemore.center.dto.response.CenterProfileResponseDto; +import com.somemore.center.repository.CenterJpaRepository; import com.somemore.center.repository.CenterRepository; import com.somemore.center.repository.PreferItemRepository; import com.somemore.global.exception.BadRequestException; @@ -17,9 +19,7 @@ import java.util.List; import java.util.UUID; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.*; @Transactional class CenterQueryServiceTest extends IntegrationTestSupport { @@ -29,6 +29,9 @@ class CenterQueryServiceTest extends IntegrationTestSupport { @Autowired private CenterRepository centerRepository; + + @Autowired + private CenterJpaRepository centerJpaRepository; @Autowired private PreferItemRepository preferItemRepository; @@ -37,15 +40,7 @@ class CenterQueryServiceTest extends IntegrationTestSupport { @Test void getCenterProfileById() { // given - Center center = Center.create( - "기본 기관 이름", - "010-1234-5678", - "http://example.com/image.jpg", - "기관 소개 내용", - "http://example.com", - "account123", - "password123" - ); + Center center = createCenter(); Center foundCenter = centerRepository.save(center); PreferItem preferItem = PreferItem.create(foundCenter.getId(), "어린이 동화책"); @@ -80,6 +75,30 @@ void getCenterProfileById() { .containsExactly("어린이 동화책", "간식"); } + @DisplayName("기관 Id들로 기관의 오버뷰 정보를 조회할 수 있다.") + @Test + void getCenterOverviewsByIds() { + //given + Center center = createCenter(); + Center center1 = createCenter(); + Center center2 = createCenter(); + centerJpaRepository.saveAll(List.of(center, center1, center2)); + List ids = List.of(center.getId(),center1.getId(), center2.getId()); + + //when + List responseDtos = centerQueryService.getCenterOverviewsByIds(ids); + + //then + assertThat(responseDtos) + .isNotNull() + .hasSize(3) + .extracting("centerId", "centerName", "imgUrl") + .containsExactlyInAnyOrder( + tuple(center.getId(), center.getName(), center.getImgUrl()), + tuple(center1.getId(), center1.getName(), center1.getImgUrl()), + tuple(center2.getId(), center2.getName(), center2.getImgUrl()) + ); + } @DisplayName("존재하지 않는 기관 ID를 검증할 수 있다.") @Test @@ -118,4 +137,16 @@ void validateExistingCenter() { // then assertThatCode(callable).doesNotThrowAnyException(); } + + private Center createCenter() { + return Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + } } diff --git a/src/test/java/com/somemore/interestcenter/controller/InterestCenterQueryApiControllerTest.java b/src/test/java/com/somemore/interestcenter/controller/InterestCenterQueryApiControllerTest.java new file mode 100644 index 000000000..80eabcea0 --- /dev/null +++ b/src/test/java/com/somemore/interestcenter/controller/InterestCenterQueryApiControllerTest.java @@ -0,0 +1,69 @@ +package com.somemore.interestcenter.controller; + +import com.somemore.ControllerTestSupport; +import com.somemore.WithMockCustomUser; +import com.somemore.interestcenter.dto.response.InterestCentersResponseDto; +import com.somemore.interestcenter.usecase.InterestCenterQueryUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; + +import java.util.List; +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class InterestCenterQueryApiControllerTest extends ControllerTestSupport { + + @MockBean + private InterestCenterQueryUseCase interestCenterQueryUseCase; + + @DisplayName("봉사자 ID로 관심기관 목록을 조회할 수 있다.") + @Test + @WithMockCustomUser + void getInterestCenters_ShouldReturnInterestCentersList() throws Exception { + // given + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID centerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174001"); + List responseDtos = List.of( + new InterestCentersResponseDto(centerId, "센터1", "http://image1.jpg"), + new InterestCentersResponseDto(centerId, "센터2", "http://image2.jpg") + ); + + given(interestCenterQueryUseCase.getInterestCenters(volunteerId)).willReturn(responseDtos); + + // when & then + mockMvc.perform(get("/api/interest-centers") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("관심기관 조회 성공")) + .andExpect(jsonPath("$.data").isArray()) + .andExpect(jsonPath("$.data[0].center_name").value("센터1")) + .andExpect(jsonPath("$.data[0].img_url").value("http://image1.jpg")) + .andExpect(jsonPath("$.data[1].center_name").value("센터2")) + .andExpect(jsonPath("$.data[1].img_url").value("http://image2.jpg")); + } + + + @DisplayName("봉사자 ID로 관심기관이 없을 경우 빈 리스트를 반환한다.") + @Test + @WithMockCustomUser + void getInterestCenters_ShouldReturnEmptyList_WhenNoInterestCenters() throws Exception { + // given + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + + given(interestCenterQueryUseCase.getInterestCenters(volunteerId)).willReturn(List.of()); + + // when & then + mockMvc.perform(get("/api/interest-centers") + .param("volunteerId", volunteerId.toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("관심기관 조회 성공")) + .andExpect(jsonPath("$.data").isEmpty()); + } +} + diff --git a/src/test/java/com/somemore/interestcenter/service/InterestCenterQueryServiceTest.java b/src/test/java/com/somemore/interestcenter/service/InterestCenterQueryServiceTest.java new file mode 100644 index 000000000..98aef2aa0 --- /dev/null +++ b/src/test/java/com/somemore/interestcenter/service/InterestCenterQueryServiceTest.java @@ -0,0 +1,108 @@ +package com.somemore.interestcenter.service; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import com.somemore.center.repository.CenterJpaRepository; +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.dto.response.InterestCentersResponseDto; +import com.somemore.interestcenter.repository.InterestCenterJpaRepository; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +@Transactional +class InterestCenterQueryServiceTest extends IntegrationTestSupport { + + + @Autowired + private InterestCenterQueryService interestCenterQueryService; + + @Autowired + private InterestCenterRepository interestCenterRepository; + + @Autowired + private InterestCenterJpaRepository interestCenterJpaRepository; + + @Autowired + private CenterJpaRepository centerJpaRepository; + + @DisplayName("봉사자 ID로 관심 센터 정보를 조회할 수 있다.") + @Test + void getInterestCenters() { + // given + UUID volunteerId = UUID.randomUUID(); + + Center center = createCenter(); + Center center1 = createCenter(); + Center center2 = createCenter(); + centerJpaRepository.saveAll(List.of(center, center1, center2)); + + InterestCenter interestCenter = createInterestCenter(volunteerId, center.getId()); + InterestCenter interestCenter1 = createInterestCenter(volunteerId, center1.getId()); + InterestCenter interestCenter2 = createInterestCenter(volunteerId, center2.getId()); + interestCenterJpaRepository.saveAll(List.of(interestCenter, interestCenter1, interestCenter2)); + + // when + List result = interestCenterQueryService.getInterestCenters(volunteerId); + + // then + assertThat(result) + .hasSize(3) + .extracting("centerId") + .containsExactlyInAnyOrder(center.getId(), center1.getId(), center2.getId()); + + assertThat(result) + .extracting("centerName") + .containsExactlyInAnyOrder("기본 기관 이름", "기본 기관 이름", "기본 기관 이름"); + + assertThat(result) + .extracting("imgUrl") + .containsExactlyInAnyOrder("http://image.jpg", "http://image.jpg", "http://image.jpg"); + } + + @DisplayName("봉사자의 관심 센터가 없을 경우 빈 리스트를 반환한다.") + @Test + void getInterestCenters_ReturnsEmptyList_WhenNoInterestCenters() { + // given + UUID volunteerId = UUID.randomUUID(); + + Center center = createCenter(); + centerJpaRepository.save(center); + + InterestCenter unrelatedInterestCenter = createInterestCenter(UUID.randomUUID(), center.getId()); + interestCenterJpaRepository.save(unrelatedInterestCenter); + + // when + List result = interestCenterQueryService.getInterestCenters(volunteerId); + + // then + assertThat(result).isEmpty(); + } + + + private Center createCenter() { + return Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + } + + private InterestCenter createInterestCenter(UUID volunteerId, UUID centerId) { + return InterestCenter.builder() + .volunteerId(volunteerId) + .centerId(centerId) + .build(); + } +} \ No newline at end of file