From 6865a2dd384358cc67877e5a3889b7e9d8fbb232 Mon Sep 17 00:00:00 2001 From: mike7643 Date: Mon, 29 Jun 2026 12:39:59 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=AA=85=EC=9D=84=20userLat=EA=B3=BC=20userLng=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/LockerSearchFilterFactory.java | 35 +++++++++++++++++++ .../application/pin/LockerPinQuery.java | 13 ++++--- .../pin/LockerPinQueryService.java | 11 +++--- .../dto/request/pin/LockerPinRequest.java | 9 +++-- 4 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/zimdugo/locker/application/filter/LockerSearchFilterFactory.java diff --git a/src/main/java/com/zimdugo/locker/application/filter/LockerSearchFilterFactory.java b/src/main/java/com/zimdugo/locker/application/filter/LockerSearchFilterFactory.java new file mode 100644 index 00000000..001a2ad5 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/application/filter/LockerSearchFilterFactory.java @@ -0,0 +1,35 @@ +package com.zimdugo.locker.application.filter; + +import com.zimdugo.locker.domain.search.LockerSearchFilter; +import java.util.Set; +import java.util.stream.Collectors; + +public final class LockerSearchFilterFactory { + + private LockerSearchFilterFactory() { + } + + public static LockerSearchFilter create( + Set sizeTypes, + Set indoorOutdoorTypes, + Set lockerTypes + ) { + return new LockerSearchFilter( + sizeTypes == null + ? Set.of() + : sizeTypes.stream() + .map(LockerSizeFilterType::toDomain) + .collect(Collectors.toUnmodifiableSet()), + indoorOutdoorTypes == null + ? Set.of() + : indoorOutdoorTypes.stream() + .map(IndoorOutdoorFilterType::toDomain) + .collect(Collectors.toUnmodifiableSet()), + lockerTypes == null + ? Set.of() + : lockerTypes.stream() + .map(LockerFacilityFilterType::toDomain) + .collect(Collectors.toUnmodifiableSet()) + ); + } +} diff --git a/src/main/java/com/zimdugo/locker/application/pin/LockerPinQuery.java b/src/main/java/com/zimdugo/locker/application/pin/LockerPinQuery.java index 3e86df93..fc826422 100644 --- a/src/main/java/com/zimdugo/locker/application/pin/LockerPinQuery.java +++ b/src/main/java/com/zimdugo/locker/application/pin/LockerPinQuery.java @@ -1,5 +1,8 @@ package com.zimdugo.locker.application.pin; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import java.util.Set; public record LockerPinQuery( @@ -8,12 +11,12 @@ public record LockerPinQuery( double neLat, double neLng, double zoomLevel, - Double latitude, - Double longitude, + Double userLat, + Double userLng, String keyword, - Set sizeTypes, - Set indoorOutdoorTypes, - Set lockerTypes + Set sizeTypes, + Set indoorOutdoorTypes, + Set lockerTypes ) { public LockerPinQuery( double swLat, diff --git a/src/main/java/com/zimdugo/locker/application/pin/LockerPinQueryService.java b/src/main/java/com/zimdugo/locker/application/pin/LockerPinQueryService.java index b1419802..05f946af 100644 --- a/src/main/java/com/zimdugo/locker/application/pin/LockerPinQueryService.java +++ b/src/main/java/com/zimdugo/locker/application/pin/LockerPinQueryService.java @@ -1,5 +1,6 @@ package com.zimdugo.locker.application.pin; +import com.zimdugo.locker.application.filter.LockerSearchFilterFactory; import com.zimdugo.locker.application.common.LocationValidator; import com.zimdugo.locker.application.search.LockerSearchResultQueryService; import com.zimdugo.locker.application.result.search.LockerSearchItemResult; @@ -29,9 +30,9 @@ public class LockerPinQueryService { private final LockerSearchResultQueryService lockerSearchResultQueryService; public LockerPinResult getPins(Long userId, LockerPinQuery query) { - LocationValidator.validateBounds(query.swLat(), query.swLng(), query.neLat(), query.neLng()); + LocationValidator.validateBounds(query.swLat(), query.swLng(), query.neLat(), query.neLng()); //좌표 검증 - LockerSearchFilter filter = LockerSearchFilter.from( + LockerSearchFilter filter = LockerSearchFilterFactory.create( query.sizeTypes(), query.indoorOutdoorTypes(), query.lockerTypes() @@ -58,14 +59,14 @@ public LockerPinResult getPins(Long userId, LockerPinQuery query) { } private LockerPinResult getSearchPins(Long userId, LockerPinQuery query, LockerSearchFilter filter) { - if (query.latitude() == null || query.longitude() == null) { + if (query.userLat() == null || query.userLng() == null) { return LockerPinResult.empty(); } List items = lockerSearchResultQueryService.getDisplayableSearchItemsForPins( userId, - query.latitude(), - query.longitude(), + query.userLat(), + query.userLng(), query.keyword(), filter ); diff --git a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java index f3f6b62e..ab98ca72 100644 --- a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java +++ b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java @@ -1,6 +1,9 @@ package com.zimdugo.locker.entrypoint.dto.request.pin; import com.zimdugo.locker.application.pin.LockerPinQuery; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.DecimalMax; @@ -54,13 +57,13 @@ public record LockerPinRequest( String keyword, @Parameter(description = "보관함 크기 필터") - Set sizeTypes, + Set sizeTypes, @Parameter(description = "실내/실외 필터") - Set indoorOutdoorTypes, + Set indoorOutdoorTypes, @Parameter(description = "보관함 유형 필터") - Set lockerTypes + Set lockerTypes ) { public LockerPinQuery toQuery() { return new LockerPinQuery( From fb5197cf83baeb450348499a272ab87fbc4f77dd Mon Sep 17 00:00:00 2001 From: mike7643 Date: Mon, 29 Jun 2026 16:22:46 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=EA=B2=80=EC=83=89=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=ED=8C=8C=EC=8B=B1=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/IndoorOutdoorFilterType.java | 12 ++++ .../filter/LockerFacilityFilterType.java | 18 ++++++ .../filter/LockerSizeFilterType.java | 13 ++++ .../place/PlaceLockerQueryCommand.java | 9 ++- .../place/PlaceLockerQueryService.java | 3 +- .../search/LockerSearchCommand.java | 9 ++- .../LockerSearchResultQueryService.java | 3 +- .../domain/search/LockerSearchFilter.java | 63 ------------------- .../config/LockerRequestWebMvcConfig.java | 19 ++++++ .../IndoorOutdoorTypeRequestConverter.java | 24 +++++++ .../LockerFilterRequestValueNormalizer.java | 14 +++++ .../LockerSizeTypeRequestConverter.java | 24 +++++++ .../converter/LockerTypeRequestConverter.java | 24 +++++++ .../dto/request/place/PlaceLockerRequest.java | 9 ++- .../request/search/LockerSearchRequest.java | 9 ++- .../pin/LockerPinQueryServiceTest.java | 40 ++++++++---- .../place/PlaceLockerQueryServiceTest.java | 9 ++- .../LockerSearchResultQueryServiceTest.java | 15 +++-- .../domain/search/LockerSearchFilterTest.java | 54 +++++----------- .../LockerFilterRequestConverterTest.java | 50 +++++++++++++++ .../search/LockerSearchRequestTest.java | 36 +++++++++++ 21 files changed, 321 insertions(+), 136 deletions(-) create mode 100644 src/main/java/com/zimdugo/locker/application/filter/IndoorOutdoorFilterType.java create mode 100644 src/main/java/com/zimdugo/locker/application/filter/LockerFacilityFilterType.java create mode 100644 src/main/java/com/zimdugo/locker/application/filter/LockerSizeFilterType.java create mode 100644 src/main/java/com/zimdugo/locker/entrypoint/config/LockerRequestWebMvcConfig.java create mode 100644 src/main/java/com/zimdugo/locker/entrypoint/converter/IndoorOutdoorTypeRequestConverter.java create mode 100644 src/main/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestValueNormalizer.java create mode 100644 src/main/java/com/zimdugo/locker/entrypoint/converter/LockerSizeTypeRequestConverter.java create mode 100644 src/main/java/com/zimdugo/locker/entrypoint/converter/LockerTypeRequestConverter.java create mode 100644 src/test/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestConverterTest.java create mode 100644 src/test/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequestTest.java diff --git a/src/main/java/com/zimdugo/locker/application/filter/IndoorOutdoorFilterType.java b/src/main/java/com/zimdugo/locker/application/filter/IndoorOutdoorFilterType.java new file mode 100644 index 00000000..41aa001d --- /dev/null +++ b/src/main/java/com/zimdugo/locker/application/filter/IndoorOutdoorFilterType.java @@ -0,0 +1,12 @@ +package com.zimdugo.locker.application.filter; + +import com.zimdugo.locker.domain.locker.IndoorOutdoorType; + +public enum IndoorOutdoorFilterType { + INDOOR, + OUTDOOR; + + public IndoorOutdoorType toDomain() { + return IndoorOutdoorType.valueOf(name()); + } +} diff --git a/src/main/java/com/zimdugo/locker/application/filter/LockerFacilityFilterType.java b/src/main/java/com/zimdugo/locker/application/filter/LockerFacilityFilterType.java new file mode 100644 index 00000000..2ae88596 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/application/filter/LockerFacilityFilterType.java @@ -0,0 +1,18 @@ +package com.zimdugo.locker.application.filter; + +import com.zimdugo.locker.domain.locker.LockerType; + +public enum LockerFacilityFilterType { + MUSEUM, + SUBWAY_STATION, + DEPARTMENT_STORE, + CONVENIENCE_STORE, + PUBLIC_OFFICE, + PRIVATE_LOCKER, + TRAIN_STATION, + ETC; + + public LockerType toDomain() { + return LockerType.valueOf(name()); + } +} diff --git a/src/main/java/com/zimdugo/locker/application/filter/LockerSizeFilterType.java b/src/main/java/com/zimdugo/locker/application/filter/LockerSizeFilterType.java new file mode 100644 index 00000000..9a9923fd --- /dev/null +++ b/src/main/java/com/zimdugo/locker/application/filter/LockerSizeFilterType.java @@ -0,0 +1,13 @@ +package com.zimdugo.locker.application.filter; + +import com.zimdugo.locker.domain.locker.LockerSizeType; + +public enum LockerSizeFilterType { + SMALL, + MEDIUM, + LARGE; + + public LockerSizeType toDomain() { + return LockerSizeType.valueOf(name()); + } +} diff --git a/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryCommand.java b/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryCommand.java index ce988b5c..ef70e85b 100644 --- a/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryCommand.java +++ b/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryCommand.java @@ -1,13 +1,16 @@ package com.zimdugo.locker.application.place; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import java.util.Set; public record PlaceLockerQueryCommand( Long placeId, double latitude, double longitude, - Set sizeTypes, - Set indoorOutdoorTypes, - Set lockerTypes + Set sizeTypes, + Set indoorOutdoorTypes, + Set lockerTypes ) { } diff --git a/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryService.java b/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryService.java index 102e4f98..c6bdfedc 100644 --- a/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryService.java +++ b/src/main/java/com/zimdugo/locker/application/place/PlaceLockerQueryService.java @@ -1,5 +1,6 @@ package com.zimdugo.locker.application.place; +import com.zimdugo.locker.application.filter.LockerSearchFilterFactory; import com.zimdugo.common.i18n.CurrentRequestLanguage; import com.zimdugo.core.exception.BusinessException; import com.zimdugo.core.exception.ErrorCode; @@ -37,7 +38,7 @@ public PlaceLockerResult getPlaceLockers(Long userId, PlaceLockerQueryCommand co LockerPlace place = lockerPlaceReader.readById(command.placeId(), languageCode) .orElseThrow(() -> new BusinessException(ErrorCode.PLACE_NOT_FOUND)); - LockerSearchFilter filter = LockerSearchFilter.from( + LockerSearchFilter filter = LockerSearchFilterFactory.create( command.sizeTypes(), command.indoorOutdoorTypes(), command.lockerTypes() diff --git a/src/main/java/com/zimdugo/locker/application/search/LockerSearchCommand.java b/src/main/java/com/zimdugo/locker/application/search/LockerSearchCommand.java index abdf7ae4..51dba071 100644 --- a/src/main/java/com/zimdugo/locker/application/search/LockerSearchCommand.java +++ b/src/main/java/com/zimdugo/locker/application/search/LockerSearchCommand.java @@ -1,14 +1,17 @@ package com.zimdugo.locker.application.search; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import java.util.Set; public record LockerSearchCommand( double latitude, double longitude, String keyword, - Set sizeTypes, - Set indoorOutdoorTypes, - Set lockerTypes, + Set sizeTypes, + Set indoorOutdoorTypes, + Set lockerTypes, Double zoom ) { } diff --git a/src/main/java/com/zimdugo/locker/application/search/LockerSearchResultQueryService.java b/src/main/java/com/zimdugo/locker/application/search/LockerSearchResultQueryService.java index c3463b3d..95ac98bf 100644 --- a/src/main/java/com/zimdugo/locker/application/search/LockerSearchResultQueryService.java +++ b/src/main/java/com/zimdugo/locker/application/search/LockerSearchResultQueryService.java @@ -1,5 +1,6 @@ package com.zimdugo.locker.application.search; +import com.zimdugo.locker.application.filter.LockerSearchFilterFactory; import com.zimdugo.common.i18n.CurrentRequestLanguage; import com.zimdugo.locker.application.pin.LockerSearchPinAssembler; import com.zimdugo.locker.application.pin.LockerPinClusterer; @@ -102,7 +103,7 @@ public LockerSearchResult getSearchResults( } private LockerSearchFilter toSearchFilter(LockerSearchCommand command) { - return LockerSearchFilter.from( + return LockerSearchFilterFactory.create( command.sizeTypes(), command.indoorOutdoorTypes(), command.lockerTypes() diff --git a/src/main/java/com/zimdugo/locker/domain/search/LockerSearchFilter.java b/src/main/java/com/zimdugo/locker/domain/search/LockerSearchFilter.java index d42b9404..65730119 100644 --- a/src/main/java/com/zimdugo/locker/domain/search/LockerSearchFilter.java +++ b/src/main/java/com/zimdugo/locker/domain/search/LockerSearchFilter.java @@ -1,21 +1,11 @@ package com.zimdugo.locker.domain.search; -import com.zimdugo.core.exception.BusinessException; -import com.zimdugo.core.exception.ErrorCode; import com.zimdugo.locker.domain.locker.IndoorOutdoorType; import com.zimdugo.locker.domain.locker.LockerSizeType; import com.zimdugo.locker.domain.locker.LockerType; -import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.Locale; -import java.util.Objects; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; - -@Slf4j public record LockerSearchFilter( Set sizeTypes, Set indoorOutdoorTypes, @@ -38,30 +28,6 @@ public static LockerSearchFilter empty() { return new LockerSearchFilter(Set.of(), Set.of(), Set.of()); } - public static LockerSearchFilter from( - Set sizeTypes, - Set indoorOutdoorTypes, - Set lockerTypes - ) { - log.debug( - "검색 필터 파싱 시작. sizeTypes={}, indoorOutdoorTypes={}, lockerTypes={}", - sizeTypes, - indoorOutdoorTypes, - lockerTypes - ); - - Set parsedSizeTypes = parseEnumSet(sizeTypes, LockerSizeType::from); - Set parsedIndoorOutdoorTypes = - parseEnumSet(indoorOutdoorTypes, val -> parse(val, IndoorOutdoorType.class)); - Set parsedLockerTypes = parseEnumSet(lockerTypes, val -> parse(val, LockerType.class)); - - return new LockerSearchFilter( - parsedSizeTypes, - parsedIndoorOutdoorTypes, - parsedLockerTypes - ); - } - public boolean isEmpty() { return sizeTypes.isEmpty() && indoorOutdoorTypes.isEmpty() && lockerTypes.isEmpty(); } @@ -75,33 +41,4 @@ public boolean matches( && (indoorOutdoorTypes.isEmpty() || indoorOutdoorTypes.contains(lockerIndoorOutdoorType)) && (lockerTypes.isEmpty() || lockerTypes.contains(actualLockerType)); } - - private static > Set parseEnumSet( - Set values, - Function mapper - ) { - if (values == null) { - return Set.of(); - } - return values.stream() - .filter(Objects::nonNull) - .map(value -> value.replaceAll("[\\[\\]\"]", "")) - .flatMap(value -> Arrays.stream(value.split(","))) - .map(String::trim) - .map(mapper) - .filter(Objects::nonNull) - .collect(Collectors.toUnmodifiableSet()); - } - - private static > T parse(String value, Class enumType) { - if (value == null || value.isBlank()) { - return null; - } - - try { - return Enum.valueOf(enumType, value.trim().toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException e) { - throw new BusinessException(ErrorCode.INVALID_PARAMETER_FORMAT); - } - } } diff --git a/src/main/java/com/zimdugo/locker/entrypoint/config/LockerRequestWebMvcConfig.java b/src/main/java/com/zimdugo/locker/entrypoint/config/LockerRequestWebMvcConfig.java new file mode 100644 index 00000000..31a5aa8c --- /dev/null +++ b/src/main/java/com/zimdugo/locker/entrypoint/config/LockerRequestWebMvcConfig.java @@ -0,0 +1,19 @@ +package com.zimdugo.locker.entrypoint.config; + +import com.zimdugo.locker.entrypoint.converter.IndoorOutdoorTypeRequestConverter; +import com.zimdugo.locker.entrypoint.converter.LockerSizeTypeRequestConverter; +import com.zimdugo.locker.entrypoint.converter.LockerTypeRequestConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class LockerRequestWebMvcConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new LockerSizeTypeRequestConverter()); + registry.addConverter(new IndoorOutdoorTypeRequestConverter()); + registry.addConverter(new LockerTypeRequestConverter()); + } +} diff --git a/src/main/java/com/zimdugo/locker/entrypoint/converter/IndoorOutdoorTypeRequestConverter.java b/src/main/java/com/zimdugo/locker/entrypoint/converter/IndoorOutdoorTypeRequestConverter.java new file mode 100644 index 00000000..ad39cd01 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/entrypoint/converter/IndoorOutdoorTypeRequestConverter.java @@ -0,0 +1,24 @@ +package com.zimdugo.locker.entrypoint.converter; + +import com.zimdugo.core.exception.BusinessException; +import com.zimdugo.core.exception.ErrorCode; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import java.util.Locale; +import org.springframework.core.convert.converter.Converter; + +public class IndoorOutdoorTypeRequestConverter implements Converter { + + @Override + public IndoorOutdoorFilterType convert(String source) { + String normalized = LockerFilterRequestValueNormalizer.normalize(source); + if (normalized == null || normalized.isBlank()) { + return null; + } + + try { + return IndoorOutdoorFilterType.valueOf(normalized.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.INVALID_PARAMETER_FORMAT); + } + } +} diff --git a/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestValueNormalizer.java b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestValueNormalizer.java new file mode 100644 index 00000000..68cba459 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestValueNormalizer.java @@ -0,0 +1,14 @@ +package com.zimdugo.locker.entrypoint.converter; + +final class LockerFilterRequestValueNormalizer { + + private LockerFilterRequestValueNormalizer() { + } + + static String normalize(String source) { + if (source == null) { + return null; + } + return source.replaceAll("[\\[\\]\"]", "").trim(); + } +} diff --git a/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerSizeTypeRequestConverter.java b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerSizeTypeRequestConverter.java new file mode 100644 index 00000000..98f955c8 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerSizeTypeRequestConverter.java @@ -0,0 +1,24 @@ +package com.zimdugo.locker.entrypoint.converter; + +import com.zimdugo.core.exception.BusinessException; +import com.zimdugo.core.exception.ErrorCode; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; +import java.util.Locale; +import org.springframework.core.convert.converter.Converter; + +public class LockerSizeTypeRequestConverter implements Converter { + + @Override + public LockerSizeFilterType convert(String source) { + String normalized = LockerFilterRequestValueNormalizer.normalize(source); + if (normalized == null || normalized.isBlank()) { + return null; + } + + try { + return LockerSizeFilterType.valueOf(normalized.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.INVALID_LOCKER_SIZE_TYPE); + } + } +} diff --git a/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerTypeRequestConverter.java b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerTypeRequestConverter.java new file mode 100644 index 00000000..61454e02 --- /dev/null +++ b/src/main/java/com/zimdugo/locker/entrypoint/converter/LockerTypeRequestConverter.java @@ -0,0 +1,24 @@ +package com.zimdugo.locker.entrypoint.converter; + +import com.zimdugo.core.exception.BusinessException; +import com.zimdugo.core.exception.ErrorCode; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import java.util.Locale; +import org.springframework.core.convert.converter.Converter; + +public class LockerTypeRequestConverter implements Converter { + + @Override + public LockerFacilityFilterType convert(String source) { + String normalized = LockerFilterRequestValueNormalizer.normalize(source); + if (normalized == null || normalized.isBlank()) { + return null; + } + + try { + return LockerFacilityFilterType.valueOf(normalized.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.INVALID_PARAMETER_FORMAT); + } + } +} diff --git a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/place/PlaceLockerRequest.java b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/place/PlaceLockerRequest.java index 3db8b8e4..cd020727 100644 --- a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/place/PlaceLockerRequest.java +++ b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/place/PlaceLockerRequest.java @@ -1,6 +1,9 @@ package com.zimdugo.locker.entrypoint.dto.request.place; import com.zimdugo.locker.application.place.PlaceLockerQueryCommand; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.DecimalMax; import jakarta.validation.constraints.DecimalMin; @@ -21,10 +24,10 @@ public record PlaceLockerRequest( Double lng, @Schema(description = "보관함 크기 필터, 복수 선택 가능", example = "[\"SMALL\", \"MEDIUM\", \"LARGE\"]") - Set sizeTypes, + Set sizeTypes, @Schema(description = "실내/실외 필터, 복수 선택 가능", example = "[\"INDOOR\", \"OUTDOOR\"]") - Set indoorOutdoorTypes, + Set indoorOutdoorTypes, @Schema( description = "보관함 유형 필터 (MUSEUM, SUBWAY_STATION, DEPARTMENT_STORE, CONVENIENCE_STORE, " @@ -32,7 +35,7 @@ public record PlaceLockerRequest( example = "[\"MUSEUM\", \"SUBWAY_STATION\", \"DEPARTMENT_STORE\", \"CONVENIENCE_STORE\", " + "\"PUBLIC_OFFICE\", \"PRIVATE_LOCKER\", \"TRAIN_STATION\", \"ETC\"]" ) - Set lockerTypes + Set lockerTypes ) { public PlaceLockerQueryCommand toCommand(Long placeId) { diff --git a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequest.java b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequest.java index f97d69e0..4da4e010 100644 --- a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequest.java +++ b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequest.java @@ -1,6 +1,9 @@ package com.zimdugo.locker.entrypoint.dto.request.search; import com.zimdugo.locker.application.search.LockerSearchCommand; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.DecimalMax; import jakarta.validation.constraints.DecimalMin; @@ -28,10 +31,10 @@ public record LockerSearchRequest( String keyword, @Schema(description = "보관함 크기 필터, 복수 선택 가능", example = "[\"SMALL\", \"MEDIUM\", \"LARGE\"]") - Set sizeTypes, + Set sizeTypes, @Schema(description = "실내/실외 필터, 복수 선택 가능", example = "[\"INDOOR\", \"OUTDOOR\"]") - Set indoorOutdoorTypes, + Set indoorOutdoorTypes, @Schema( description = "보관함 유형 필터 (MUSEUM, SUBWAY_STATION, DEPARTMENT_STORE, CONVENIENCE_STORE, " @@ -39,7 +42,7 @@ public record LockerSearchRequest( example = "[\"MUSEUM\", \"SUBWAY_STATION\", \"DEPARTMENT_STORE\", \"CONVENIENCE_STORE\", " + "\"PUBLIC_OFFICE\", \"PRIVATE_LOCKER\", \"TRAIN_STATION\", \"ETC\"]" ) - Set lockerTypes + Set lockerTypes ) { public LockerSearchCommand toCommand() { diff --git a/src/test/java/com/zimdugo/locker/application/pin/LockerPinQueryServiceTest.java b/src/test/java/com/zimdugo/locker/application/pin/LockerPinQueryServiceTest.java index 51247545..d26d654d 100644 --- a/src/test/java/com/zimdugo/locker/application/pin/LockerPinQueryServiceTest.java +++ b/src/test/java/com/zimdugo/locker/application/pin/LockerPinQueryServiceTest.java @@ -2,12 +2,18 @@ import com.zimdugo.core.exception.BusinessException; import com.zimdugo.core.exception.ErrorCode; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import com.zimdugo.locker.application.search.LockerSearchResultQueryService; import com.zimdugo.locker.application.result.LockerItemType; import com.zimdugo.locker.application.result.search.LockerSearchItemResult; import com.zimdugo.locker.application.result.pin.LockerPinItemResult; import com.zimdugo.locker.application.result.pin.LockerPinResult; import com.zimdugo.locker.domain.favorite.FavoriteLockerReader; +import com.zimdugo.locker.domain.locker.IndoorOutdoorType; +import com.zimdugo.locker.domain.locker.LockerSizeType; +import com.zimdugo.locker.domain.locker.LockerType; import com.zimdugo.locker.domain.locker.NearbyLocker; import com.zimdugo.locker.domain.locker.NearbyLockerPlaceReader; import com.zimdugo.locker.domain.search.LockerSearchFilter; @@ -30,6 +36,18 @@ @ExtendWith(MockitoExtension.class) class LockerPinQueryServiceTest { + private static final LockerSearchFilter SUBWAY_INDOOR_SMALL_FILTER = new LockerSearchFilter( + Set.of(LockerSizeType.SMALL), + Set.of(IndoorOutdoorType.INDOOR), + Set.of(LockerType.SUBWAY_STATION) + ); + + private static final LockerSearchFilter SUBWAY_INDOOR_LARGE_FILTER = new LockerSearchFilter( + Set.of(LockerSizeType.LARGE), + Set.of(IndoorOutdoorType.INDOOR), + Set.of(LockerType.SUBWAY_STATION) + ); + private static final LockerPinQuery DETAIL_QUERY = new LockerPinQuery( 37.54, 126.92, @@ -153,9 +171,9 @@ void clustersKeywordPinsWithinBounds() { 37.55, 126.93, "station", - Set.of("SMALL"), - Set.of("INDOOR"), - Set.of("SUBWAY_STATION") + Set.of(LockerSizeFilterType.SMALL), + Set.of(IndoorOutdoorFilterType.INDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION) ); List items = List.of(sampleKeywordLockerItem()); List assembledPins = List.of( @@ -171,7 +189,7 @@ void clustersKeywordPinsWithinBounds() { 37.55, 126.93, "station", - LockerSearchFilter.from(Set.of("SMALL"), Set.of("INDOOR"), Set.of("SUBWAY_STATION")) + SUBWAY_INDOOR_SMALL_FILTER )).willReturn(items); given(lockerSearchPinAssembler.assemble(items)).willReturn(assembledPins); given(lockerPinClusterer.cluster(List.of(assembledPins.get(0)), 13.0)).willReturn(clusteredPins); @@ -185,7 +203,7 @@ void clustersKeywordPinsWithinBounds() { 126.92, 37.56, 126.94, - LockerSearchFilter.from(Set.of("SMALL"), Set.of("INDOOR"), Set.of("SUBWAY_STATION")) + SUBWAY_INDOOR_SMALL_FILTER ); verify(lockerSearchPinAssembler).assemble(items); verify(lockerPinClusterer).cluster(List.of(assembledPins.get(0)), 13.0); @@ -203,19 +221,15 @@ void appliesFilterToBoundsPins() { null, null, null, - Set.of("LARGE"), - Set.of("INDOOR"), - Set.of("SUBWAY_STATION") + Set.of(LockerSizeFilterType.LARGE), + Set.of(IndoorOutdoorFilterType.INDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION) ); List nearbyLockers = List.of(sampleLocker()); List pins = List.of( LockerPinItemResult.locker(1L, 37.55, 126.93, false) ); - LockerSearchFilter filter = LockerSearchFilter.from( - Set.of("LARGE"), - Set.of("INDOOR"), - Set.of("SUBWAY_STATION") - ); + LockerSearchFilter filter = SUBWAY_INDOOR_LARGE_FILTER; given(nearbyLockerPlaceReader.findWithinBounds(37.54, 126.92, 37.56, 126.94, filter)) .willReturn(nearbyLockers); diff --git a/src/test/java/com/zimdugo/locker/application/place/PlaceLockerQueryServiceTest.java b/src/test/java/com/zimdugo/locker/application/place/PlaceLockerQueryServiceTest.java index b3411b00..9b3a6c95 100644 --- a/src/test/java/com/zimdugo/locker/application/place/PlaceLockerQueryServiceTest.java +++ b/src/test/java/com/zimdugo/locker/application/place/PlaceLockerQueryServiceTest.java @@ -4,6 +4,9 @@ import com.zimdugo.common.i18n.SupportedLanguage; import com.zimdugo.core.exception.BusinessException; import com.zimdugo.core.exception.ErrorCode; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import com.zimdugo.locker.application.result.place.PlaceLockerResult; import com.zimdugo.locker.domain.locker.IndoorOutdoorType; import com.zimdugo.locker.domain.place.LockerPlace; @@ -117,9 +120,9 @@ private PlaceLockerQueryCommand command() { 101L, 37.55, 126.93, - Set.of("LARGE"), - Set.of("INDOOR"), - Set.of("SUBWAY_STATION") + Set.of(LockerSizeFilterType.LARGE), + Set.of(IndoorOutdoorFilterType.INDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION) ); } diff --git a/src/test/java/com/zimdugo/locker/application/search/LockerSearchResultQueryServiceTest.java b/src/test/java/com/zimdugo/locker/application/search/LockerSearchResultQueryServiceTest.java index b526844f..ef909c87 100644 --- a/src/test/java/com/zimdugo/locker/application/search/LockerSearchResultQueryServiceTest.java +++ b/src/test/java/com/zimdugo/locker/application/search/LockerSearchResultQueryServiceTest.java @@ -2,6 +2,9 @@ import com.zimdugo.common.i18n.CurrentRequestLanguage; import com.zimdugo.common.i18n.SupportedLanguage; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; import com.zimdugo.locker.application.pin.LockerSearchPinAssembler; import com.zimdugo.locker.application.pin.LockerPinClusterer; import com.zimdugo.locker.application.result.search.LockerSearchItemResult; @@ -101,9 +104,9 @@ void convertsCommandFilters() { 37.55, 126.93, "신촌", - Set.of("SMALL", "LARGE"), - Set.of("INDOOR"), - Set.of("SUBWAY_STATION"), + Set.of(LockerSizeFilterType.SMALL, LockerSizeFilterType.LARGE), + Set.of(IndoorOutdoorFilterType.INDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION), null ); @@ -212,9 +215,9 @@ void excludesPlaceWithoutFilteredLockers() { 37.55, 126.93, "신촌", - Set.of("LARGE"), - Set.of("OUTDOOR"), - Set.of("SUBWAY_STATION"), + Set.of(LockerSizeFilterType.LARGE), + Set.of(IndoorOutdoorFilterType.OUTDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION), null ) ); diff --git a/src/test/java/com/zimdugo/locker/domain/search/LockerSearchFilterTest.java b/src/test/java/com/zimdugo/locker/domain/search/LockerSearchFilterTest.java index 1d1acbe7..4ff086ca 100644 --- a/src/test/java/com/zimdugo/locker/domain/search/LockerSearchFilterTest.java +++ b/src/test/java/com/zimdugo/locker/domain/search/LockerSearchFilterTest.java @@ -3,13 +3,12 @@ import com.zimdugo.locker.domain.locker.IndoorOutdoorType; import com.zimdugo.locker.domain.locker.LockerSizeType; import com.zimdugo.locker.domain.locker.LockerType; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class LockerSearchFilterTest { @@ -54,46 +53,27 @@ void matchesAllFilterTypes() { } @Test - @DisplayName("API 문자열 필터를 도메인 enum으로 변환한다") - void parsesApiFilterValues() { - LockerSearchFilter filter = LockerSearchFilter.from( - Set.of("SMALL,LARGE"), - Set.of("indoor"), - Set.of("subway_station") - ); - - assertThat(filter.sizeTypes()).containsExactlyInAnyOrder(LockerSizeType.SMALL, LockerSizeType.LARGE); - assertThat(filter.indoorOutdoorTypes()).containsExactly(IndoorOutdoorType.INDOOR); - assertThat(filter.lockerTypes()).containsExactly(LockerType.SUBWAY_STATION); + @DisplayName("생성자는 null 필터를 빈 집합으로 정규화한다") + void normalizesNullFiltersToEmptySets() { + LockerSearchFilter filter = new LockerSearchFilter(null, null, null); + + assertThat(filter.sizeTypes()).isEmpty(); + assertThat(filter.indoorOutdoorTypes()).isEmpty(); + assertThat(filter.lockerTypes()).isEmpty(); + assertThat(filter.isEmpty()).isTrue(); } @Test - @DisplayName("대괄호와 큰따옴표가 포함된 JSON 배열 형식의 문자열 필터도 안전하게 도메인 enum으로 변환한다") - void parsesJsonArrayStyleFilterValues() { - LockerSearchFilter filter = LockerSearchFilter.from( - Set.of("[\"SMALL\", \"LARGE\"]"), - Set.of("[\"INDOOR\"]"), - Set.of("[\"SUBWAY_STATION\"]") + @DisplayName("생성된 필터 집합은 외부에서 수정할 수 없다") + void exposesUnmodifiableSets() { + LockerSearchFilter filter = new LockerSearchFilter( + Set.of(LockerSizeType.SMALL), + Set.of(IndoorOutdoorType.INDOOR), + Set.of(LockerType.ETC) ); - assertThat(filter.sizeTypes()).containsExactlyInAnyOrder(LockerSizeType.SMALL, LockerSizeType.LARGE); - assertThat(filter.indoorOutdoorTypes()).containsExactly(IndoorOutdoorType.INDOOR); - assertThat(filter.lockerTypes()).containsExactly(LockerType.SUBWAY_STATION); - } - - @Test - @DisplayName("필터 파싱은 표준 출력에 디버그 로그를 쓰지 않는다") - void parsingDoesNotWriteToStandardOutput() { - PrintStream originalOut = System.out; - ByteArrayOutputStream output = new ByteArrayOutputStream(); - System.setOut(new PrintStream(output)); - try { - LockerSearchFilter.from(Set.of("SMALL"), Set.of("INDOOR"), Set.of("ETC")); - } finally { - System.setOut(originalOut); - } - - assertThat(output.toString()).isBlank(); + assertThatThrownBy(() -> filter.sizeTypes().add(LockerSizeType.LARGE)) + .isInstanceOf(UnsupportedOperationException.class); } @Test diff --git a/src/test/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestConverterTest.java b/src/test/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestConverterTest.java new file mode 100644 index 00000000..04b96ac6 --- /dev/null +++ b/src/test/java/com/zimdugo/locker/entrypoint/converter/LockerFilterRequestConverterTest.java @@ -0,0 +1,50 @@ +package com.zimdugo.locker.entrypoint.converter; + +import com.zimdugo.core.exception.BusinessException; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LockerFilterRequestConverterTest { + + private final LockerSizeTypeRequestConverter lockerSizeTypeRequestConverter = + new LockerSizeTypeRequestConverter(); + private final IndoorOutdoorTypeRequestConverter indoorOutdoorTypeRequestConverter = + new IndoorOutdoorTypeRequestConverter(); + private final LockerTypeRequestConverter lockerTypeRequestConverter = + new LockerTypeRequestConverter(); + + @Test + @DisplayName("보관함 크기 컨버터는 소문자와 JSON 배열 토큰을 정규화한다") + void convertLockerSizeType() { + assertThat(lockerSizeTypeRequestConverter.convert("\"small\"")).isEqualTo(LockerSizeFilterType.SMALL); + assertThat(lockerSizeTypeRequestConverter.convert("[LARGE]")).isEqualTo(LockerSizeFilterType.LARGE); + } + + @Test + @DisplayName("실내외 컨버터는 대소문자와 공백을 정규화한다") + void convertIndoorOutdoorType() { + assertThat(indoorOutdoorTypeRequestConverter.convert(" indoor ")).isEqualTo(IndoorOutdoorFilterType.INDOOR); + assertThat(indoorOutdoorTypeRequestConverter.convert("\"OUTDOOR\"")) + .isEqualTo(IndoorOutdoorFilterType.OUTDOOR); + } + + @Test + @DisplayName("보관함 유형 컨버터는 유효하지 않은 입력을 예외로 처리한다") + void invalidLockerTypeThrowsException() { + assertThatThrownBy(() -> lockerTypeRequestConverter.convert("unknown")) + .isInstanceOf(BusinessException.class); + } + + @Test + @DisplayName("보관함 유형 컨버터는 JSON 배열 토큰을 정규화한다") + void convertLockerType() { + assertThat(lockerTypeRequestConverter.convert("[\"subway_station\"]")) + .isEqualTo(LockerFacilityFilterType.SUBWAY_STATION); + } +} diff --git a/src/test/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequestTest.java b/src/test/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequestTest.java new file mode 100644 index 00000000..fd49bd1f --- /dev/null +++ b/src/test/java/com/zimdugo/locker/entrypoint/dto/request/search/LockerSearchRequestTest.java @@ -0,0 +1,36 @@ +package com.zimdugo.locker.entrypoint.dto.request.search; + +import com.zimdugo.locker.application.search.LockerSearchCommand; +import com.zimdugo.locker.application.filter.IndoorOutdoorFilterType; +import com.zimdugo.locker.application.filter.LockerFacilityFilterType; +import com.zimdugo.locker.application.filter.LockerSizeFilterType; +import java.util.Set; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LockerSearchRequestTest { + + @Test + @DisplayName("검색 요청은 enum 필터를 유지한 command로 변환한다") + void toCommandPreservesTypedFilters() { + LockerSearchRequest request = new LockerSearchRequest( + 37.55, + 126.93, + "신촌", + Set.of(LockerSizeFilterType.SMALL, LockerSizeFilterType.LARGE), + Set.of(IndoorOutdoorFilterType.INDOOR), + Set.of(LockerFacilityFilterType.SUBWAY_STATION) + ); + + LockerSearchCommand command = request.toCommand(); + + assertThat(command.sizeTypes()).containsExactlyInAnyOrder( + LockerSizeFilterType.SMALL, + LockerSizeFilterType.LARGE + ); + assertThat(command.indoorOutdoorTypes()).containsExactly(IndoorOutdoorFilterType.INDOOR); + assertThat(command.lockerTypes()).containsExactly(LockerFacilityFilterType.SUBWAY_STATION); + } +} From 8e8a967521272797cf0dcc116baf6a10e8e7472f Mon Sep 17 00:00:00 2001 From: mike7643 Date: Mon, 29 Jun 2026 14:16:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=ED=95=80=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/pin/LockerPinRequest.java | 6 ++ .../dto/request/pin/LockerPinRequestTest.java | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/test/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequestTest.java diff --git a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java index ab98ca72..126bad7f 100644 --- a/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java +++ b/src/main/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequest.java @@ -6,6 +6,7 @@ import com.zimdugo.locker.application.filter.LockerSizeFilterType; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.DecimalMax; import jakarta.validation.constraints.DecimalMin; import java.util.Set; @@ -65,6 +66,11 @@ public record LockerPinRequest( @Parameter(description = "보관함 유형 필터") Set lockerTypes ) { + @AssertTrue(message = "validation.user_location_required") + public boolean hasUserLocation() { + return lat != null && lng != null; + } + public LockerPinQuery toQuery() { return new LockerPinQuery( swLat, diff --git a/src/test/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequestTest.java b/src/test/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequestTest.java new file mode 100644 index 00000000..af2e5e9c --- /dev/null +++ b/src/test/java/com/zimdugo/locker/entrypoint/dto/request/pin/LockerPinRequestTest.java @@ -0,0 +1,69 @@ +package com.zimdugo.locker.entrypoint.dto.request.pin; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LockerPinRequestTest { + + @Test + @DisplayName("사용자 기준 좌표는 키워드와 무관하게 항상 필요하다") + void rejectsMissingLocationEvenWithoutKeyword() { + LockerPinRequest request = new LockerPinRequest( + 37.54, + 126.92, + 37.56, + 126.94, + 13.0, + null, + null, + null, + null, + null, + null + ); + + assertThat(request.hasUserLocation()).isFalse(); + } + + @Test + @DisplayName("키워드 검색이면 기준 좌표가 없을 때도 유효하지 않다") + void rejectsMissingKeywordLocation() { + LockerPinRequest request = new LockerPinRequest( + 37.54, + 126.92, + 37.56, + 126.94, + 13.0, + null, + null, + "station", + null, + null, + null + ); + + assertThat(request.hasUserLocation()).isFalse(); + } + + @Test + @DisplayName("사용자 기준 좌표가 모두 있으면 유효하다") + void acceptsCompleteLocation() { + LockerPinRequest request = new LockerPinRequest( + 37.54, + 126.92, + 37.56, + 126.94, + 13.0, + 37.55, + 126.93, + "station", + null, + null, + null + ); + + assertThat(request.hasUserLocation()).isTrue(); + } +}