Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.typenull.pingdom.place.api.dto.conversion;

import static org.assertj.core.api.Assertions.assertThat;

import com.typenull.pingdom.place.domain.conversion.MapLinkConversionType;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

class MapLinkConversionRequestTest {

private static Validator validator;

@BeforeAll
static void setUpValidator() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}

@Test
void NAVER_provider와_필수_입력이_있으면_검증을_통과한다() {
Set<String> invalidFields = invalidFields(new MapLinkConversionRequest(
MapLinkConversionType.DIRECTIONS, "NAVER", "request-1"));

assertThat(invalidFields).isEmpty();
}

@Test
void provider와_linkType과_requestId의_필수값과_길이를_검증한다() {
Set<String> invalidFields = invalidFields(new MapLinkConversionRequest(
null, " ", " "));
Set<String> longProviderInvalidFields = invalidFields(new MapLinkConversionRequest(
MapLinkConversionType.DIRECTIONS, "N".repeat(31), "request-1"));

assertThat(invalidFields).containsExactlyInAnyOrder("linkType", "provider", "requestId");
assertThat(longProviderInvalidFields).containsExactly("provider");
}

private Set<String> invalidFields(MapLinkConversionRequest request) {
return validator.validate(request).stream()
.map(violation -> violation.getPropertyPath().toString())
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.typenull.pingdom.place.application.service.localhot;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

import com.typenull.pingdom.place.domain.place.core.MapPlace;
Expand All @@ -12,6 +14,8 @@
import com.typenull.pingdom.place.domain.place.region.ResolvedPlaceAdministrativeRegion;
import com.typenull.pingdom.place.infrastructure.persistence.place.MapPlaceRepository;
import com.typenull.pingdom.place.infrastructure.persistence.place.PlaceAdministrativeRegionRepository;
import com.typenull.pingdom.shared.exception.MapErrorCode;
import com.typenull.pingdom.shared.exception.MapException;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
Expand Down Expand Up @@ -73,6 +77,56 @@ class PlaceAdministrativeRegionServiceTest {
verify(place).updateAdministrativeRegion("11680");
}

@Test
void Resolver가_비활성화되면_지역과_장소를_변경하지_않는다() {
PlaceAdministrativeRegionResolver resolver = mock(PlaceAdministrativeRegionResolver.class);
PlaceAdministrativeRegionRepository regionRepository = mock(PlaceAdministrativeRegionRepository.class);
MapPlaceRepository placeRepository = mock(MapPlaceRepository.class);
MapPlace place = mock(MapPlace.class);
when(resolver.isConfigured()).thenReturn(false);

boolean synchronizedRegion = service(resolver, regionRepository, placeRepository).synchronizeIfConfigured(place);

assertThat(synchronizedRegion).isFalse();
verifyNoInteractions(regionRepository, placeRepository, place);
}

@Test
void Resolver_조회_실패시_지역과_장소를_부분_저장하지_않는다() {
PlaceAdministrativeRegionResolver resolver = mock(PlaceAdministrativeRegionResolver.class);
PlaceAdministrativeRegionRepository regionRepository = mock(PlaceAdministrativeRegionRepository.class);
MapPlaceRepository placeRepository = mock(MapPlaceRepository.class);
MapPlace place = mock(MapPlace.class);
when(place.getLatitude()).thenReturn(37.5172d);
when(place.getLongitude()).thenReturn(127.0473d);
MapException resolutionFailure = new MapException(MapErrorCode.LOCAL_HOT_REGION_RESOLUTION_FAILED);
when(resolver.resolve(37.5172d, 127.0473d)).thenThrow(resolutionFailure);

assertThatThrownBy(() -> service(resolver, regionRepository, placeRepository).synchronize(place))
.isSameAs(resolutionFailure);

verifyNoInteractions(regionRepository, placeRepository);
verify(place, org.mockito.Mockito.never()).updateAdministrativeRegion(org.mockito.ArgumentMatchers.anyString());
}

@Test
void 지역을_갱신할_장소가_없으면_Resolver를_호출하지_않는다() {
PlaceAdministrativeRegionResolver resolver = mock(PlaceAdministrativeRegionResolver.class);
PlaceAdministrativeRegionRepository regionRepository = mock(PlaceAdministrativeRegionRepository.class);
MapPlaceRepository placeRepository = mock(MapPlaceRepository.class);
when(resolver.isConfigured()).thenReturn(true);
when(placeRepository.findByIdForUpdate(10L)).thenReturn(Optional.empty());

assertThatThrownBy(() -> service(resolver, regionRepository, placeRepository).synchronizeByIdIfConfigured(10L))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("placeId=10");

verifyNoInteractions(regionRepository);
verify(resolver).isConfigured();
verify(resolver, org.mockito.Mockito.never()).resolve(org.mockito.ArgumentMatchers.anyDouble(),
org.mockito.ArgumentMatchers.anyDouble());
}

private PlaceAdministrativeRegionService service(
PlaceAdministrativeRegionResolver resolver,
PlaceAdministrativeRegionRepository regionRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.typenull.pingdom.place.application.service.localhot;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
Expand All @@ -15,6 +16,8 @@
import com.typenull.pingdom.place.domain.place.region.ResolvedPlaceAdministrativeRegion;
import com.typenull.pingdom.place.infrastructure.persistence.place.PlaceAdministrativeRegionRepository;
import com.typenull.pingdom.place.infrastructure.persistence.place.PlaceLocalHotQueryRepository;
import com.typenull.pingdom.shared.exception.MapErrorCode;
import com.typenull.pingdom.shared.exception.MapException;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -104,6 +107,22 @@ class PlaceLocalHotQueryServiceTest {
verifyNoInteractions(regionResolver);
}

@Test
void 좌표_조회에서_Resolver_실패는_조회_저장소를_호출하지_않고_전파한다() {
PlaceAdministrativeRegionResolver regionResolver = mock(PlaceAdministrativeRegionResolver.class);
PlaceAdministrativeRegionRepository regionRepository = mock(PlaceAdministrativeRegionRepository.class);
PlaceLocalHotQueryRepository queryRepository = mock(PlaceLocalHotQueryRepository.class);
MapException resolutionFailure = new MapException(MapErrorCode.LOCAL_HOT_REGION_RESOLUTION_FAILED);
when(regionResolver.resolve(37.5172d, 127.0473d)).thenThrow(resolutionFailure);
PlaceLocalHotQueryService service = new PlaceLocalHotQueryService(
regionResolver, regionRepository, queryRepository);

assertThatThrownBy(() -> service.find(new PlaceLocalHotQuery(37.5172d, 127.0473d, null, 1, 20), 7L))
.isSameAs(resolutionFailure);

verifyNoInteractions(regionRepository, queryRepository);
}

private PlaceLocalHotQueryRepository.PlaceLocalHotProjection projection(
Long placeId,
long bookmarkCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,46 @@ void setUp() {
server.verify();
}

@Test
void 세종이_아닌_빈_area2_응답은_지역없음으로_처리한다() {
server.expect(requestTo(REQUEST_URL))
.andRespond(withSuccess(successResponse("1168010100", "서울특별시", ""), MediaType.APPLICATION_JSON));

assertFailure(resolver(true, "test-client-id", "test-client-secret"),
MapErrorCode.LOCAL_HOT_REGION_NOT_FOUND, HttpStatus.NOT_FOUND);
server.verify();
}

@Test
void legalcode_코드가_불완전하면_지역없음으로_처리한다() {
server.expect(requestTo(REQUEST_URL))
.andRespond(withSuccess("""
{"status":{"code":0},"results":[{
"name":"legalcode",
"code":{"id":"361101070"},
"region":{"area1":{"name":"세종특별자치시"},"area2":{"name":""}}
}]}""", MediaType.APPLICATION_JSON));

assertFailure(resolver(true, "test-client-id", "test-client-secret"),
MapErrorCode.LOCAL_HOT_REGION_NOT_FOUND, HttpStatus.NOT_FOUND);
server.verify();
}

@Test
void legalcode_결과가_없으면_지역없음으로_처리한다() {
server.expect(requestTo(REQUEST_URL))
.andRespond(withSuccess("""
{"status":{"code":0},"results":[{
"name":"addr",
"code":{"id":"1168010100"},
"region":{"area1":{"name":"서울특별시"},"area2":{"name":"강남구"}}
}]}""", MediaType.APPLICATION_JSON));

assertFailure(resolver(true, "test-client-id", "test-client-secret"),
MapErrorCode.LOCAL_HOT_REGION_NOT_FOUND, HttpStatus.NOT_FOUND);
server.verify();
}

@Test
void 네이버_내부_상태가_결과없음이면_지역없음으로_처리한다() {
server.expect(requestTo(REQUEST_URL))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.typenull.pingdom.place.infrastructure.localhot;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.typenull.pingdom.place.application.service.localhot.PlaceAdministrativeRegionService;
import com.typenull.pingdom.place.domain.place.core.MapPlace;
import com.typenull.pingdom.place.infrastructure.persistence.place.MapPlaceRepository;
import com.typenull.pingdom.shared.exception.MapErrorCode;
import com.typenull.pingdom.shared.exception.MapException;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.data.domain.Pageable;

class PlaceAdministrativeRegionBackfillRunnerTest {

@Test
void regionCode가_없는_장소만_설정된_batchSize만큼_조회한다() {
MapPlaceRepository placeRepository = mock(MapPlaceRepository.class);
PlaceAdministrativeRegionService regionService = mock(PlaceAdministrativeRegionService.class);
MapPlace place = place(10L);
when(placeRepository.findByRegionCodeIsNullOrderByIdAsc(org.mockito.ArgumentMatchers.any(Pageable.class)))
.thenReturn(List.of(place));
when(regionService.synchronizeByIdIfConfigured(10L)).thenReturn(false);
PlaceAdministrativeRegionBackfillRunner runner = runner(2, placeRepository, regionService);

runner.run(mock(ApplicationArguments.class));

ArgumentCaptor<Pageable> pageableCaptor = ArgumentCaptor.forClass(Pageable.class);
verify(placeRepository).findByRegionCodeIsNullOrderByIdAsc(pageableCaptor.capture());
assertThat(pageableCaptor.getValue().getPageNumber()).isZero();
assertThat(pageableCaptor.getValue().getPageSize()).isEqualTo(2);
verify(regionService).synchronizeByIdIfConfigured(10L);
}

@Test
void 한_장소의_지역_조회가_실패해도_다음_장소를_계속_처리한다() {
MapPlaceRepository placeRepository = mock(MapPlaceRepository.class);
PlaceAdministrativeRegionService regionService = mock(PlaceAdministrativeRegionService.class);
MapPlace failedPlace = place(10L);
MapPlace succeededPlace = place(11L);
when(placeRepository.findByRegionCodeIsNullOrderByIdAsc(org.mockito.ArgumentMatchers.any(Pageable.class)))
.thenReturn(List.of(failedPlace, succeededPlace));
doThrow(new MapException(MapErrorCode.LOCAL_HOT_REGION_NOT_FOUND))
.when(regionService).synchronizeByIdIfConfigured(10L);
when(regionService.synchronizeByIdIfConfigured(11L)).thenReturn(true);

runner(10, placeRepository, regionService).run(mock(ApplicationArguments.class));

verify(regionService).synchronizeByIdIfConfigured(10L);
verify(regionService).synchronizeByIdIfConfigured(11L);
}

private PlaceAdministrativeRegionBackfillRunner runner(
int batchSize,
MapPlaceRepository placeRepository,
PlaceAdministrativeRegionService regionService
) {
return new PlaceAdministrativeRegionBackfillRunner(
new PlaceAdministrativeRegionBackfillProperties(true, batchSize),
placeRepository,
regionService
);
}

private MapPlace place(long id) {
MapPlace place = mock(MapPlace.class);
when(place.getId()).thenReturn(id);
return place;
}
}
Loading