From a66d59653052dc0cf40762ddff74ad30e259586d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:23:41 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=8B=9D?= =?UTF-8?q?=EB=B3=84=EC=9E=90=20=EA=B8=B0=EB=B0=98=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=ED=99=95=EC=9D=B8=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradlew | 0 .../reservation/persistence/ReservationRepository.kt | 2 ++ .../persistence/SpringDataJpaShopPersistenceAdapter.kt | 9 ++++++++- .../src/main/resources/data.sql | 3 +++ .../SpringDataJpaShopPersistenceAdapterTest.kt | 7 +++++++ 5 files changed, 20 insertions(+), 1 deletion(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt index 53c76f5..b0e0b02 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt @@ -10,4 +10,6 @@ interface ReservationRepository : JpaRepository { @EntityGraph(attributePaths = ["shop"]) fun findOneWithShopById(reservationId: UUID): Optional + + fun existsReservationByShopId(shopId: Long): Boolean } diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt index e0955ee..d1c12c5 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt @@ -1,9 +1,11 @@ package com.mealkitary.shop.persistence import com.mealkitary.common.exception.EntityNotFoundException +import com.mealkitary.reservation.persistence.ReservationRepository import com.mealkitary.shop.application.port.output.LoadProductPort import com.mealkitary.shop.application.port.output.LoadReservableTimePort import com.mealkitary.shop.application.port.output.LoadShopPort +import com.mealkitary.shop.application.port.output.ShopPersistencePort import com.mealkitary.shop.domain.shop.Shop import org.springframework.stereotype.Repository import java.util.Optional @@ -14,7 +16,8 @@ private const val NOT_FOUND_SHOP_MESSAGE = "존재하지 않는 가게입니다. @Repository class SpringDataJpaShopPersistenceAdapter( private val shopRepository: ShopRepository, -) : LoadShopPort, LoadProductPort, LoadReservableTimePort { + private val reservationRepository: ReservationRepository +) : LoadShopPort, LoadProductPort, LoadReservableTimePort, ShopPersistencePort { override fun loadAllShop(): List = shopRepository.findAll() @@ -26,6 +29,10 @@ class SpringDataJpaShopPersistenceAdapter( override fun loadAllReservableTimeByShopId(shopId: Long) = getShopOrThrow(shopRepository::findOneWithReservableTimesById, shopId).reservableTimes + override fun hasReservations(shopId: Long): Boolean { + return reservationRepository.existsReservationByShopId(shopId) + } + private fun getShopOrThrow(queryMethod: Function>, shopId: Long): Shop { return (queryMethod.apply(shopId).orElseThrow { throw EntityNotFoundException(NOT_FOUND_SHOP_MESSAGE) }) } diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql index 80ae59c..199652a 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql @@ -45,3 +45,6 @@ insert into reservable_time values (3, '20:00'); insert into reservable_time values (3, '23:30'); + +insert into reservation +values ('c1e170e4-57e4-4d7d-8c10-6f4a8c658020', 1, '2023-09-22T12:30:00', 'NOTPAID'); diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt index 17e2bfc..b1b4d1e 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt @@ -14,6 +14,13 @@ class SpringDataJpaShopPersistenceAdapterTest( private val adapterUnderTest: SpringDataJpaShopPersistenceAdapter, ) : PersistenceIntegrationTestSupport() { + @Test + fun `db integration test - 가게에 예약이 존재하는지 확인한다`() { + val existsReservation = adapterUnderTest.hasReservations(1L) + + existsReservation.shouldBeTrue() + } + @Test fun `db integration test - 모든 가게를 조회한다`() { val shops = adapterUnderTest.loadAllShop() From 98ff9ef2ad705da9c204f9e06280b07dddf7a7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:47:37 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=9D=98=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=99=95=EC=9D=B8=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt | 8 ++++++++ .../kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt index 8cd2fa6..b5de0a6 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt @@ -82,4 +82,12 @@ class Shop( val match = reservableTimes.filter { it == reserveAt.toLocalTime() } return match.isNotEmpty() } + + fun changeStatusValid() { + status = ShopStatus.VALID + } + + fun changeStatusInvalid() { + status = ShopStatus.INVALID + } } diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt index 641de30..acee886 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt @@ -8,4 +8,8 @@ enum class ShopStatus { fun isInvalidStatus(): Boolean { return this == INVALID } + + fun isValidStatus(): Boolean { + return this == VALID + } } From 56cf39176d0a1747d8f34995a0f3585532d296c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:55:33 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../port/input/UpdateShopStatusUseCase.kt | 6 ++ .../port/output/ShopPersistencePort.kt | 10 +++ .../service/UpdateShopStatusService.kt | 41 +++++++++++ .../service/UpdateShopStatusServiceTest.kt | 70 +++++++++++++++++++ .../PersistenceIntegrationTestSupport.kt | 4 ++ 5 files changed, 131 insertions(+) create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt create mode 100644 mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt new file mode 100644 index 0000000..c65e600 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt @@ -0,0 +1,6 @@ +package com.mealkitary.shop.application.port.input + +interface UpdateShopStatusUseCase { + + fun update(shopId: Long) +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt new file mode 100644 index 0000000..6a59f3f --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt @@ -0,0 +1,10 @@ +package com.mealkitary.shop.application.port.output + +import com.mealkitary.shop.domain.shop.Shop + +interface ShopPersistencePort { + + fun loadOneShopById(shopId: Long): Shop + + fun hasReservations(shopId: Long): Boolean +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt new file mode 100644 index 0000000..404c8cc --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt @@ -0,0 +1,41 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase +import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.domain.shop.Shop +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional +class UpdateShopStatusService( + private val shopPersistencePort: ShopPersistencePort, +) : UpdateShopStatusUseCase { + + override fun update(shopId: Long) { + val shop = findShopById(shopId) + + if (shop.status.isValidStatus()) { + checkReservationByShopId(shopId) + } + updateStatus(shop) + } + + private fun findShopById(shopId: Long): Shop { + return shopPersistencePort.loadOneShopById(shopId) + } + + private fun updateStatus(shop: Shop) { + if (shop.status.isValidStatus()) { + shop.changeStatusInvalid() + } else { + shop.changeStatusValid() + } + } + + private fun checkReservationByShopId(shopId: Long) { + if (shopPersistencePort.hasReservations(shopId)) { + throw IllegalStateException("예약이 존재할 경우 가게 상태를 INVALID로 변경할 수 없습니다.") + } + } +} diff --git a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt new file mode 100644 index 0000000..4b38654 --- /dev/null +++ b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt @@ -0,0 +1,70 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.ShopStatus +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk + +class UpdateShopStatusServiceTest : AnnotationSpec() { + + private val shopPersistencePort = mockk() + private val updateShopStatusService = UpdateShopStatusService(shopPersistencePort) + + @Test + fun `service unit test - 가게의 상태를 INVALID로 변경한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.VALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns false + + updateShopStatusService.update(shopId) + + validShop.status shouldBe ShopStatus.INVALID + } + + @Test + fun `service unit test - 가게의 상태를 VALID로 변경한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.INVALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns false + + updateShopStatusService.update(shopId) + + validShop.status shouldBe ShopStatus.VALID + } + + @Test + fun `service unit test - 가게의 상태를 INVALID로 변경할 때, 예약이 존재하면 예외가 발생한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.VALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns true + + shouldThrow { + updateShopStatusService.update(shopId) + } + } +} diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt index a1836a0..b110289 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt @@ -3,6 +3,7 @@ package com.mealkitary import com.mealkitary.reservation.application.service.AcceptReservationService import com.mealkitary.reservation.application.service.PayReservationService import com.mealkitary.reservation.application.service.RejectReservationService +import com.mealkitary.shop.application.service.UpdateShopStatusService import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -32,4 +33,7 @@ abstract class PersistenceIntegrationTestSupport : AnnotationSpec() { @MockkBean private lateinit var rejectReservationService: RejectReservationService + + @MockkBean + private lateinit var updateShopStatusService: UpdateShopStatusService } From d389bd7ad4ed5eb2c8c9215ea9e9d7603f67a275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:59:24 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/web/UpdateShopStatusController.kt | 22 +++++++++++++++++++ .../mealkitary/WebIntegrationTestSupport.kt | 8 ++++++- .../web/UpdateShopStatusControllerTest.kt | 20 +++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt create mode 100644 mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt diff --git a/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt new file mode 100644 index 0000000..aeb88a0 --- /dev/null +++ b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt @@ -0,0 +1,22 @@ +package com.mealkitary.shop.web + +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/shops") +class UpdateShopStatusController( + private val updateShopStatusUseCase: UpdateShopStatusUseCase +) { + + @PostMapping("/{shopId}/status") + fun updateShopStatus(@PathVariable("shopId") shopId: Long): ResponseEntity { + updateShopStatusUseCase.update(shopId) + + return ResponseEntity.noContent().build() + } +} diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt index 337dfb9..2aa5cde 100644 --- a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt @@ -14,9 +14,11 @@ import com.mealkitary.reservation.web.ReserveProductController import com.mealkitary.shop.application.port.input.GetProductQuery import com.mealkitary.shop.application.port.input.GetReservableTimeQuery import com.mealkitary.shop.application.port.input.GetShopQuery +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase import com.mealkitary.shop.web.GetProductController import com.mealkitary.shop.web.GetReservableTimeController import com.mealkitary.shop.web.GetShopController +import com.mealkitary.shop.web.UpdateShopStatusController import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -33,7 +35,8 @@ import org.springframework.test.web.servlet.MockMvc GetReservationController::class, GetShopController::class, GetReservableTimeController::class, - GetProductController::class + GetProductController::class, + UpdateShopStatusController::class ] ) abstract class WebIntegrationTestSupport : AnnotationSpec() { @@ -69,4 +72,7 @@ abstract class WebIntegrationTestSupport : AnnotationSpec() { @MockkBean protected lateinit var getProductQuery: GetProductQuery + + @MockkBean + protected lateinit var updateShopStatusUseCase: UpdateShopStatusUseCase } diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt new file mode 100644 index 0000000..6f39494 --- /dev/null +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt @@ -0,0 +1,20 @@ +package com.mealkitary.shop.web + +import com.mealkitary.WebIntegrationTestSupport +import io.mockk.every +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers + +class UpdateShopStatusControllerTest : WebIntegrationTestSupport() { + + @Test + fun `api integration test - updateShopStatus`() { + val shopId = 1L + every { updateShopStatusUseCase.update(any()) } answers {} + + mvc.perform( + MockMvcRequestBuilders.post("/shops/{shopId}/status", shopId) + ) + .andExpect(MockMvcResultMatchers.status().isNoContent) + } +} From 7da22b5e96947423f1e33519373369e48c7e1229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:23:41 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=8B=9D?= =?UTF-8?q?=EB=B3=84=EC=9E=90=20=EA=B8=B0=EB=B0=98=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=ED=99=95=EC=9D=B8=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradlew | 0 .../reservation/persistence/ReservationRepository.kt | 2 ++ .../persistence/SpringDataJpaShopPersistenceAdapter.kt | 9 ++++++++- .../src/main/resources/data.sql | 3 +++ .../SpringDataJpaShopPersistenceAdapterTest.kt | 7 +++++++ 5 files changed, 20 insertions(+), 1 deletion(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt index 136f4d8..470c588 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt @@ -12,4 +12,6 @@ interface ReservationRepository : JpaRepository { fun findOneWithShopById(reservationId: UUID): Optional fun findAllByShopId(shopId: Long): List + + fun existsReservationByShopId(shopId: Long): Boolean } diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt index e0955ee..d1c12c5 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt @@ -1,9 +1,11 @@ package com.mealkitary.shop.persistence import com.mealkitary.common.exception.EntityNotFoundException +import com.mealkitary.reservation.persistence.ReservationRepository import com.mealkitary.shop.application.port.output.LoadProductPort import com.mealkitary.shop.application.port.output.LoadReservableTimePort import com.mealkitary.shop.application.port.output.LoadShopPort +import com.mealkitary.shop.application.port.output.ShopPersistencePort import com.mealkitary.shop.domain.shop.Shop import org.springframework.stereotype.Repository import java.util.Optional @@ -14,7 +16,8 @@ private const val NOT_FOUND_SHOP_MESSAGE = "존재하지 않는 가게입니다. @Repository class SpringDataJpaShopPersistenceAdapter( private val shopRepository: ShopRepository, -) : LoadShopPort, LoadProductPort, LoadReservableTimePort { + private val reservationRepository: ReservationRepository +) : LoadShopPort, LoadProductPort, LoadReservableTimePort, ShopPersistencePort { override fun loadAllShop(): List = shopRepository.findAll() @@ -26,6 +29,10 @@ class SpringDataJpaShopPersistenceAdapter( override fun loadAllReservableTimeByShopId(shopId: Long) = getShopOrThrow(shopRepository::findOneWithReservableTimesById, shopId).reservableTimes + override fun hasReservations(shopId: Long): Boolean { + return reservationRepository.existsReservationByShopId(shopId) + } + private fun getShopOrThrow(queryMethod: Function>, shopId: Long): Shop { return (queryMethod.apply(shopId).orElseThrow { throw EntityNotFoundException(NOT_FOUND_SHOP_MESSAGE) }) } diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql index 80ae59c..199652a 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql @@ -45,3 +45,6 @@ insert into reservable_time values (3, '20:00'); insert into reservable_time values (3, '23:30'); + +insert into reservation +values ('c1e170e4-57e4-4d7d-8c10-6f4a8c658020', 1, '2023-09-22T12:30:00', 'NOTPAID'); diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt index 17e2bfc..b1b4d1e 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt @@ -14,6 +14,13 @@ class SpringDataJpaShopPersistenceAdapterTest( private val adapterUnderTest: SpringDataJpaShopPersistenceAdapter, ) : PersistenceIntegrationTestSupport() { + @Test + fun `db integration test - 가게에 예약이 존재하는지 확인한다`() { + val existsReservation = adapterUnderTest.hasReservations(1L) + + existsReservation.shouldBeTrue() + } + @Test fun `db integration test - 모든 가게를 조회한다`() { val shops = adapterUnderTest.loadAllShop() From 7f4cc5f3bddd4062b3c33a7e45fc6bb5d8acafce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:47:37 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=9D=98=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=99=95=EC=9D=B8=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt | 8 ++++++++ .../kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt index 8cd2fa6..b5de0a6 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt @@ -82,4 +82,12 @@ class Shop( val match = reservableTimes.filter { it == reserveAt.toLocalTime() } return match.isNotEmpty() } + + fun changeStatusValid() { + status = ShopStatus.VALID + } + + fun changeStatusInvalid() { + status = ShopStatus.INVALID + } } diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt index 641de30..acee886 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopStatus.kt @@ -8,4 +8,8 @@ enum class ShopStatus { fun isInvalidStatus(): Boolean { return this == INVALID } + + fun isValidStatus(): Boolean { + return this == VALID + } } From 88c795c4e65a62162d88c700d5d336595089fef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:55:33 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../port/input/UpdateShopStatusUseCase.kt | 6 ++ .../port/output/ShopPersistencePort.kt | 10 +++ .../service/UpdateShopStatusService.kt | 41 +++++++++++ .../service/UpdateShopStatusServiceTest.kt | 70 +++++++++++++++++++ .../PersistenceIntegrationTestSupport.kt | 4 ++ 5 files changed, 131 insertions(+) create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt create mode 100644 mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt new file mode 100644 index 0000000..c65e600 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/UpdateShopStatusUseCase.kt @@ -0,0 +1,6 @@ +package com.mealkitary.shop.application.port.input + +interface UpdateShopStatusUseCase { + + fun update(shopId: Long) +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt new file mode 100644 index 0000000..6a59f3f --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt @@ -0,0 +1,10 @@ +package com.mealkitary.shop.application.port.output + +import com.mealkitary.shop.domain.shop.Shop + +interface ShopPersistencePort { + + fun loadOneShopById(shopId: Long): Shop + + fun hasReservations(shopId: Long): Boolean +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt new file mode 100644 index 0000000..404c8cc --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt @@ -0,0 +1,41 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase +import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.domain.shop.Shop +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional +class UpdateShopStatusService( + private val shopPersistencePort: ShopPersistencePort, +) : UpdateShopStatusUseCase { + + override fun update(shopId: Long) { + val shop = findShopById(shopId) + + if (shop.status.isValidStatus()) { + checkReservationByShopId(shopId) + } + updateStatus(shop) + } + + private fun findShopById(shopId: Long): Shop { + return shopPersistencePort.loadOneShopById(shopId) + } + + private fun updateStatus(shop: Shop) { + if (shop.status.isValidStatus()) { + shop.changeStatusInvalid() + } else { + shop.changeStatusValid() + } + } + + private fun checkReservationByShopId(shopId: Long) { + if (shopPersistencePort.hasReservations(shopId)) { + throw IllegalStateException("예약이 존재할 경우 가게 상태를 INVALID로 변경할 수 없습니다.") + } + } +} diff --git a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt new file mode 100644 index 0000000..4b38654 --- /dev/null +++ b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt @@ -0,0 +1,70 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.ShopStatus +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk + +class UpdateShopStatusServiceTest : AnnotationSpec() { + + private val shopPersistencePort = mockk() + private val updateShopStatusService = UpdateShopStatusService(shopPersistencePort) + + @Test + fun `service unit test - 가게의 상태를 INVALID로 변경한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.VALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns false + + updateShopStatusService.update(shopId) + + validShop.status shouldBe ShopStatus.INVALID + } + + @Test + fun `service unit test - 가게의 상태를 VALID로 변경한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.INVALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns false + + updateShopStatusService.update(shopId) + + validShop.status shouldBe ShopStatus.VALID + } + + @Test + fun `service unit test - 가게의 상태를 INVALID로 변경할 때, 예약이 존재하면 예외가 발생한다`() { + val shopId = 1L + val validShop = Shop( + "제목", + ShopStatus.VALID, + mutableListOf(), + mutableListOf() + ) + + every { shopPersistencePort.loadOneShopById(shopId) } returns validShop + every { shopPersistencePort.hasReservations(shopId) } returns true + + shouldThrow { + updateShopStatusService.update(shopId) + } + } +} diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt index a1836a0..b110289 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt @@ -3,6 +3,7 @@ package com.mealkitary import com.mealkitary.reservation.application.service.AcceptReservationService import com.mealkitary.reservation.application.service.PayReservationService import com.mealkitary.reservation.application.service.RejectReservationService +import com.mealkitary.shop.application.service.UpdateShopStatusService import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -32,4 +33,7 @@ abstract class PersistenceIntegrationTestSupport : AnnotationSpec() { @MockkBean private lateinit var rejectReservationService: RejectReservationService + + @MockkBean + private lateinit var updateShopStatusService: UpdateShopStatusService } From 2320566bd64ba460e959c9ce186836f30558effd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 00:59:24 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/web/UpdateShopStatusController.kt | 22 +++++++++++++++++++ .../mealkitary/WebIntegrationTestSupport.kt | 8 ++++++- .../web/UpdateShopStatusControllerTest.kt | 20 +++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt create mode 100644 mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt diff --git a/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt new file mode 100644 index 0000000..aeb88a0 --- /dev/null +++ b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/UpdateShopStatusController.kt @@ -0,0 +1,22 @@ +package com.mealkitary.shop.web + +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/shops") +class UpdateShopStatusController( + private val updateShopStatusUseCase: UpdateShopStatusUseCase +) { + + @PostMapping("/{shopId}/status") + fun updateShopStatus(@PathVariable("shopId") shopId: Long): ResponseEntity { + updateShopStatusUseCase.update(shopId) + + return ResponseEntity.noContent().build() + } +} diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt index 337dfb9..2aa5cde 100644 --- a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt @@ -14,9 +14,11 @@ import com.mealkitary.reservation.web.ReserveProductController import com.mealkitary.shop.application.port.input.GetProductQuery import com.mealkitary.shop.application.port.input.GetReservableTimeQuery import com.mealkitary.shop.application.port.input.GetShopQuery +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase import com.mealkitary.shop.web.GetProductController import com.mealkitary.shop.web.GetReservableTimeController import com.mealkitary.shop.web.GetShopController +import com.mealkitary.shop.web.UpdateShopStatusController import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -33,7 +35,8 @@ import org.springframework.test.web.servlet.MockMvc GetReservationController::class, GetShopController::class, GetReservableTimeController::class, - GetProductController::class + GetProductController::class, + UpdateShopStatusController::class ] ) abstract class WebIntegrationTestSupport : AnnotationSpec() { @@ -69,4 +72,7 @@ abstract class WebIntegrationTestSupport : AnnotationSpec() { @MockkBean protected lateinit var getProductQuery: GetProductQuery + + @MockkBean + protected lateinit var updateShopStatusUseCase: UpdateShopStatusUseCase } diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt new file mode 100644 index 0000000..6f39494 --- /dev/null +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/UpdateShopStatusControllerTest.kt @@ -0,0 +1,20 @@ +package com.mealkitary.shop.web + +import com.mealkitary.WebIntegrationTestSupport +import io.mockk.every +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers + +class UpdateShopStatusControllerTest : WebIntegrationTestSupport() { + + @Test + fun `api integration test - updateShopStatus`() { + val shopId = 1L + every { updateShopStatusUseCase.update(any()) } answers {} + + mvc.perform( + MockMvcRequestBuilders.post("/shops/{shopId}/status", shopId) + ) + .andExpect(MockMvcResultMatchers.status().isNoContent) + } +} From bc607b00351757a2cdd34c1f050e694c4684ffc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 02:12:10 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mealkitary/reservation/persistence/ReservationRepository.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt index 2943cc9..470c588 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/ReservationRepository.kt @@ -11,8 +11,6 @@ interface ReservationRepository : JpaRepository { @EntityGraph(attributePaths = ["shop"]) fun findOneWithShopById(reservationId: UUID): Optional - fun existsReservationByShopId(shopId: Long): Boolean - fun findAllByShopId(shopId: Long): List fun existsReservationByShopId(shopId: Long): Boolean From e64ba674916bdcc20228210a8a279b7639dc3392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 17:11:31 +0900 Subject: [PATCH 10/14] =?UTF-8?q?test:=20=EA=B0=80=EA=B2=8C=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/domain/shop/ShopStatusTest.kt | 22 +++++++++++++++++++ .../mealkitary/shop/domain/shop/ShopTest.kt | 21 ++++++++++++++++++ .../src/main/resources/data.sql | 11 ++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopStatusTest.kt diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopStatusTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopStatusTest.kt new file mode 100644 index 0000000..5b59fd4 --- /dev/null +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopStatusTest.kt @@ -0,0 +1,22 @@ +package com.mealkitary.shop.domain.shop + +import data.ShopTestData.Companion.defaultShop +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class ShopStatusTest { + + @Test + fun isInvalidStatus() { + val shop = defaultShop().withStatus(ShopStatus.VALID).build() + + shop.status.isValidStatus() shouldBe true + } + + @Test + fun isValidStatus() { + val shop = defaultShop().withStatus(ShopStatus.INVALID).build() + + shop.status.isInvalidStatus() shouldBe true + } +} diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt index d12bf3b..6a5e371 100644 --- a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt @@ -5,10 +5,31 @@ import data.ShopTestData.Companion.defaultShop import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.AnnotationSpec import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe import io.kotest.matchers.throwable.shouldHaveMessage class ShopTest : AnnotationSpec() { + @Test + fun `가게 상태를 VALID로 변경한다`() { + val sut = defaultShop().withStatus(ShopStatus.INVALID) + .build() + + sut.changeStatusValid() + + sut.status shouldBe ShopStatus.VALID + } + + @Test + fun `가게 상태를 INVALID로 변경한다`() { + val sut = defaultShop().withStatus(ShopStatus.VALID) + .build() + + sut.changeStatusInvalid() + + sut.status shouldBe ShopStatus.INVALID + } + @Test fun `유효하지 않은 가게라면 예외를 발생한다`() { val sut = defaultShop().withStatus(ShopStatus.INVALID) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql index 199652a..fe38e21 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql @@ -46,5 +46,12 @@ values (3, '20:00'); insert into reservable_time values (3, '23:30'); -insert into reservation -values ('c1e170e4-57e4-4d7d-8c10-6f4a8c658020', 1, '2023-09-22T12:30:00', 'NOTPAID'); + +insert into shop +values (4, 'VALID', '집밥뚝딱 다산점'); +insert into product +values (10, '김치찌개', 15800, 4); +insert into reservable_time +values (4, '16:30'); +insert into reservation (id, shop_id, reserve_at, reservation_status) +values ('c1e170e4-57e4-4d7d-8c10-6f4a8c658020', 4, '2023-09-22T16:30:00', 'NOTPAID'); From 8f4274c9418994804c08582bfcd815b0526ab479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 24 Sep 2023 17:27:03 +0900 Subject: [PATCH 11/14] =?UTF-8?q?test:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/SpringDataJpaShopPersistenceAdapterTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt index b1b4d1e..9b5746e 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt @@ -16,7 +16,7 @@ class SpringDataJpaShopPersistenceAdapterTest( @Test fun `db integration test - 가게에 예약이 존재하는지 확인한다`() { - val existsReservation = adapterUnderTest.hasReservations(1L) + val existsReservation = adapterUnderTest.hasReservations(4L) existsReservation.shouldBeTrue() } @@ -25,7 +25,7 @@ class SpringDataJpaShopPersistenceAdapterTest( fun `db integration test - 모든 가게를 조회한다`() { val shops = adapterUnderTest.loadAllShop() - shops.size shouldBe 3 + shops.size shouldBe 4 shops.get(0).title shouldBe "집밥뚝딱 철산점" } @@ -101,6 +101,8 @@ class SpringDataJpaShopPersistenceAdapterTest( @Test fun `db integration test - 가게가 하나도 존재하지 않는 경우 빈 배열을 반환한다 `() { + em.createQuery("delete from Reservation r") + .executeUpdate() em.createQuery("delete from Product p") .executeUpdate() em.createNativeQuery("delete from reservable_time") From 9f67061374aa78454db61adae4ffc98743284d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Mon, 25 Sep 2023 21:00:20 +0900 Subject: [PATCH 12/14] =?UTF-8?q?test:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20API=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UpdateShopStatusControllerDocsTest.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 mealkitary-api/src/test/kotlin/com/docs/shop/UpdateShopStatusControllerDocsTest.kt diff --git a/mealkitary-api/src/test/kotlin/com/docs/shop/UpdateShopStatusControllerDocsTest.kt b/mealkitary-api/src/test/kotlin/com/docs/shop/UpdateShopStatusControllerDocsTest.kt new file mode 100644 index 0000000..7a573ba --- /dev/null +++ b/mealkitary-api/src/test/kotlin/com/docs/shop/UpdateShopStatusControllerDocsTest.kt @@ -0,0 +1,45 @@ +package com.docs.shop + +import com.docs.RestDocsSupport +import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase +import com.mealkitary.shop.web.UpdateShopStatusController +import io.mockk.every +import io.mockk.mockk +import org.springframework.http.MediaType +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders +import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest +import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse +import org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint +import org.springframework.restdocs.request.RequestDocumentation.parameterWithName +import org.springframework.restdocs.request.RequestDocumentation.pathParameters +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class UpdateShopStatusControllerDocsTest : RestDocsSupport() { + + private val updateShopStatsUseCase = mockk() + + @Test + fun `api docs test - updateShopStatus`() { + val shopId = 1L + every { updateShopStatsUseCase.update(shopId) }.answers { } + + mvc.perform( + RestDocumentationRequestBuilders.post("/shops/{shopId}/status", shopId) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent) + .andDo( + document( + "shop-post-update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("shopId").description("상태 변경 가게의 식별자") + ), + ) + ) + } + + override fun initController() = UpdateShopStatusController(updateShopStatsUseCase) +} From 5789df1c80eb41502e1f754305cd17ab5470b280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Tue, 26 Sep 2023 23:33:35 +0900 Subject: [PATCH 13/14] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../port/output/CheckExistenceShopPort.kt | 6 ++++++ .../port/output/ShopPersistencePort.kt | 10 ---------- .../service/UpdateShopStatusService.kt | 10 ++++++---- .../service/UpdateShopStatusServiceTest.kt | 20 ++++++++++--------- .../SpringDataJpaShopPersistenceAdapter.kt | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/CheckExistenceShopPort.kt delete mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/CheckExistenceShopPort.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/CheckExistenceShopPort.kt new file mode 100644 index 0000000..7630295 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/CheckExistenceShopPort.kt @@ -0,0 +1,6 @@ +package com.mealkitary.shop.application.port.output + +interface CheckExistenceShopPort { + + fun hasReservations(shopId: Long): Boolean +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt deleted file mode 100644 index 6a59f3f..0000000 --- a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/ShopPersistencePort.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.mealkitary.shop.application.port.output - -import com.mealkitary.shop.domain.shop.Shop - -interface ShopPersistencePort { - - fun loadOneShopById(shopId: Long): Shop - - fun hasReservations(shopId: Long): Boolean -} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt index 404c8cc..e7ce594 100644 --- a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt @@ -1,7 +1,8 @@ package com.mealkitary.shop.application.service import com.mealkitary.shop.application.port.input.UpdateShopStatusUseCase -import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.application.port.output.CheckExistenceShopPort +import com.mealkitary.shop.application.port.output.LoadShopPort import com.mealkitary.shop.domain.shop.Shop import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -9,7 +10,8 @@ import org.springframework.transaction.annotation.Transactional @Service @Transactional class UpdateShopStatusService( - private val shopPersistencePort: ShopPersistencePort, + private val checkExistenceShopPort: CheckExistenceShopPort, + private val loadShopPort: LoadShopPort ) : UpdateShopStatusUseCase { override fun update(shopId: Long) { @@ -22,7 +24,7 @@ class UpdateShopStatusService( } private fun findShopById(shopId: Long): Shop { - return shopPersistencePort.loadOneShopById(shopId) + return loadShopPort.loadOneShopById(shopId) } private fun updateStatus(shop: Shop) { @@ -34,7 +36,7 @@ class UpdateShopStatusService( } private fun checkReservationByShopId(shopId: Long) { - if (shopPersistencePort.hasReservations(shopId)) { + if (checkExistenceShopPort.hasReservations(shopId)) { throw IllegalStateException("예약이 존재할 경우 가게 상태를 INVALID로 변경할 수 없습니다.") } } diff --git a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt index 4b38654..5e69d08 100644 --- a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt +++ b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusServiceTest.kt @@ -1,6 +1,7 @@ package com.mealkitary.shop.application.service -import com.mealkitary.shop.application.port.output.ShopPersistencePort +import com.mealkitary.shop.application.port.output.CheckExistenceShopPort +import com.mealkitary.shop.application.port.output.LoadShopPort import com.mealkitary.shop.domain.shop.Shop import com.mealkitary.shop.domain.shop.ShopStatus import io.kotest.assertions.throwables.shouldThrow @@ -11,8 +12,9 @@ import io.mockk.mockk class UpdateShopStatusServiceTest : AnnotationSpec() { - private val shopPersistencePort = mockk() - private val updateShopStatusService = UpdateShopStatusService(shopPersistencePort) + private val checkExistenceShopPort = mockk() + private val loadShopPort = mockk() + private val updateShopStatusService = UpdateShopStatusService(checkExistenceShopPort, loadShopPort) @Test fun `service unit test - 가게의 상태를 INVALID로 변경한다`() { @@ -24,8 +26,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() { mutableListOf() ) - every { shopPersistencePort.loadOneShopById(shopId) } returns validShop - every { shopPersistencePort.hasReservations(shopId) } returns false + every { loadShopPort.loadOneShopById(shopId) } returns validShop + every { checkExistenceShopPort.hasReservations(shopId) } returns false updateShopStatusService.update(shopId) @@ -42,8 +44,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() { mutableListOf() ) - every { shopPersistencePort.loadOneShopById(shopId) } returns validShop - every { shopPersistencePort.hasReservations(shopId) } returns false + every { loadShopPort.loadOneShopById(shopId) } returns validShop + every { checkExistenceShopPort.hasReservations(shopId) } returns false updateShopStatusService.update(shopId) @@ -60,8 +62,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() { mutableListOf() ) - every { shopPersistencePort.loadOneShopById(shopId) } returns validShop - every { shopPersistencePort.hasReservations(shopId) } returns true + every { loadShopPort.loadOneShopById(shopId) } returns validShop + every { checkExistenceShopPort.hasReservations(shopId) } returns true shouldThrow { updateShopStatusService.update(shopId) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt index d1c12c5..c89489e 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt @@ -2,10 +2,10 @@ package com.mealkitary.shop.persistence import com.mealkitary.common.exception.EntityNotFoundException import com.mealkitary.reservation.persistence.ReservationRepository +import com.mealkitary.shop.application.port.output.CheckExistenceShopPort import com.mealkitary.shop.application.port.output.LoadProductPort import com.mealkitary.shop.application.port.output.LoadReservableTimePort import com.mealkitary.shop.application.port.output.LoadShopPort -import com.mealkitary.shop.application.port.output.ShopPersistencePort import com.mealkitary.shop.domain.shop.Shop import org.springframework.stereotype.Repository import java.util.Optional @@ -17,7 +17,7 @@ private const val NOT_FOUND_SHOP_MESSAGE = "존재하지 않는 가게입니다. class SpringDataJpaShopPersistenceAdapter( private val shopRepository: ShopRepository, private val reservationRepository: ReservationRepository -) : LoadShopPort, LoadProductPort, LoadReservableTimePort, ShopPersistencePort { +) : LoadShopPort, LoadProductPort, LoadReservableTimePort, CheckExistenceShopPort { override fun loadAllShop(): List = shopRepository.findAll() From 2b2c20fbaf1b505b554f5539576cd214d3f42da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Tue, 26 Sep 2023 23:35:32 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B8=EB=9D=BC=EC=9D=B8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/UpdateShopStatusService.kt | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt index e7ce594..3893e0c 100644 --- a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/UpdateShopStatusService.kt @@ -15,7 +15,7 @@ class UpdateShopStatusService( ) : UpdateShopStatusUseCase { override fun update(shopId: Long) { - val shop = findShopById(shopId) + val shop = loadShopPort.loadOneShopById(shopId) if (shop.status.isValidStatus()) { checkReservationByShopId(shopId) @@ -23,8 +23,10 @@ class UpdateShopStatusService( updateStatus(shop) } - private fun findShopById(shopId: Long): Shop { - return loadShopPort.loadOneShopById(shopId) + private fun checkReservationByShopId(shopId: Long) { + if (checkExistenceShopPort.hasReservations(shopId)) { + throw IllegalStateException("예약이 존재할 경우 가게 상태를 INVALID로 변경할 수 없습니다.") + } } private fun updateStatus(shop: Shop) { @@ -34,10 +36,4 @@ class UpdateShopStatusService( shop.changeStatusValid() } } - - private fun checkReservationByShopId(shopId: Long) { - if (checkExistenceShopPort.hasReservations(shopId)) { - throw IllegalStateException("예약이 존재할 경우 가게 상태를 INVALID로 변경할 수 없습니다.") - } - } }