feat(gift): 선물 응답 시각을 UTC 문자열로 변환한다

This commit is contained in:
2026-09-30 17:38:51 +09:00
parent 2bda30e7e2
commit 22c6cd2d38
4 changed files with 103 additions and 44 deletions
@@ -1,23 +1,29 @@
package kr.co.vividnext.sodalive.v2.api.admin.gift.dto package kr.co.vividnext.sodalive.v2.api.admin.gift.dto
import kr.co.vividnext.sodalive.extensions.toUtcIso
import kr.co.vividnext.sodalive.v2.gift.application.GiftCategoryResult import kr.co.vividnext.sodalive.v2.gift.application.GiftCategoryResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftOperationStatusResult import kr.co.vividnext.sodalive.v2.gift.application.GiftOperationStatusResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftSizePriceResult import kr.co.vividnext.sodalive.v2.gift.application.GiftSizePriceResult
import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus
import java.time.LocalDateTime
data class AdminGiftCategoryResponse( data class AdminGiftCategoryResponse(
val categoryId: Long, val categoryId: Long,
val classificationNumber: String,
val categoryCode: String, val categoryCode: String,
val name: String, val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean, val requiresDamageWaiver: Boolean,
val isActive: Boolean val isActive: Boolean
) { ) {
companion object { companion object {
fun from(result: GiftCategoryResult) = AdminGiftCategoryResponse( fun from(result: GiftCategoryResult) = AdminGiftCategoryResponse(
categoryId = result.categoryId, categoryId = result.categoryId,
classificationNumber = result.classificationNumber,
categoryCode = result.categoryCode, categoryCode = result.categoryCode,
name = result.name, name = result.name,
receiptCode = result.receiptCode,
representativeItem = result.representativeItem,
requiresDamageWaiver = result.requiresDamageWaiver, requiresDamageWaiver = result.requiresDamageWaiver,
isActive = result.isActive isActive = result.isActive
) )
@@ -46,14 +52,14 @@ data class AdminGiftOperationStatusResponse(
val applicationNo: String, val applicationNo: String,
val status: GiftStatus, val status: GiftStatus,
val statusName: String, val statusName: String,
val occurredAt: LocalDateTime val occurredAt: String
) { ) {
companion object { companion object {
fun from(result: GiftOperationStatusResult) = AdminGiftOperationStatusResponse( fun from(result: GiftOperationStatusResult) = AdminGiftOperationStatusResponse(
applicationNo = result.applicationNo, applicationNo = result.applicationNo,
status = result.status, status = result.status,
statusName = result.statusName, statusName = result.statusName,
occurredAt = result.occurredAt occurredAt = result.occurredAt.toUtcIso()
) )
} }
} }
@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.v2.api.gift.dto package kr.co.vividnext.sodalive.v2.api.gift.dto
import kr.co.vividnext.sodalive.extensions.toUtcIso
import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftCancellationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftCancellationResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftDeliveryConfirmationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftDeliveryConfirmationResult
@@ -9,7 +10,6 @@ import kr.co.vividnext.sodalive.v2.gift.application.GiftListResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftReviewResult import kr.co.vividnext.sodalive.v2.gift.application.GiftReviewResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationResult
import java.time.LocalDateTime
data class GiftFormOptionsResponse( data class GiftFormOptionsResponse(
val sizes: List<GiftSizeOptionResponse>, val sizes: List<GiftSizeOptionResponse>,
@@ -59,7 +59,7 @@ data class GiftListResponse(
priceCan = it.priceCan, priceCan = it.priceCan,
categoryName = it.categoryName, categoryName = it.categoryName,
sizeName = it.sizeName, sizeName = it.sizeName,
createdAt = it.createdAt createdAt = it.createdAt?.toUtcIso()
) )
}, },
page = result.page, page = result.page,
@@ -77,7 +77,7 @@ data class GiftListItemResponse(
val priceCan: Int, val priceCan: Int,
val categoryName: String, val categoryName: String,
val sizeName: String, val sizeName: String,
val createdAt: LocalDateTime? val createdAt: String?
) )
data class GiftDetailResponse( data class GiftDetailResponse(
@@ -91,7 +91,7 @@ data class GiftDetailResponse(
val mailbox: GiftMailboxResponse?, val mailbox: GiftMailboxResponse?,
val trackingRequired: Boolean, val trackingRequired: Boolean,
val recipientAddressRequired: Boolean, val recipientAddressRequired: Boolean,
val recipientAddressDeadlineAt: LocalDateTime?, val recipientAddressDeadlineAt: String?,
val delivery: GiftDeliveryInfoResponse, val delivery: GiftDeliveryInfoResponse,
val statusTimeline: List<GiftStatusTimelineResponse> val statusTimeline: List<GiftStatusTimelineResponse>
) { ) {
@@ -109,21 +109,21 @@ data class GiftDetailResponse(
applicationNo = result.giftInfo.applicationNo, applicationNo = result.giftInfo.applicationNo,
paidCan = result.giftInfo.paidCan, paidCan = result.giftInfo.paidCan,
tracking = result.giftInfo.tracking, tracking = result.giftInfo.tracking,
shippingRequestedAt = result.giftInfo.shippingRequestedAt shippingRequestedAt = result.giftInfo.shippingRequestedAt?.toUtcIso()
), ),
senderInfo = result.senderInfo?.let { GiftAddressResponse(it.name, it.phoneNumber, it.address) }, senderInfo = result.senderInfo?.let { GiftAddressResponse(it.name, it.phoneNumber, it.address) },
recipientAddress = result.recipientAddress?.let { GiftAddressResponse(it.name, it.phoneNumber, it.address) }, recipientAddress = result.recipientAddress?.let { GiftAddressResponse(it.name, it.phoneNumber, it.address) },
mailbox = result.mailbox?.let { GiftMailboxResponse(it.name, it.address, it.phoneNumber) }, mailbox = result.mailbox?.let { GiftMailboxResponse(it.name, it.address, it.phoneNumber) },
trackingRequired = result.trackingRequired, trackingRequired = result.trackingRequired,
recipientAddressRequired = result.recipientAddressRequired, recipientAddressRequired = result.recipientAddressRequired,
recipientAddressDeadlineAt = result.recipientAddressDeadlineAt, recipientAddressDeadlineAt = result.recipientAddressDeadlineAt?.toUtcIso(),
delivery = GiftDeliveryInfoResponse( delivery = GiftDeliveryInfoResponse(
canceledAt = result.delivery.canceledAt, canceledAt = result.delivery.canceledAt?.toUtcIso(),
undeliverableAt = result.delivery.undeliverableAt, undeliverableAt = result.delivery.undeliverableAt?.toUtcIso(),
undeliverableReason = result.delivery.undeliverableReason undeliverableReason = result.delivery.undeliverableReason
), ),
statusTimeline = result.statusTimeline.map { statusTimeline = result.statusTimeline.map {
GiftStatusTimelineResponse(it.status.name, it.statusName, it.occurredAt) GiftStatusTimelineResponse(it.status.name, it.statusName, it.occurredAt?.toUtcIso())
} }
) )
} }
@@ -137,7 +137,7 @@ data class GiftDetailInfoResponse(
val applicationNo: String?, val applicationNo: String?,
val paidCan: Int?, val paidCan: Int?,
val tracking: String?, val tracking: String?,
val shippingRequestedAt: LocalDateTime? val shippingRequestedAt: String?
) )
data class GiftAddressResponse( data class GiftAddressResponse(
@@ -153,15 +153,15 @@ data class GiftMailboxResponse(
) )
data class GiftDeliveryInfoResponse( data class GiftDeliveryInfoResponse(
val canceledAt: LocalDateTime?, val canceledAt: String?,
val undeliverableAt: LocalDateTime?, val undeliverableAt: String?,
val undeliverableReason: String? val undeliverableReason: String?
) )
data class GiftStatusTimelineResponse( data class GiftStatusTimelineResponse(
val status: String, val status: String,
val statusName: String, val statusName: String,
val occurredAt: LocalDateTime? val occurredAt: String?
) )
data class GiftApplicationResponse( data class GiftApplicationResponse(
@@ -169,7 +169,7 @@ data class GiftApplicationResponse(
val status: String, val status: String,
val statusName: String, val statusName: String,
val priceCan: Int, val priceCan: Int,
val trackingDeadlineAt: LocalDateTime val trackingDeadlineAt: String
) { ) {
companion object { companion object {
fun from(result: GiftApplicationResult) = GiftApplicationResponse( fun from(result: GiftApplicationResult) = GiftApplicationResponse(
@@ -177,7 +177,7 @@ data class GiftApplicationResponse(
status = result.status.name, status = result.status.name,
statusName = result.statusName, statusName = result.statusName,
priceCan = result.priceCan, priceCan = result.priceCan,
trackingDeadlineAt = result.trackingDeadlineAt trackingDeadlineAt = result.trackingDeadlineAt.toUtcIso()
) )
} }
} }
@@ -187,7 +187,7 @@ data class GiftCancellationResponse(
val status: String, val status: String,
val statusName: String, val statusName: String,
val priceCan: Int, val priceCan: Int,
val canceledAt: LocalDateTime val canceledAt: String
) { ) {
companion object { companion object {
fun from(result: GiftCancellationResult) = GiftCancellationResponse( fun from(result: GiftCancellationResult) = GiftCancellationResponse(
@@ -195,7 +195,7 @@ data class GiftCancellationResponse(
status = result.status.name, status = result.status.name,
statusName = result.statusName, statusName = result.statusName,
priceCan = result.priceCan, priceCan = result.priceCan,
canceledAt = result.canceledAt canceledAt = result.canceledAt.toUtcIso()
) )
} }
} }
@@ -206,8 +206,8 @@ data class GiftTrackingRegistrationResponse(
val statusName: String, val statusName: String,
val courierCompanyName: String, val courierCompanyName: String,
val trackingNumber: String, val trackingNumber: String,
val trackingRegisteredAt: LocalDateTime, val trackingRegisteredAt: String,
val recipientAddressDeadlineAt: LocalDateTime val recipientAddressDeadlineAt: String
) { ) {
companion object { companion object {
fun from(result: GiftTrackingRegistrationResult) = GiftTrackingRegistrationResponse( fun from(result: GiftTrackingRegistrationResult) = GiftTrackingRegistrationResponse(
@@ -216,8 +216,8 @@ data class GiftTrackingRegistrationResponse(
statusName = result.statusName, statusName = result.statusName,
courierCompanyName = result.courierCompanyName, courierCompanyName = result.courierCompanyName,
trackingNumber = result.trackingNumber, trackingNumber = result.trackingNumber,
trackingRegisteredAt = result.trackingRegisteredAt, trackingRegisteredAt = result.trackingRegisteredAt.toUtcIso(),
recipientAddressDeadlineAt = result.recipientAddressDeadlineAt recipientAddressDeadlineAt = result.recipientAddressDeadlineAt.toUtcIso()
) )
} }
} }
@@ -226,14 +226,14 @@ data class GiftRecipientAddressRegistrationResponse(
val applicationNo: String, val applicationNo: String,
val status: String, val status: String,
val statusName: String, val statusName: String,
val recipientAddressRegisteredAt: LocalDateTime val recipientAddressRegisteredAt: String
) { ) {
companion object { companion object {
fun from(result: GiftRecipientAddressRegistrationResult) = GiftRecipientAddressRegistrationResponse( fun from(result: GiftRecipientAddressRegistrationResult) = GiftRecipientAddressRegistrationResponse(
applicationNo = result.applicationNo, applicationNo = result.applicationNo,
status = result.status.name, status = result.status.name,
statusName = result.statusName, statusName = result.statusName,
recipientAddressRegisteredAt = result.recipientAddressRegisteredAt recipientAddressRegisteredAt = result.recipientAddressRegisteredAt.toUtcIso()
) )
} }
} }
@@ -242,14 +242,14 @@ data class GiftDeliveryConfirmationResponse(
val applicationNo: String, val applicationNo: String,
val status: String, val status: String,
val statusName: String, val statusName: String,
val deliveredAt: LocalDateTime val deliveredAt: String
) { ) {
companion object { companion object {
fun from(result: GiftDeliveryConfirmationResult) = GiftDeliveryConfirmationResponse( fun from(result: GiftDeliveryConfirmationResult) = GiftDeliveryConfirmationResponse(
applicationNo = result.applicationNo, applicationNo = result.applicationNo,
status = result.status.name, status = result.status.name,
statusName = result.statusName, statusName = result.statusName,
deliveredAt = result.deliveredAt deliveredAt = result.deliveredAt.toUtcIso()
) )
} }
} }
@@ -260,7 +260,7 @@ data class GiftReviewResponse(
val rating: Int, val rating: Int,
val keywords: List<String>, val keywords: List<String>,
val comment: String?, val comment: String?,
val createdAt: LocalDateTime? val createdAt: String?
) { ) {
companion object { companion object {
fun from(result: GiftReviewResult) = GiftReviewResponse( fun from(result: GiftReviewResult) = GiftReviewResponse(
@@ -269,7 +269,7 @@ data class GiftReviewResponse(
rating = result.rating, rating = result.rating,
keywords = result.keywords, keywords = result.keywords,
comment = result.comment, comment = result.comment,
createdAt = result.createdAt createdAt = result.createdAt?.toUtcIso()
) )
} }
} }
@@ -10,6 +10,7 @@ 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.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
import org.hamcrest.Matchers.endsWith
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -62,13 +63,24 @@ class AdminGiftControllerTest @Autowired constructor(
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content( .content(
""" """
{"categoryCode":"DOLL","name":"인형","requiresDamageWaiver":true,"isActive":true} {
"classificationNumber":"100",
"categoryCode":"DOLL",
"name":"인형",
"receiptCode":"A",
"representativeItem":"피규어",
"requiresDamageWaiver":true,
"isActive":true
}
""".trimIndent() """.trimIndent()
) )
) )
.andExpect(status().isOk) .andExpect(status().isOk)
.andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.classificationNumber").value("100"))
.andExpect(jsonPath("$.data.categoryCode").value("DOLL")) .andExpect(jsonPath("$.data.categoryCode").value("DOLL"))
.andExpect(jsonPath("$.data.receiptCode").value("A"))
.andExpect(jsonPath("$.data.representativeItem").value("피규어"))
val categoryId = categoryRepository.findByCategoryCode("DOLL")!!.id!! val categoryId = categoryRepository.findByCategoryCode("DOLL")!!.id!!
@@ -78,12 +90,23 @@ class AdminGiftControllerTest @Autowired constructor(
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content( .content(
""" """
{"categoryCode":"DOLL","name":"큰 인형","requiresDamageWaiver":false,"isActive":true} {
"classificationNumber":"101",
"categoryCode":"DOLL",
"name":"큰 인형",
"receiptCode":"B",
"representativeItem":"대형 피규어",
"requiresDamageWaiver":false,
"isActive":true
}
""".trimIndent() """.trimIndent()
) )
) )
.andExpect(status().isOk) .andExpect(status().isOk)
.andExpect(jsonPath("$.data.classificationNumber").value("101"))
.andExpect(jsonPath("$.data.name").value("큰 인형")) .andExpect(jsonPath("$.data.name").value("큰 인형"))
.andExpect(jsonPath("$.data.receiptCode").value("B"))
.andExpect(jsonPath("$.data.representativeItem").value("대형 피규어"))
mockMvc.perform(get("/api/v2/admin/gift-categories").with(user("admin").roles("ADMIN"))) mockMvc.perform(get("/api/v2/admin/gift-categories").with(user("admin").roles("ADMIN")))
.andExpect(status().isOk) .andExpect(status().isOk)
@@ -132,11 +155,13 @@ class AdminGiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO)) .andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO))
.andExpect(jsonPath("$.data.status").value("ARRIVED_AT_MAILBOX")) .andExpect(jsonPath("$.data.status").value("ARRIVED_AT_MAILBOX"))
.andExpect(jsonPath("$.data.occurredAt").value(endsWith("Z")))
mockMvc.perform(post("/api/v2/admin/gifts/$APPLICATION_NO/complete-inspection").with(user("admin").roles("ADMIN"))) mockMvc.perform(post("/api/v2/admin/gifts/$APPLICATION_NO/complete-inspection").with(user("admin").roles("ADMIN")))
.andExpect(status().isOk) .andExpect(status().isOk)
.andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.status").value("INSPECTION_COMPLETED")) .andExpect(jsonPath("$.data.status").value("INSPECTION_COMPLETED"))
.andExpect(jsonPath("$.data.occurredAt").value(endsWith("Z")))
} }
@Test @Test
@@ -154,6 +179,7 @@ class AdminGiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO)) .andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO))
.andExpect(jsonPath("$.data.status").value("UNDELIVERABLE")) .andExpect(jsonPath("$.data.status").value("UNDELIVERABLE"))
.andExpect(jsonPath("$.data.occurredAt").value(endsWith("Z")))
} }
@Test @Test
@@ -181,6 +207,7 @@ class AdminGiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO)) .andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO))
.andExpect(jsonPath("$.data.status").value("DELIVERED")) .andExpect(jsonPath("$.data.status").value("DELIVERED"))
.andExpect(jsonPath("$.data.occurredAt").value(endsWith("Z")))
} }
private fun saveGift(status: GiftStatus) { private fun saveGift(status: GiftStatus) {
@@ -26,6 +26,7 @@ import kr.co.vividnext.sodalive.v2.gift.application.GiftReviewResult
import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationResult import kr.co.vividnext.sodalive.v2.gift.application.GiftTrackingRegistrationResult
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
import org.hamcrest.Matchers.endsWith
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mockito import org.mockito.Mockito
@@ -64,8 +65,8 @@ class GiftControllerTest @Autowired constructor(
@Test @Test
fun `팬은 활성 폼 옵션을 조회하고 categoryCode를 받지 않는다`() { fun `팬은 활성 폼 옵션을 조회하고 categoryCode를 받지 않는다`() {
categoryRepository.save(GiftCategory("DOLL", "인형", true, true)) categoryRepository.save(giftCategory("100", "DOLL", "A", "인형", "피규어", true, true))
categoryRepository.save(GiftCategory("FOOD", "간식", false, false)) categoryRepository.save(giftCategory("200", "FOOD", "B", "간식", "쿠키", false, false))
sizePriceRepository.save(GiftSizePrice(GiftSize.SMALL, 100, 80, true)) sizePriceRepository.save(GiftSizePrice(GiftSize.SMALL, 100, 80, true))
mockMvc.perform(get("/api/v2/gifts/form-options").with(user("fan").roles("USER"))) mockMvc.perform(get("/api/v2/gifts/form-options").with(user("fan").roles("USER")))
@@ -75,8 +76,11 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.sizes[0].name").value("소형")) .andExpect(jsonPath("$.data.sizes[0].name").value("소형"))
.andExpect(jsonPath("$.data.sizes[0].basePriceCan").value(100)) .andExpect(jsonPath("$.data.sizes[0].basePriceCan").value(100))
.andExpect(jsonPath("$.data.sizes[0].salePriceCan").value(80)) .andExpect(jsonPath("$.data.sizes[0].salePriceCan").value(80))
.andExpect(jsonPath("$.data.categories[0].name").value("인형")) .andExpect(jsonPath("$.data.categories[0].name").value("인형/피규어"))
.andExpect(jsonPath("$.data.categories[0].categoryCode").doesNotExist()) .andExpect(jsonPath("$.data.categories[0].categoryCode").doesNotExist())
.andExpect(jsonPath("$.data.categories[0].classificationNumber").doesNotExist())
.andExpect(jsonPath("$.data.categories[0].receiptCode").doesNotExist())
.andExpect(jsonPath("$.data.categories[0].representativeItem").doesNotExist())
} }
@Test @Test
@@ -113,7 +117,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.items[0].priceCan").value(80)) .andExpect(jsonPath("$.data.items[0].priceCan").value(80))
.andExpect(jsonPath("$.data.items[0].categoryName").value("인형")) .andExpect(jsonPath("$.data.items[0].categoryName").value("인형"))
.andExpect(jsonPath("$.data.items[0].sizeName").value("소형")) .andExpect(jsonPath("$.data.items[0].sizeName").value("소형"))
.andExpect(jsonPath("$.data.items[0].createdAt").exists()) .andExpect(jsonPath("$.data.items[0].createdAt").value(endsWith("Z")))
} }
@Test @Test
@@ -161,7 +165,11 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.recipientAddress").isEmpty) .andExpect(jsonPath("$.data.recipientAddress").isEmpty)
.andExpect(jsonPath("$.data.trackingRequired").value(false)) .andExpect(jsonPath("$.data.trackingRequired").value(false))
.andExpect(jsonPath("$.data.statusTimeline[0].status").value("RECEIVED")) .andExpect(jsonPath("$.data.statusTimeline[0].status").value("RECEIVED"))
.andExpect(jsonPath("$.data.statusTimeline[1].occurredAt").value("2026-09-30T12:00:00")) .andExpect(jsonPath("$.data.recipientAddressDeadlineAt").isEmpty)
.andExpect(jsonPath("$.data.giftInfo.shippingRequestedAt").isEmpty)
.andExpect(jsonPath("$.data.delivery.canceledAt").isEmpty)
.andExpect(jsonPath("$.data.delivery.undeliverableAt").isEmpty)
.andExpect(jsonPath("$.data.statusTimeline[1].occurredAt").value("2026-09-30T12:00:00Z"))
} }
@Test @Test
@@ -209,7 +217,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.status").value("RECEIVED")) .andExpect(jsonPath("$.data.status").value("RECEIVED"))
.andExpect(jsonPath("$.data.statusName").value("접수 완료")) .andExpect(jsonPath("$.data.statusName").value("접수 완료"))
.andExpect(jsonPath("$.data.priceCan").value(80)) .andExpect(jsonPath("$.data.priceCan").value(80))
.andExpect(jsonPath("$.data.trackingDeadlineAt").value("2026-10-02T12:00:00")) .andExpect(jsonPath("$.data.trackingDeadlineAt").value("2026-10-02T12:00:00Z"))
} }
@Test @Test
@@ -240,7 +248,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.status").value("CANCELED")) .andExpect(jsonPath("$.data.status").value("CANCELED"))
.andExpect(jsonPath("$.data.statusName").value("신청 취소")) .andExpect(jsonPath("$.data.statusName").value("신청 취소"))
.andExpect(jsonPath("$.data.priceCan").value(80)) .andExpect(jsonPath("$.data.priceCan").value(80))
.andExpect(jsonPath("$.data.canceledAt").value("2026-09-29T12:00:00")) .andExpect(jsonPath("$.data.canceledAt").value("2026-09-29T12:00:00Z"))
} }
@Test @Test
@@ -281,8 +289,8 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.statusName").value("발송 확인")) .andExpect(jsonPath("$.data.statusName").value("발송 확인"))
.andExpect(jsonPath("$.data.courierCompanyName").value("CJ대한통운")) .andExpect(jsonPath("$.data.courierCompanyName").value("CJ대한통운"))
.andExpect(jsonPath("$.data.trackingNumber").value("1234567890")) .andExpect(jsonPath("$.data.trackingNumber").value("1234567890"))
.andExpect(jsonPath("$.data.trackingRegisteredAt").value("2026-09-29T12:00:00")) .andExpect(jsonPath("$.data.trackingRegisteredAt").value("2026-09-29T12:00:00Z"))
.andExpect(jsonPath("$.data.recipientAddressDeadlineAt").value("2026-10-06T12:00:00")) .andExpect(jsonPath("$.data.recipientAddressDeadlineAt").value("2026-10-06T12:00:00Z"))
} }
@Test @Test
@@ -324,7 +332,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO)) .andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO))
.andExpect(jsonPath("$.data.status").value("TRACKING_REGISTERED")) .andExpect(jsonPath("$.data.status").value("TRACKING_REGISTERED"))
.andExpect(jsonPath("$.data.statusName").value("발송 확인")) .andExpect(jsonPath("$.data.statusName").value("발송 확인"))
.andExpect(jsonPath("$.data.recipientAddressRegisteredAt").value("2026-09-29T12:00:00")) .andExpect(jsonPath("$.data.recipientAddressRegisteredAt").value("2026-09-29T12:00:00Z"))
} }
@Test @Test
@@ -350,7 +358,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO)) .andExpect(jsonPath("$.data.applicationNo").value(APPLICATION_NO))
.andExpect(jsonPath("$.data.status").value("DELIVERED")) .andExpect(jsonPath("$.data.status").value("DELIVERED"))
.andExpect(jsonPath("$.data.statusName").value("전달완료")) .andExpect(jsonPath("$.data.statusName").value("전달완료"))
.andExpect(jsonPath("$.data.deliveredAt").value("2026-09-29T12:00:00")) .andExpect(jsonPath("$.data.deliveredAt").value("2026-09-29T12:00:00Z"))
Mockito.verify(commandService).confirmDelivery(member, APPLICATION_NO) Mockito.verify(commandService).confirmDelivery(member, APPLICATION_NO)
} }
@@ -391,7 +399,7 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.keywords[1]").value("안내가 친절해요")) .andExpect(jsonPath("$.data.keywords[1]").value("안내가 친절해요"))
.andExpect(jsonPath("$.data.keyword").doesNotExist()) .andExpect(jsonPath("$.data.keyword").doesNotExist())
.andExpect(jsonPath("$.data.comment").value("선물 전달 과정이 만족스러웠습니다.")) .andExpect(jsonPath("$.data.comment").value("선물 전달 과정이 만족스러웠습니다."))
.andExpect(jsonPath("$.data.createdAt").value("2026-10-11T12:00:00")) .andExpect(jsonPath("$.data.createdAt").value("2026-10-11T12:00:00Z"))
Mockito.verify(commandService).writeReview(member, APPLICATION_NO, request.toCommand()) Mockito.verify(commandService).writeReview(member, APPLICATION_NO, request.toCommand())
} }
@@ -419,6 +427,24 @@ class GiftControllerTest @Autowired constructor(
) )
) )
private fun giftCategory(
classificationNumber: String,
categoryCode: String,
receiptCode: String,
name: String,
representativeItem: String,
requiresDamageWaiver: Boolean,
isActive: Boolean
) = GiftCategory(
classificationNumber = classificationNumber,
categoryCode = categoryCode,
name = name,
receiptCode = receiptCode,
representativeItem = representativeItem,
requiresDamageWaiver = requiresDamageWaiver,
isActive = isActive
)
private companion object { private companion object {
const val SENDER_ID = 1L const val SENDER_ID = 1L
const val RECIPIENT_ID = 2L const val RECIPIENT_ID = 2L