From 73da634ece928420611581f0f91ac3ceed6dc147 Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 00:00:05 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20=EA=B3=B5=ED=86=B5=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wayble/server/auth/resolver/CurrentUser.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/wayble/server/auth/resolver/CurrentUser.java diff --git a/src/main/java/com/wayble/server/auth/resolver/CurrentUser.java b/src/main/java/com/wayble/server/auth/resolver/CurrentUser.java new file mode 100644 index 00000000..ff32e4b3 --- /dev/null +++ b/src/main/java/com/wayble/server/auth/resolver/CurrentUser.java @@ -0,0 +1,8 @@ +package com.wayble.server.auth.resolver; + +import java.lang.annotation.*; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface CurrentUser {} From 8c4abe0b47b12ccff2b75f358c2e81845b2a9452 Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 00:06:46 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20=EB=A6=AC=EC=A1=B8=EB=B2=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=EC=97=90=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20ID=EB=A5=BC=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A3=BC=EC=9E=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resolver/CurrentUserArgumentResolver.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/com/wayble/server/auth/resolver/CurrentUserArgumentResolver.java diff --git a/src/main/java/com/wayble/server/auth/resolver/CurrentUserArgumentResolver.java b/src/main/java/com/wayble/server/auth/resolver/CurrentUserArgumentResolver.java new file mode 100644 index 00000000..091bad77 --- /dev/null +++ b/src/main/java/com/wayble/server/auth/resolver/CurrentUserArgumentResolver.java @@ -0,0 +1,45 @@ +package com.wayble.server.auth.resolver; + +import org.springframework.core.MethodParameter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(CurrentUser.class) + && Long.class.equals(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mav, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null) { + throw new IllegalStateException("인증 정보가 없습니다."); + } + + Object principal = auth.getPrincipal(); + if (principal instanceof Long l) return l; + if (principal instanceof Integer i) return i.longValue(); + if (principal instanceof String s) { + try { + return Long.parseLong(s); + } catch (NumberFormatException ignored) {} + } + try { + return Long.parseLong(auth.getName()); + } catch (Exception e) { + throw new IllegalStateException("userId를 추출할 수 없습니다.", e); + } + } +} \ No newline at end of file From fb407c28b827191fb42d977dd185d764847d169e Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 00:12:12 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[feat]=20=EB=A6=AC=EC=A1=B8=EB=B2=84=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20WebMvcConfig?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/common/config/WebMvcConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/wayble/server/common/config/WebMvcConfig.java diff --git a/src/main/java/com/wayble/server/common/config/WebMvcConfig.java b/src/main/java/com/wayble/server/common/config/WebMvcConfig.java new file mode 100644 index 00000000..cb514877 --- /dev/null +++ b/src/main/java/com/wayble/server/common/config/WebMvcConfig.java @@ -0,0 +1,21 @@ +package com.wayble.server.common.config; + +import com.wayble.server.auth.resolver.CurrentUserArgumentResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebMvcConfig implements WebMvcConfigurer { + + private final CurrentUserArgumentResolver currentUserArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(currentUserArgumentResolver); + } +} \ No newline at end of file From d21dca8c43e11f576ff3015392d6024fa59e147e Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 00:21:02 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[refactor]=20=EA=B0=81=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=EC=97=90=20=EB=A6=AC=EC=A1=B8=EB=B2=84=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/controller/ReviewController.java | 27 +----------- .../user/controller/UserPlaceController.java | 43 ++++--------------- 2 files changed, 10 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/wayble/server/review/controller/ReviewController.java b/src/main/java/com/wayble/server/review/controller/ReviewController.java index 5c460247..1661a7c9 100644 --- a/src/main/java/com/wayble/server/review/controller/ReviewController.java +++ b/src/main/java/com/wayble/server/review/controller/ReviewController.java @@ -1,5 +1,6 @@ package com.wayble.server.review.controller; +import com.wayble.server.auth.resolver.CurrentUser; import com.wayble.server.common.response.CommonResponse; import com.wayble.server.review.dto.ReviewRegisterDto; import com.wayble.server.review.dto.ReviewResponseDto; @@ -11,8 +12,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; @@ -38,9 +37,9 @@ public class ReviewController { }) public CommonResponse registerReview( @PathVariable Long waybleZoneId, + @CurrentUser Long userId, @RequestBody @Valid ReviewRegisterDto dto ) { - Long userId = extractUserId(); // 토큰에서 유저 ID 추출 reviewService.registerReview(waybleZoneId, userId, dto); return CommonResponse.success("리뷰가 등록되었습니다."); } @@ -57,26 +56,4 @@ public CommonResponse> getReviews( ) { return CommonResponse.success(reviewService.getReviews(waybleZoneId, sort)); } - - private Long extractUserId() { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null) { throw new IllegalStateException("인증 정보가 없습니다."); } - - Object p = auth.getPrincipal(); - if (p instanceof Long l) { return l; } - if (p instanceof Integer i) { return i.longValue(); } - if (p instanceof String s) { - try { - return Long.parseLong(s); - } catch (NumberFormatException e) { - throw new IllegalStateException("principal에서 userId 파싱 실패"); - } - } - try { - return Long.parseLong(auth.getName()); - } - catch (Exception e) { - throw new IllegalStateException("인증 정보에서 userId를 추출할 수 없습니다."); - } - } } \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java index ea1e6a64..5dc568a7 100644 --- a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java +++ b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java @@ -1,6 +1,7 @@ package com.wayble.server.user.controller; +import com.wayble.server.auth.resolver.CurrentUser; import com.wayble.server.common.response.CommonResponse; import com.wayble.server.user.dto.UserPlaceRemoveRequestDto; import com.wayble.server.user.dto.UserPlaceRequestDto; @@ -13,8 +14,6 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -35,9 +34,9 @@ public class UserPlaceController { @ApiResponse(responseCode = "403", description = "권한이 없습니다.") }) public CommonResponse saveUserPlace( + @CurrentUser Long userId, @RequestBody @Valid UserPlaceRequestDto request ) { - Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); userPlaceService.saveUserPlace(userId, request); // userId 파라미터로 넘김 return CommonResponse.success("장소가 저장되었습니다."); } @@ -50,9 +49,9 @@ public CommonResponse saveUserPlace( @ApiResponse(responseCode = "403", description = "권한이 없습니다.") }) public CommonResponse> getMyPlaceSummaries( + @CurrentUser Long userId, @RequestParam(name = "sort", defaultValue = "latest") String sort ) { - Long userId = extractUserId(); List summaries = userPlaceService.getMyPlaceSummaries(userId, sort); return CommonResponse.success(summaries); } @@ -67,11 +66,11 @@ public CommonResponse> getMyPlaceSummaries( @ApiResponse(responseCode = "403", description = "권한이 없습니다.") }) public CommonResponse> getZonesInPlace( + @CurrentUser Long userId, @RequestParam Long placeId, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "20") Integer size ) { - Long userId = extractUserId(); Page zones = userPlaceService.getZonesInPlace(userId, placeId, page, size); return CommonResponse.success(zones); } @@ -86,37 +85,11 @@ public CommonResponse> getZonesInPlace( @ApiResponse(responseCode = "404", description = "장소 또는 매핑 정보를 찾을 수 없음"), @ApiResponse(responseCode = "403", description = "권한이 없습니다.") }) - public CommonResponse removeZoneFromPlace(@RequestBody @Valid UserPlaceRemoveRequestDto request) { - Long userId = extractUserId(); + public CommonResponse removeZoneFromPlace( + @CurrentUser Long userId, + @RequestBody @Valid UserPlaceRemoveRequestDto request + ) { userPlaceService.removeZoneFromPlace(userId, request.placeId(), request.waybleZoneId()); return CommonResponse.success("제거되었습니다."); } - - - // SecurityContext에서 userId 추출하는 로직 - private Long extractUserId() { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null) { - throw new IllegalStateException("인증 정보가 없습니다."); - } - - Object p = auth.getPrincipal(); - - if (p instanceof Long l) { return l; } - if (p instanceof Integer i) { return i.longValue(); } - if (p instanceof String s) { - try { - return Long.parseLong(s); - } catch (NumberFormatException e) { - // 숫자 변환 실패 시 출력 - System.err.println("Principal 문자열을 Long으로 변환할 수 없습니다: " + s); - } - } - - try { - return Long.parseLong(auth.getName()); - } catch (Exception e) { - throw new IllegalStateException("인증 정보에서 userId를 추출할 수 없습니다. Principal=" + p, e); - } - } } From 6bbf8174d50492cfd1ac674b2637fd5aa8b1f98d Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 01:14:19 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[test]=20=EC=9B=A8=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=A1=B4=20=EB=A6=AC=EB=B7=B0=20=EB=93=B1=EB=A1=9D=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/service/ReviewServiceTest.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/test/java/com/wayble/server/review/service/ReviewServiceTest.java diff --git a/src/test/java/com/wayble/server/review/service/ReviewServiceTest.java b/src/test/java/com/wayble/server/review/service/ReviewServiceTest.java new file mode 100644 index 00000000..41fd9b8d --- /dev/null +++ b/src/test/java/com/wayble/server/review/service/ReviewServiceTest.java @@ -0,0 +1,106 @@ +package com.wayble.server.review.service; + +import com.wayble.server.common.exception.ApplicationException; +import com.wayble.server.review.dto.ReviewRegisterDto; +import com.wayble.server.review.entity.Review; +import com.wayble.server.review.entity.ReviewImage; +import com.wayble.server.review.repository.ReviewImageRepository; +import com.wayble.server.review.repository.ReviewRepository; +import com.wayble.server.user.entity.User; +import com.wayble.server.user.repository.UserRepository; +import com.wayble.server.wayblezone.entity.WaybleZone; +import com.wayble.server.wayblezone.repository.WaybleZoneRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class ReviewServiceTest { + + private final ReviewRepository reviewRepository = mock(ReviewRepository.class); + private final ReviewImageRepository reviewImageRepository = mock(ReviewImageRepository.class); + private final WaybleZoneRepository waybleZoneRepository = mock(WaybleZoneRepository.class); + private final UserRepository userRepository = mock(UserRepository.class); + + private final ReviewService sut = + new ReviewService(reviewRepository, reviewImageRepository, waybleZoneRepository, userRepository); + + @Test + @DisplayName("리뷰 등록 성공 - 평점 갱신, 카운트 증가, 이미지 저장") + void t1() { + Long zoneId = 10L; + Long userId = 5L; + + WaybleZone zone = mock(WaybleZone.class); + when(waybleZoneRepository.findById(zoneId)).thenReturn(Optional.of(zone)); + when(zone.getRating()).thenReturn(4.0); + when(zone.getReviewCount()).thenReturn(1L); + + User user = mock(User.class); + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + + ReviewRegisterDto dto = new ReviewRegisterDto( + "뷰가 좋고 접근성이 좋아요", + 5.0, + LocalDate.of(2025, 6, 30), + List.of("주차장 있음", "장애인 화장실 있음"), + List.of("https://image.url/review1.jpg") + ); + + doAnswer(invocation -> invocation.getArgument(0)) + .when(reviewRepository).save(any(Review.class)); + + sut.registerReview(zoneId, userId, dto); + + + verify(reviewRepository, times(1)).save(any(Review.class)); + + ArgumentCaptor ratingCaptor = ArgumentCaptor.forClass(Double.class); + verify(zone, times(1)).updateRating(ratingCaptor.capture()); + + assertEquals(4.5, ratingCaptor.getValue(), 1e-6); + + verify(zone, times(1)).addReviewCount(1L); + verify(reviewImageRepository, times(1)).save(any(ReviewImage.class)); + verify(waybleZoneRepository, times(1)).save(zone); + } + + @Test + @DisplayName("리뷰 등록 실패 - 웨이블존 없음") + void t2() { + Long zoneId = 99L; + Long userId = 1L; + when(waybleZoneRepository.findById(zoneId)).thenReturn(Optional.empty()); + + ReviewRegisterDto dto = new ReviewRegisterDto( + "좋아요", 4.0, LocalDate.now(), List.of("주차장"), List.of() + ); + + assertThrows(ApplicationException.class, + () -> sut.registerReview(zoneId, userId, dto)); + } + + @Test + @DisplayName("리뷰 등록 실패 - 유저 없음") + void t3() { + Long zoneId = 10L; + Long userId = 999L; + + WaybleZone zone = mock(WaybleZone.class); + when(waybleZoneRepository.findById(zoneId)).thenReturn(Optional.of(zone)); + when(userRepository.findById(userId)).thenReturn(Optional.empty()); + + ReviewRegisterDto dto = new ReviewRegisterDto( + "좋아요", 4.0, LocalDate.now(), List.of("주차장"), List.of() + ); + + assertThrows(ApplicationException.class, + () -> sut.registerReview(zoneId, userId, dto)); + } +} \ No newline at end of file From f7e0c4c7df1e938845d84d34b67f49f9f1488d5a Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 01:44:29 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[refactor]=20=EC=9B=A8=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=A1=B4=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20ResponseD?= =?UTF-8?q?to=EC=97=90=20=EC=9C=84=EB=8F=84,=EA=B2=BD=EB=8F=84=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/wayblezone/dto/WaybleZoneDetailResponseDto.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wayble/server/wayblezone/dto/WaybleZoneDetailResponseDto.java b/src/main/java/com/wayble/server/wayblezone/dto/WaybleZoneDetailResponseDto.java index 93d0bc20..3f252816 100644 --- a/src/main/java/com/wayble/server/wayblezone/dto/WaybleZoneDetailResponseDto.java +++ b/src/main/java/com/wayble/server/wayblezone/dto/WaybleZoneDetailResponseDto.java @@ -18,7 +18,9 @@ public record WaybleZoneDetailResponseDto( String imageUrl, FacilityDto facilities, Map businessHours, - List photos + List photos, + Double latitude, + Double longitude ) { @Builder public record BusinessHourDto( From 3e56e8d50f200f3e4c221264cdbac36d2f829b37 Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 01:46:53 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[refactor]=20WaybleZoneService=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=A7=A4=ED=95=91=EC=97=90=20=EC=9C=84=EB=8F=84,?= =?UTF-8?q?=EA=B2=BD=EB=8F=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wayble/server/wayblezone/service/WaybleZoneService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/wayble/server/wayblezone/service/WaybleZoneService.java b/src/main/java/com/wayble/server/wayblezone/service/WaybleZoneService.java index 0707bdc5..490e93b8 100644 --- a/src/main/java/com/wayble/server/wayblezone/service/WaybleZoneService.java +++ b/src/main/java/com/wayble/server/wayblezone/service/WaybleZoneService.java @@ -83,6 +83,9 @@ public WaybleZoneDetailResponseDto getWaybleZoneDetail(Long waybleZoneId) { .build()); } + Double lat = zone.getAddress() != null ? zone.getAddress().getLatitude() : null; + Double lon = zone.getAddress() != null ? zone.getAddress().getLongitude() : null; + return WaybleZoneDetailResponseDto.builder() .waybleZoneId(zone.getId()) .name(zone.getZoneName()) @@ -102,6 +105,8 @@ public WaybleZoneDetailResponseDto getWaybleZoneDetail(Long waybleZoneId) { .floorInfo(f.getFloorInfo()) .build()) .businessHours(businessHours) + .latitude(lat) + .longitude(lon) .build(); } From cf2b256bb2f7d206a9d1b2b198f0df596cef7aa0 Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 18 Aug 2025 02:30:33 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[test]=20=EC=9B=A8=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=A1=B4=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/WaybleZoneControllerTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/com/wayble/server/wayblezone/controller/WaybleZoneControllerTest.java diff --git a/src/test/java/com/wayble/server/wayblezone/controller/WaybleZoneControllerTest.java b/src/test/java/com/wayble/server/wayblezone/controller/WaybleZoneControllerTest.java new file mode 100644 index 00000000..407d1946 --- /dev/null +++ b/src/test/java/com/wayble/server/wayblezone/controller/WaybleZoneControllerTest.java @@ -0,0 +1,94 @@ +package com.wayble.server.wayblezone.controller; + +import com.wayble.server.common.dto.FacilityDto; +import com.wayble.server.common.response.CommonResponse; +import com.wayble.server.wayblezone.dto.WaybleZoneListResponseDto; +import com.wayble.server.wayblezone.service.WaybleZoneService; +import org.junit.jupiter.api.DisplayName; +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.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +public class WaybleZoneControllerTest { + @Mock + private WaybleZoneService waybleZoneService; + + @InjectMocks + private WaybleZoneController waybleZoneController; + + @Test + @DisplayName("웨이블존 목록 조회 - 성공") + void t1() { + FacilityDto facilities = FacilityDto.builder() + .hasSlope(true) + .hasNoDoorStep(true) + .hasElevator(false) + .hasTableSeat(true) + .hasDisabledToilet(false) + .floorInfo("1층") + .build(); + + WaybleZoneListResponseDto item1 = WaybleZoneListResponseDto.builder() + .waybleZoneId(1L) + .name("스타벅스 강남점") + .category("카페") + .address("서울 강남구 강남대로 446 (역삼동)") + .rating(4.5) + .reviewCount(23L) + .imageUrl("https://image.url/wayble1.jpg") + .contactNumber("02-558-2161") + .facilities(facilities) + .build(); + + WaybleZoneListResponseDto item2 = WaybleZoneListResponseDto.builder() + .waybleZoneId(2L) + .name("메가엠지씨커피 강남중앙점") + .category("카페") + .address("서울특별시 서초구 서초대로77길 35, 1층 102호 (서초동)") + .rating(4.2) + .reviewCount(520L) + .imageUrl("https://image.url/wayble1.jpg") + .contactNumber("02-533-0656") + .facilities(facilities) + .build(); + + when(waybleZoneService.getWaybleZones("서초구", "카페")) + .thenReturn(List.of(item1, item2)); + + CommonResponse> response = waybleZoneController.getWaybleZoneList("서초구", "카페"); + + assertNotNull(response); + assertNotNull(response.getData()); + assertEquals(2, response.getData().size()); + + WaybleZoneListResponseDto first = response.getData().get(0); + assertEquals(1L, first.waybleZoneId()); + assertEquals("스타벅스 강남점", first.name()); + assertEquals("카페", first.category()); + assertTrue(first.address().contains("강남대로")); + assertEquals(4.5, first.rating(), 1e-6); + assertEquals(23L, first.reviewCount()); + assertEquals("https://image.url/wayble1.jpg", first.imageUrl()); + assertEquals("02-558-2161", first.contactNumber()); + assertNotNull(first.facilities()); + assertTrue(first.facilities().hasSlope()); + assertTrue(first.facilities().hasNoDoorStep()); + assertFalse(first.facilities().hasElevator()); + assertTrue(first.facilities().hasTableSeat()); + assertFalse(first.facilities().hasDisabledToilet()); + assertEquals("1층", first.facilities().floorInfo()); + + WaybleZoneListResponseDto second = response.getData().get(1); + assertEquals(2L, second.waybleZoneId()); + assertEquals(520L, second.reviewCount()); + } +}