fix(gift): 누락된 발신자 푸시를 보완하고 가격 스냅샷을 검증한다
This commit is contained in:
+1
@@ -85,6 +85,7 @@ class GiftScheduler(
|
|||||||
gift.status = GiftStatus.UNDELIVERABLE
|
gift.status = GiftStatus.UNDELIVERABLE
|
||||||
delivery.undeliverableAt = now
|
delivery.undeliverableAt = now
|
||||||
delivery.undeliverableReason = EXPIRED_RECIPIENT_ADDRESS_REASON
|
delivery.undeliverableReason = EXPIRED_RECIPIENT_ADDRESS_REASON
|
||||||
|
giftPushService.sendUndeliverableToSender(gift.applicationNo, gift.senderMemberId)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ class GiftCommandService(
|
|||||||
|
|
||||||
gift.status = GiftStatus.DELIVERED
|
gift.status = GiftStatus.DELIVERED
|
||||||
delivery.deliveredAt = deliveredAt
|
delivery.deliveredAt = deliveredAt
|
||||||
|
giftPushService.sendDeliveredToSender(gift.applicationNo, gift.senderMemberId)
|
||||||
|
|
||||||
return GiftDeliveryConfirmationResult(
|
return GiftDeliveryConfirmationResult(
|
||||||
applicationNo = gift.applicationNo,
|
applicationNo = gift.applicationNo,
|
||||||
|
|||||||
+248
@@ -33,9 +33,11 @@ import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice
|
|||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository
|
||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.scheduler.GiftScheduler
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.scheduler.GiftScheduler
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftAdminService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftAdminService
|
||||||
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationCommand
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftCommandService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftCommandService
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftPushService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftPushService
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationCommand
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationCommand
|
||||||
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftSizePriceCommand
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationCommand
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationCommand
|
||||||
import kr.co.vividnext.sodalive.v2.gift.domain.GiftSize
|
import kr.co.vividnext.sodalive.v2.gift.domain.GiftSize
|
||||||
import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus
|
import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus
|
||||||
@@ -167,6 +169,7 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
giftDeliveryRepository.deleteAll()
|
giftDeliveryRepository.deleteAll()
|
||||||
giftRepository.deleteAll()
|
giftRepository.deleteAll()
|
||||||
categoryRepository.deleteAll()
|
categoryRepository.deleteAll()
|
||||||
|
sizePriceRepository.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@@ -544,6 +547,78 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("실제 JWT HTTP 수령 확인은 발신자만 한 번 알리고 거부·반복 요청은 추가 알림을 보내지 않는다")
|
||||||
|
fun shouldConfirmDeliveryThroughRealJwtHttpAndNotifyOnlySenderOnce() {
|
||||||
|
val recipient = memberRepository.saveAndFlush(
|
||||||
|
Member(
|
||||||
|
email = "${UUID.randomUUID()}@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = "confirmation-creator",
|
||||||
|
role = MemberRole.CREATOR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val fixture = saveFundedGift(recipient)
|
||||||
|
val giftId = requireNotNull(fixture.gift.id)
|
||||||
|
TransactionTemplate(transactionManager).executeWithoutResult {
|
||||||
|
giftRepository.findById(giftId).orElseThrow().status = GiftStatus.INSPECTION_COMPLETED
|
||||||
|
}
|
||||||
|
val path = "/api/v2/gifts/$APPLICATION_NO/delivery-complete"
|
||||||
|
giftHttp(HttpMethod.POST, path, expectedStatus = HttpStatus.UNAUTHORIZED)
|
||||||
|
val denied = giftHttp(HttpMethod.POST, path, tokenFor(fixture.sender))
|
||||||
|
assertFalse(denied.path("success").asBoolean())
|
||||||
|
assertEquals("Invalid request.", denied.path("message").asText())
|
||||||
|
assertEquals(GiftStatus.INSPECTION_COMPLETED, giftRepository.findById(giftId).orElseThrow().status)
|
||||||
|
assertEquals(null, giftDeliveryRepository.findByGiftId(giftId)?.deliveredAt)
|
||||||
|
Mockito.verifyNoInteractions(giftPushService)
|
||||||
|
|
||||||
|
val recipientToken = tokenFor(recipient)
|
||||||
|
val confirmed = giftHttp(HttpMethod.POST, path, recipientToken)
|
||||||
|
assertTrue(confirmed.path("success").asBoolean())
|
||||||
|
assertEquals(APPLICATION_NO, confirmed.path("data").path("applicationNo").asText())
|
||||||
|
assertEquals("DELIVERED", confirmed.path("data").path("status").asText())
|
||||||
|
val deliveredAt = requireNotNull(giftDeliveryRepository.findByGiftId(giftId)?.deliveredAt)
|
||||||
|
assertEquals(deliveredAt.toUtcIso(), confirmed.path("data").path("deliveredAt").asText())
|
||||||
|
assertEquals(GiftStatus.DELIVERED, giftRepository.findById(giftId).orElseThrow().status)
|
||||||
|
Mockito.verify(giftPushService).sendDeliveredToSender(APPLICATION_NO, requireNotNull(fixture.sender.id))
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
|
|
||||||
|
val repeated = giftHttp(HttpMethod.POST, path, recipientToken)
|
||||||
|
assertFalse(repeated.path("success").asBoolean())
|
||||||
|
assertEquals("Invalid request.", repeated.path("message").asText())
|
||||||
|
assertEquals(deliveredAt, giftDeliveryRepository.findByGiftId(giftId)?.deliveredAt)
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
|
assertRefundState(fixture, refunded = false)
|
||||||
|
println("P16_CONFIRM_HTTP status=200 success=true rejected=true repeated=false senderPush=1 recipientPush=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("실제 transactional 스케줄러 배송지 만료는 발신자만 한 번 알리고 재실행은 추가 알림을 보내지 않는다")
|
||||||
|
fun shouldExpireRecipientAddressThroughTransactionalSchedulerAndNotifyOnlySenderOnce() {
|
||||||
|
val fixture = saveFundedGift()
|
||||||
|
val giftId = requireNotNull(fixture.gift.id)
|
||||||
|
TransactionTemplate(transactionManager).executeWithoutResult {
|
||||||
|
giftRepository.findById(giftId).orElseThrow().status = GiftStatus.TRACKING_REGISTERED
|
||||||
|
requireNotNull(giftDeliveryRepository.findByGiftId(giftId)).recipientAddressDeadlineAt = NOW.minusSeconds(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(TransactionSynchronizationManager.isActualTransactionActive())
|
||||||
|
assertEquals(1, scheduler.markExpiredRecipientAddressGiftsUndeliverable(NOW))
|
||||||
|
assertFalse(TransactionSynchronizationManager.isActualTransactionActive())
|
||||||
|
assertEquals(GiftStatus.UNDELIVERABLE, giftRepository.findById(giftId).orElseThrow().status)
|
||||||
|
val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(giftId))
|
||||||
|
assertEquals(NOW, delivery.undeliverableAt)
|
||||||
|
assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason)
|
||||||
|
Mockito.verify(giftPushService).sendUndeliverableToSender(APPLICATION_NO, requireNotNull(fixture.sender.id))
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
|
|
||||||
|
assertEquals(0, scheduler.markExpiredRecipientAddressGiftsUndeliverable(NOW.plusMinutes(1)))
|
||||||
|
assertEquals(NOW, giftDeliveryRepository.findByGiftId(giftId)?.undeliverableAt)
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
|
assertRefundState(fixture, refunded = false)
|
||||||
|
println("P16_EXPIRY_DB applicationNo=$APPLICATION_NO transitioned=1 repeated=0 senderPush=1 recipientPush=0")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("관리자는 카테고리를 등록, 조회, 수정, 논리삭제한다")
|
@DisplayName("관리자는 카테고리를 등록, 조회, 수정, 논리삭제한다")
|
||||||
fun shouldManageGiftCategories() {
|
fun shouldManageGiftCategories() {
|
||||||
@@ -635,6 +710,166 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
.andExpect(jsonPath("$.success").value(false))
|
.andExpect(jsonPath("$.success").value(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("실제 신청은 가격 변경과 카테고리 비활성화 후에도 기존 가격·카테고리명·결제 스냅샷을 보존한다")
|
||||||
|
fun shouldPreserveExistingGiftSnapshotsAfterPriceChangeAndCategoryDeactivation() {
|
||||||
|
val transaction = TransactionTemplate(transactionManager)
|
||||||
|
val sender = memberRepository.saveAndFlush(
|
||||||
|
Member(email = "${UUID.randomUUID()}@test.com", password = "password", nickname = "snapshot-sender").apply {
|
||||||
|
pgChargeCan = 300
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val creator = memberRepository.saveAndFlush(
|
||||||
|
Member(
|
||||||
|
email = "${UUID.randomUUID()}@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = "snapshot-creator",
|
||||||
|
role = MemberRole.CREATOR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(creator.id, memberRepository.findCreatorByIdOrNull(requireNotNull(creator.id))?.id)
|
||||||
|
val funding = chargeRepository.saveAndFlush(
|
||||||
|
Charge(300, 0).apply {
|
||||||
|
member = sender
|
||||||
|
payment = Payment(status = PaymentStatus.COMPLETE, paymentGateway = PaymentGateway.PG)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val category = categoryRepository.saveAndFlush(
|
||||||
|
GiftCategory("100", "SNAPSHOT", "인형", "A", "피규어")
|
||||||
|
)
|
||||||
|
val categoryId = requireNotNull(category.id)
|
||||||
|
val senderId = requireNotNull(sender.id)
|
||||||
|
val originalUseCount = useCanRepository.count()
|
||||||
|
val originalCalculateCount = calculateRepository.count()
|
||||||
|
val originalChargeCount = chargeRepository.count()
|
||||||
|
adminService.updateSizePrice(GiftSize.SMALL, GiftSizePriceCommand(150, 80, true))
|
||||||
|
val command = GiftApplicationCommand(
|
||||||
|
recipientMemberId = requireNotNull(creator.id),
|
||||||
|
senderName = "sender",
|
||||||
|
senderPhoneNumber = "01012345678",
|
||||||
|
senderZipCode = "06234",
|
||||||
|
senderAddress = "test address",
|
||||||
|
senderAddressDetail = null,
|
||||||
|
sizeCode = GiftSize.SMALL,
|
||||||
|
categoryId = categoryId,
|
||||||
|
senderTermsAgreed = true,
|
||||||
|
senderPrivacyAgreed = true,
|
||||||
|
damageWaiverAgreed = false
|
||||||
|
)
|
||||||
|
|
||||||
|
val first = commandService.registerGift(sender, command)
|
||||||
|
assertEquals(80, first.priceCan)
|
||||||
|
val firstUsageId = requireNotNull(
|
||||||
|
transaction.execute {
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
val gift = requireNotNull(giftRepository.findByApplicationNo(first.applicationNo))
|
||||||
|
assertEquals(80, gift.salePriceCan)
|
||||||
|
assertEquals("인형", gift.categoryNameSnapshot)
|
||||||
|
assertEquals(80, useCanRepository.findById(gift.canUsageId).orElseThrow().can)
|
||||||
|
assertEquals(220, memberRepository.findById(senderId).orElseThrow().pgChargeCan)
|
||||||
|
assertEquals(220, chargeRepository.findById(requireNotNull(funding.id)).orElseThrow().chargeCan)
|
||||||
|
gift.canUsageId
|
||||||
|
}
|
||||||
|
)
|
||||||
|
adminService.updateSizePrice(GiftSize.SMALL, GiftSizePriceCommand(150, 120, true))
|
||||||
|
val second = commandService.registerGift(sender, command)
|
||||||
|
assertEquals(120, second.priceCan)
|
||||||
|
assertNotEquals(first.applicationNo, second.applicationNo)
|
||||||
|
val prices = mapOf(first.applicationNo to 80, second.applicationNo to 120)
|
||||||
|
val usageIds = prices.keys.associateWith {
|
||||||
|
requireNotNull(giftRepository.findByApplicationNo(it)).canUsageId
|
||||||
|
}
|
||||||
|
assertEquals(firstUsageId, usageIds[first.applicationNo])
|
||||||
|
assertNotEquals(firstUsageId, usageIds[second.applicationNo])
|
||||||
|
val senderToken = tokenFor(sender)
|
||||||
|
|
||||||
|
listOf(false, true).forEach { deactivated ->
|
||||||
|
if (deactivated) {
|
||||||
|
assertFalse(adminService.deleteCategory(categoryId).isActive)
|
||||||
|
val exception = assertThrows(SodaException::class.java) { commandService.registerGift(sender, command) }
|
||||||
|
assertEquals("common.error.invalid_request", exception.messageKey)
|
||||||
|
}
|
||||||
|
transaction.executeWithoutResult {
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
assertEquals(!deactivated, categoryRepository.findById(categoryId).orElseThrow().isActive)
|
||||||
|
assertEquals(120, requireNotNull(sizePriceRepository.findBySizeCode(GiftSize.SMALL)).salePriceCan)
|
||||||
|
prices.forEach { (applicationNo, price) ->
|
||||||
|
val gift = requireNotNull(giftRepository.findByApplicationNo(applicationNo))
|
||||||
|
assertEquals(price, gift.salePriceCan)
|
||||||
|
assertEquals("인형", gift.categoryNameSnapshot)
|
||||||
|
assertEquals(categoryId, gift.categoryId)
|
||||||
|
assertEquals(usageIds[applicationNo], gift.canUsageId)
|
||||||
|
val usage = useCanRepository.findById(gift.canUsageId).orElseThrow()
|
||||||
|
assertEquals(CanUsage.GIFT, usage.canUsage)
|
||||||
|
assertEquals(senderId, usage.member?.id)
|
||||||
|
assertEquals(price, usage.can)
|
||||||
|
assertEquals(0, usage.rewardCan)
|
||||||
|
assertFalse(usage.isRefund)
|
||||||
|
val calculations = calculateRepository.findByUseCanIdAndStatus(
|
||||||
|
gift.canUsageId,
|
||||||
|
UseCanCalculateStatus.RECEIVED
|
||||||
|
)
|
||||||
|
assertEquals(1, calculations.size)
|
||||||
|
assertEquals(price, calculations.single().can)
|
||||||
|
assertEquals(PaymentGateway.PG, calculations.single().paymentGateway)
|
||||||
|
}
|
||||||
|
val reloadedSender = memberRepository.findById(senderId).orElseThrow()
|
||||||
|
val reloadedFunding = chargeRepository.findById(requireNotNull(funding.id)).orElseThrow()
|
||||||
|
assertEquals(100, reloadedSender.pgChargeCan)
|
||||||
|
assertEquals(0, reloadedSender.pgRewardCan)
|
||||||
|
assertEquals(100, reloadedFunding.chargeCan)
|
||||||
|
assertEquals(0, reloadedFunding.rewardCan)
|
||||||
|
assertEquals(ChargeStatus.CHARGE, reloadedFunding.status)
|
||||||
|
assertEquals(PaymentStatus.COMPLETE, reloadedFunding.payment?.status)
|
||||||
|
assertEquals(PaymentGateway.PG, reloadedFunding.payment?.paymentGateway)
|
||||||
|
assertEquals(2L, giftRepository.count())
|
||||||
|
assertEquals(2L, giftDeliveryRepository.count())
|
||||||
|
assertEquals(originalUseCount + 2, useCanRepository.count())
|
||||||
|
assertEquals(originalCalculateCount + 2, calculateRepository.count())
|
||||||
|
assertEquals(originalChargeCount, chargeRepository.count())
|
||||||
|
}
|
||||||
|
val options = giftHttp(HttpMethod.GET, "/api/v2/gifts/form-options", senderToken)
|
||||||
|
assertTrue(options.path("success").asBoolean())
|
||||||
|
assertEquals(
|
||||||
|
!deactivated,
|
||||||
|
options.path("data").path("categories").any { it.path("categoryId").asLong() == categoryId }
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
120,
|
||||||
|
options.path("data").path("sizes").single { it.path("sizeCode").asText() == "SMALL" }
|
||||||
|
.path("salePriceCan").asInt()
|
||||||
|
)
|
||||||
|
prices.forEach { (applicationNo, price) ->
|
||||||
|
val detail = giftHttp(HttpMethod.GET, "/api/v2/gifts/$applicationNo", senderToken)
|
||||||
|
assertTrue(detail.path("success").asBoolean())
|
||||||
|
assertEquals(applicationNo, detail.path("data").path("applicationNo").asText())
|
||||||
|
assertEquals(price, detail.path("data").path("giftInfo").path("paidCan").asInt())
|
||||||
|
assertEquals("인형", detail.path("data").path("giftInfo").path("categoryName").asText())
|
||||||
|
println("P16_SNAPSHOT_HTTP deactivated=$deactivated applicationNo=$applicationNo paidCan=$price categoryName=인형")
|
||||||
|
}
|
||||||
|
val list = giftHttp(HttpMethod.GET, "/api/v2/gifts?type=SENT", senderToken)
|
||||||
|
assertTrue(list.path("success").asBoolean())
|
||||||
|
assertEquals(2, list.path("data").path("totalCount").asInt())
|
||||||
|
val items = list.path("data").path("items").associateBy { it.path("applicationNo").asText() }
|
||||||
|
assertEquals(prices.keys, items.keys)
|
||||||
|
prices.forEach { (applicationNo, price) ->
|
||||||
|
val item = requireNotNull(items[applicationNo])
|
||||||
|
assertEquals(price, item.path("priceCan").asInt())
|
||||||
|
assertEquals("인형", item.path("categoryName").asText())
|
||||||
|
}
|
||||||
|
println(
|
||||||
|
"P16_SNAPSHOT_DB deactivated=$deactivated A=80 B=120 categoryName=인형 usageIdsPreserved=true " +
|
||||||
|
"actualUseCan=80,120 memberBalance=100 chargeBalance=100 gifts=2 usesDelta=2 chargesDelta=0 listCount=2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Mockito.verify(canPaymentService).spendGiftCan(senderId, 80, "web")
|
||||||
|
Mockito.verify(canPaymentService).spendGiftCan(senderId, 120, "web")
|
||||||
|
assertEquals(2, Mockito.mockingDetails(canPaymentService).invocations.count { it.method.name == "spendGiftCan" })
|
||||||
|
Mockito.verifyNoInteractions(giftPushService)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("관리자는 사서함 도착과 검수완료를 처리한다")
|
@DisplayName("관리자는 사서함 도착과 검수완료를 처리한다")
|
||||||
fun shouldUpdateGiftOperationStatuses() {
|
fun shouldUpdateGiftOperationStatuses() {
|
||||||
@@ -1157,6 +1392,19 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
assertEquals(0, scheduler.markExpiredRecipientAddressGiftsUndeliverable(NOW))
|
assertEquals(0, scheduler.markExpiredRecipientAddressGiftsUndeliverable(NOW))
|
||||||
Mockito.verify(giftPushService, Mockito.times(if (operations.first == RecipientMutation.REMINDER) 1 else 0))
|
Mockito.verify(giftPushService, Mockito.times(if (operations.first == RecipientMutation.REMINDER) 1 else 0))
|
||||||
.sendRecipientAddressDeadlineReminder(APPLICATION_NO, fixture.gift.recipientMemberId)
|
.sendRecipientAddressDeadlineReminder(APPLICATION_NO, fixture.gift.recipientMemberId)
|
||||||
|
if (
|
||||||
|
listOf(operations.first, operations.second).any {
|
||||||
|
it in listOf(RecipientMutation.CONFIRM, RecipientMutation.EXPIRY)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val confirmed = RecipientMutation.CONFIRM in listOf(operations.first, operations.second) &&
|
||||||
|
operations.first != RecipientMutation.EXPIRY
|
||||||
|
Mockito.verify(giftPushService, Mockito.times(if (confirmed) 1 else 0))
|
||||||
|
.sendDeliveredToSender(APPLICATION_NO, requireNotNull(fixture.sender.id))
|
||||||
|
Mockito.verify(giftPushService, Mockito.times(if (operations.first == RecipientMutation.EXPIRY) 1 else 0))
|
||||||
|
.sendUndeliverableToSender(APPLICATION_NO, requireNotNull(fixture.sender.id))
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
|
}
|
||||||
assertRefundState(fixture, refunded = false)
|
assertRefundState(fixture, refunded = false)
|
||||||
println(
|
println(
|
||||||
"P15_R1_RACE $operations first=$firstResult second=$secondResult serialized=$blockedUntilCommit persisted=true"
|
"P15_R1_RACE $operations first=$firstResult second=$secondResult serialized=$blockedUntilCommit persisted=true"
|
||||||
|
|||||||
@@ -241,7 +241,26 @@ class GiftCommandServiceTest {
|
|||||||
)
|
)
|
||||||
assertEquals(GiftStatus.DELIVERED, fixture.deliveryConfirmationGift.status)
|
assertEquals(GiftStatus.DELIVERED, fixture.deliveryConfirmationGift.status)
|
||||||
assertEquals(NOW, fixture.deliveryConfirmationDelivery.deliveredAt)
|
assertEquals(NOW, fixture.deliveryConfirmationDelivery.deliveredAt)
|
||||||
Mockito.verifyNoInteractions(fixture.giftPushService)
|
Mockito.verify(fixture.giftPushService).sendDeliveredToSender(APPLICATION_NO, SENDER_ID)
|
||||||
|
Mockito.verifyNoMoreInteractions(fixture.giftPushService)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("수령 확인을 반복하면 전달 완료 시각을 보존하고 발신자에게 추가 알림을 보내지 않는다")
|
||||||
|
fun shouldNotNotifySenderAgainForRepeatedDeliveryConfirmation() {
|
||||||
|
val fixture = GiftApplicationFixture()
|
||||||
|
fixture.prepareGiftForDeliveryConfirmation()
|
||||||
|
fixture.service.confirmDelivery(fixture.creator, APPLICATION_NO)
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
fixture.service.confirmDelivery(fixture.creator, APPLICATION_NO)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("common.error.invalid_request", exception.messageKey)
|
||||||
|
assertEquals(GiftStatus.DELIVERED, fixture.deliveryConfirmationGift.status)
|
||||||
|
assertEquals(NOW, fixture.deliveryConfirmationDelivery.deliveredAt)
|
||||||
|
Mockito.verify(fixture.giftPushService).sendDeliveredToSender(APPLICATION_NO, SENDER_ID)
|
||||||
|
Mockito.verifyNoMoreInteractions(fixture.giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -309,6 +328,7 @@ class GiftCommandServiceTest {
|
|||||||
assertEquals("common.error.invalid_request", exception.messageKey)
|
assertEquals("common.error.invalid_request", exception.messageKey)
|
||||||
assertEquals(scenario.status, fixture.deliveryConfirmationGift.status)
|
assertEquals(scenario.status, fixture.deliveryConfirmationGift.status)
|
||||||
assertEquals(null, fixture.deliveryConfirmationDelivery.deliveredAt)
|
assertEquals(null, fixture.deliveryConfirmationDelivery.deliveredAt)
|
||||||
|
Mockito.verifyNoInteractions(fixture.giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
|||||||
+9
@@ -415,6 +415,8 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(GiftStatus.UNDELIVERABLE, gift.status)
|
assertEquals(GiftStatus.UNDELIVERABLE, gift.status)
|
||||||
assertEquals(now, delivery.undeliverableAt)
|
assertEquals(now, delivery.undeliverableAt)
|
||||||
assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason)
|
assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason)
|
||||||
|
Mockito.verify(giftPushService).sendUndeliverableToSender(APPLICATION_NO, SENDER_MEMBER_ID)
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -437,7 +439,9 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(GiftStatus.UNDELIVERABLE, gift.status)
|
assertEquals(GiftStatus.UNDELIVERABLE, gift.status)
|
||||||
assertEquals(now, delivery.undeliverableAt)
|
assertEquals(now, delivery.undeliverableAt)
|
||||||
assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason)
|
assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason)
|
||||||
|
Mockito.verify(giftPushService).sendUndeliverableToSender(gift.applicationNo, SENDER_MEMBER_ID)
|
||||||
}
|
}
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -456,6 +460,7 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(GiftStatus.TRACKING_REGISTERED, gift.status)
|
assertEquals(GiftStatus.TRACKING_REGISTERED, gift.status)
|
||||||
assertEquals(null, delivery.undeliverableAt)
|
assertEquals(null, delivery.undeliverableAt)
|
||||||
assertEquals(null, delivery.undeliverableReason)
|
assertEquals(null, delivery.undeliverableReason)
|
||||||
|
Mockito.verifyNoInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -475,6 +480,7 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(GiftStatus.TRACKING_REGISTERED, gift.status)
|
assertEquals(GiftStatus.TRACKING_REGISTERED, gift.status)
|
||||||
assertEquals(null, delivery.undeliverableAt)
|
assertEquals(null, delivery.undeliverableAt)
|
||||||
assertEquals(null, delivery.undeliverableReason)
|
assertEquals(null, delivery.undeliverableReason)
|
||||||
|
Mockito.verifyNoInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -515,6 +521,7 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(null, delivery.undeliverableAt)
|
assertEquals(null, delivery.undeliverableAt)
|
||||||
assertEquals(null, delivery.undeliverableReason)
|
assertEquals(null, delivery.undeliverableReason)
|
||||||
}
|
}
|
||||||
|
Mockito.verifyNoInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -533,6 +540,8 @@ class GiftSchedulerTest @Autowired constructor(
|
|||||||
assertEquals(1, firstUpdatedCount)
|
assertEquals(1, firstUpdatedCount)
|
||||||
assertEquals(0, secondUpdatedCount)
|
assertEquals(0, secondUpdatedCount)
|
||||||
assertEquals(now, delivery.undeliverableAt)
|
assertEquals(now, delivery.undeliverableAt)
|
||||||
|
Mockito.verify(giftPushService).sendUndeliverableToSender(APPLICATION_NO, SENDER_MEMBER_ID)
|
||||||
|
Mockito.verifyNoMoreInteractions(giftPushService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user