Skip to content

Commit c8e0d7e

Browse files
authored
feat: 주문 가능 상점 전환(주문 서비스 가입) POST API 검증로직 추가 및 테스트 추가 (#2048)
* feat: 검증 추가 * feat: 주문가능상점변환 테스트 추가 * refact: 매개변수 stirng -> ShopToOrderableRequestStatus로 변경 * refact: 예외 명 변경
1 parent e38f8a4 commit c8e0d7e

File tree

5 files changed

+199
-2
lines changed

5 files changed

+199
-2
lines changed

src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package in.koreatech.koin.domain.shoptoOrderable.repository;
22

33
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable;
4+
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus;
45

56
import org.springframework.data.repository.Repository;
67

@@ -13,4 +14,6 @@ public interface ShopToOrderableRepository extends Repository<ShopToOrderable, I
1314
Optional<ShopToOrderable> findByShopId(Integer shopId);
1415

1516
boolean existsByShopId(Integer shopId);
17+
18+
boolean existsByShopIdAndRequestStatus(Integer shopId, ShopToOrderableRequestStatus requestStatus);
1619
}

src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import in.koreatech.koin.domain.shop.repository.shop.ShopRepository;
88
import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest;
99
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable;
10+
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus;
1011
import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository;
1112
import in.koreatech.koin.global.code.ApiResponseCode;
1213
import lombok.RequiredArgsConstructor;
@@ -27,12 +28,18 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque
2728

2829
// 이미 신청한 내역이 있는지 확인
2930
if (shopToOrderableRepository.existsByShopId(shopId)) {
30-
throw CustomException.of(ApiResponseCode.ALREADY_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId);
31+
throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId);
3132
}
3233

3334
// 가게 사장님인지 확인
35+
if (!shop.getOwner().getId().equals(ownerId)) {
36+
throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, "ownerId: " + ownerId + ", shopId: " + shopId);
37+
}
3438

3539
// 이미 주문가능 상점인지 확인
40+
if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.APPROVED)) {
41+
throw CustomException.of(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP, "shopId: " + shopId);
42+
}
3643

3744
ShopToOrderable shopToOrderable = ShopToOrderable.builder()
3845
.shop(shop)

src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public enum ApiResponseCode {
8585
FORBIDDEN_BLOCKED_USER(HttpStatus.FORBIDDEN, "차단된 사용자입니다."),
8686
PAYMENT_ACCESS_DENIED(HttpStatus.FORBIDDEN, "결제 정보 접근 권한이 없습니다."),
8787
FORBIDDEN_ORDER(HttpStatus.FORBIDDEN, "주문 정보 접근 권한이 없습니다."),
88+
FORBIDDEN_SHOP_OWNER(HttpStatus.FORBIDDEN, "상점의 사장님이 아닙니다."),
8889

8990
/**
9091
* 404 Not Found (리소스를 찾을 수 없음)
@@ -120,7 +121,8 @@ public enum ApiResponseCode {
120121
REQUEST_TOO_FAST(HttpStatus.CONFLICT, "요청이 너무 빠릅니다. 다시 요청해주세요."),
121122
OPTIMISTIC_LOCKING_FAILURE(HttpStatus.CONFLICT, "이미 처리된 요청입니다."),
122123
DUPLICATE_CLUB_RECRUITMENT(HttpStatus.CONFLICT, "동아리 공고가 이미 존재합니다."),
123-
ALREADY_REQUESTED_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 전환 신청이 접수된 상점입니다."),
124+
DUPLICATE_REQUESTED_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 전환 신청이 접수된 상점입니다."),
125+
DUPLICATE_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 주문 가능한 상점입니다."),
124126

125127
/**
126128
* 429 Too Many Requests (요청량 초과)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package in.koreatech.koin.unit.domain.ShopToOrderable;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.Mockito.verify;
7+
import static org.mockito.Mockito.when;
8+
9+
import java.util.Optional;
10+
11+
import org.junit.jupiter.api.BeforeEach;
12+
import org.junit.jupiter.api.DisplayName;
13+
import org.junit.jupiter.api.Nested;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.mockito.ArgumentCaptor;
17+
import org.mockito.InjectMocks;
18+
import org.mockito.Mock;
19+
import org.mockito.junit.jupiter.MockitoExtension;
20+
21+
import in.koreatech.koin.domain.shop.model.shop.Shop;
22+
import in.koreatech.koin.domain.shop.repository.shop.ShopRepository;
23+
import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest;
24+
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable;
25+
import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus;
26+
import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository;
27+
import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService;
28+
import in.koreatech.koin.domain.owner.model.Owner;
29+
import in.koreatech.koin.domain.user.model.User;
30+
import in.koreatech.koin.global.code.ApiResponseCode;
31+
import in.koreatech.koin.global.exception.CustomException;
32+
import in.koreatech.koin.unit.fixture.OwnerFixture;
33+
import in.koreatech.koin.unit.fixture.ShopFixture;
34+
35+
import org.springframework.test.util.ReflectionTestUtils;
36+
37+
@ExtendWith(MockitoExtension.class)
38+
class ShopToOrderableServiceTest {
39+
40+
@InjectMocks
41+
private ShopToOrderableService shopToOrderableService;
42+
43+
@Mock
44+
private ShopToOrderableRepository shopToOrderableRepository;
45+
46+
@Mock
47+
private ShopRepository shopRepository;
48+
49+
private Owner owner;
50+
private User user;
51+
private Shop shop;
52+
private ShopToOrderableRequest request;
53+
54+
@BeforeEach
55+
void setUp() {
56+
owner = OwnerFixture.성빈_사장님();
57+
ReflectionTestUtils.setField(owner, "id", 1);
58+
shop = ShopFixture.주문전환_이전_상점(owner);
59+
60+
request = new ShopToOrderableRequest(
61+
5000,
62+
true,
63+
"BOTH",
64+
1000,
65+
2000,
66+
true,
67+
"https://example.com/business_license.jpg",
68+
"https://example.com/business_certificate.jpg",
69+
"https://example.com/bank_copy.jpg",
70+
"국민은행",
71+
"123-456-789"
72+
);
73+
}
74+
75+
@Nested
76+
@DisplayName("주문 가능 상점 신청 생성 테스트")
77+
class CreateOrderableRequestTest {
78+
79+
@Test
80+
@DisplayName("정상적으로 주문 가능 상점 신청이 생성된다")
81+
void createOrderableRequestSuccessfully() {
82+
// given
83+
when(shopRepository.findById(100)).thenReturn(Optional.of(shop));
84+
when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false);
85+
when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(false);
86+
when(shopToOrderableRepository.save(any(ShopToOrderable.class))).thenAnswer(
87+
invocation -> invocation.getArgument(0));
88+
89+
// when
90+
shopToOrderableService.createOrderableRequest(1, request, 100);
91+
92+
// then
93+
ArgumentCaptor<ShopToOrderable> captor = ArgumentCaptor.forClass(ShopToOrderable.class);
94+
verify(shopToOrderableRepository).save(captor.capture());
95+
96+
ShopToOrderable saved = captor.getValue();
97+
assertThat(saved.getShop()).isEqualTo(shop);
98+
assertThat(saved.getMinimumOrderAmount()).isEqualTo(5000);
99+
assertThat(saved.getTakeout()).isTrue();
100+
assertThat(saved.getDeliveryOption()).isEqualTo("BOTH");
101+
assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000);
102+
assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000);
103+
assertThat(saved.getIsOpen()).isTrue();
104+
assertThat(saved.getBank()).isEqualTo("국민은행");
105+
assertThat(saved.getAccountNumber()).isEqualTo("123-456-789");
106+
}
107+
108+
@Test
109+
@DisplayName("존재하지 않는 상점 ID로 신청하면 예외가 발생한다")
110+
void throwExceptionWhenShopNotFound() {
111+
// given
112+
when(shopRepository.findById(999)).thenReturn(Optional.empty());
113+
114+
// when & then
115+
CustomException exception = assertThrows(CustomException.class,
116+
() -> shopToOrderableService.createOrderableRequest(1, request, 999));
117+
118+
assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.NOT_FOUND_SHOP);
119+
}
120+
121+
@Test
122+
@DisplayName("이미 신청한 내역이 있으면 예외가 발생한다")
123+
void throwExceptionWhenAlreadyRequested() {
124+
// given
125+
when(shopRepository.findById(100)).thenReturn(Optional.of(shop));
126+
when(shopToOrderableRepository.existsByShopId(100)).thenReturn(true);
127+
128+
// when & then
129+
CustomException exception = assertThrows(CustomException.class,
130+
() -> shopToOrderableService.createOrderableRequest(1, request, 100));
131+
132+
assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP);
133+
}
134+
135+
@Test
136+
@DisplayName("상점 사장님이 아닌 경우 예외가 발생한다")
137+
void throwExceptionWhenNotShopOwner() {
138+
// given
139+
when(shopRepository.findById(100)).thenReturn(Optional.of(shop));
140+
141+
// when & then
142+
CustomException exception = assertThrows(CustomException.class,
143+
() -> shopToOrderableService.createOrderableRequest(2, request, 100));
144+
assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.FORBIDDEN_SHOP_OWNER);
145+
}
146+
147+
@Test
148+
@DisplayName("이미 승인된 상점이 있으면 예외가 발생한다")
149+
void throwExceptionWhenAlreadyApproved() {
150+
// given
151+
when(shopRepository.findById(100)).thenReturn(Optional.of(shop));
152+
when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false);
153+
when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(true);
154+
155+
// when & then
156+
CustomException exception = assertThrows(CustomException.class,
157+
() -> shopToOrderableService.createOrderableRequest(1, request, 100));
158+
assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP);
159+
}
160+
}
161+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package in.koreatech.koin.unit.fixture;
2+
3+
import org.springframework.test.util.ReflectionTestUtils;
4+
5+
import in.koreatech.koin.domain.owner.model.Owner;
6+
import in.koreatech.koin.domain.shop.model.shop.Shop;
7+
8+
public class ShopFixture {
9+
10+
private ShopFixture() {}
11+
12+
public static Shop 주문전환_이전_상점(Owner owner) {
13+
Shop shop = Shop.builder()
14+
.name("테스트 상점")
15+
.owner(owner)
16+
.phone("041-123-4567")
17+
.address("천안시 동남구")
18+
.description("테스트 상점입니다")
19+
.build();
20+
ReflectionTestUtils.setField(shop, "id", 100);
21+
22+
return shop;
23+
}
24+
}

0 commit comments

Comments
 (0)