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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,15 @@ private fun AfternoteMemorialDto.toDraftMemorialContent() =
media = toMemorialMedia(),
)

/**
* 미디어 URL 의 빈 값은 여기서 한 번만 `null` 로 맞춘다. 앱 안에서는 `null` 만 「없음」이다.
* BE `main` 은 빈 미디어를 `null` 로 저장하므로 빈 문자열은 옛 데이터에서만 온다.
*/
private fun AfternoteMemorialDto.toMemorialMedia() =
MemorialMedia(
photoUrl = memorialPhotoUrl,
videoUrl = memorialVideo?.videoUrl,
thumbnailUrl = memorialVideo?.thumbnailUrl,
photoUrl = memorialPhotoUrl?.ifBlank { null },
videoUrl = memorialVideo?.videoUrl?.ifBlank { null },
thumbnailUrl = memorialVideo?.thumbnailUrl?.ifBlank { null },
)

private fun AfternoteDetailReceiverDto.toDomain(): DetailReceiver? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import org.junit.Test
* [AfternoteDetailDto.toDomain] 회귀 가드 (작성자 상세).
* 핵심 경계: 공통 필드와 타입별 [DetailContent] 분리, 부분·부재 credentials 의 빈 값 강등,
* receivers null→emptyList, receiver 필드 null→"", processingMethods null→emptyList,
* memorialVideo null→video/thumbnail null.
* memorialVideo null→video/thumbnail null, 미디어 URL 빈 문자열→null.
*/
class AfternoteDetailMapperTest {
@Test
Expand Down Expand Up @@ -231,6 +231,30 @@ class AfternoteDetailMapperTest {
assertEquals("a", memorial.songs.single().artist)
}

@Test
fun `toDomain - 미디어 URL 의 빈 문자열은 없음으로 맞춘다`() {
val result =
Json
.decodeFromString<AfternoteDetailDto>(
"""
{
"afternoteId":1,"category":"PLAYLIST","title":"t","isDraft":false,
"updatedAt":"$UPDATED_AT","receivers":[],
"playlist":{
"memorialPhotoUrl":"",
"songs":[],
"memorialVideo":{"videoUrl":" ","thumbnailUrl":""}
}
}
""".trimIndent(),
).toDomain()

val media = (result.content as DetailContent.Memorial).media
assertNull(media.photoUrl)
assertNull(media.videoUrl)
assertNull(media.thumbnailUrl)
}

@Test
fun `toDomain - playlist의 공통 leaveMessage도 보존한다`() {
val result =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ internal object AfternoteEditorFormMapper {
memorialThumbnailUrl: String? = null,
): MemorialWritePayload =
MemorialWritePayload(
memorialPhotoUrl = memorialPhotoUrl?.ifBlank { null },
memorialPhotoUrl = memorialPhotoUrl,
songs = playlistSongs.map { it.toMemorialSongPayload() },
memorialVideo = memorialVideoPayload(videoUrl = memorialVideoUrl, thumbnailUrl = memorialThumbnailUrl),
)
Expand Down Expand Up @@ -368,7 +368,7 @@ internal object AfternoteEditorFormMapper {
MemorialSnapshot(
title = title,
leaveMessageBlocks = leaveMessageBlocks,
photoUrl = content.media.photoUrl?.ifBlank { null },
photoUrl = content.media.photoUrl,
video = content.media.toVideoPayload(),
songs =
content.songs.map { song ->
Expand Down Expand Up @@ -428,7 +428,7 @@ internal object AfternoteEditorFormMapper {
MemorialSnapshot(
title = title,
leaveMessageBlocks = leaveMessageBlocks,
photoUrl = memorialMediaUrls.memorialPhotoUrl?.ifBlank { null },
photoUrl = memorialMediaUrls.memorialPhotoUrl,
video = memorialMediaUrls.toVideoPayload(),
songs = playlistSongs.map { it.toMemorialSongPayload() },
)
Expand All @@ -455,14 +455,14 @@ internal object AfternoteEditorFormMapper {
private fun MemorialMediaUrls.toVideoPayload(): MemorialVideoPayload? =
memorialVideoPayload(videoUrl = memorialVideoUrl, thumbnailUrl = memorialThumbnailUrl)

/** 영상 주소가 비었으면 영상 슬롯 자체가 없다. 썸네일만 남은 값은 싣지 않는다. */
/**
* 영상 주소가 없으면 영상 슬롯 자체가 없다. 썸네일만 남은 값은 싣지 않는다.
* 빈 문자열은 입구(상세 data 매퍼, 폼의 `MediaInput` 판정과 영상 첨부)에서 이미 `null` 로 맞춰져 들어온다.
*/
private fun memorialVideoPayload(
videoUrl: String?,
thumbnailUrl: String?,
): MemorialVideoPayload? =
videoUrl?.ifBlank { null }?.let { url ->
MemorialVideoPayload(videoUrl = url, thumbnailUrl = thumbnailUrl?.ifBlank { null })
}
): MemorialVideoPayload? = videoUrl?.let { url -> MemorialVideoPayload(videoUrl = url, thumbnailUrl = thumbnailUrl) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ internal sealed class EditableMemorialVideo {
/** 새 선택으로 교체한다. 이전 선택의 썸네일은 물려주지 않는다. 빈 문자열은 [MemorialVideoAttachment.ofOrNull] 규칙대로 첨부 없음이다. */
internal fun withSelection(url: String): EditableMemorialVideo = MemorialVideoAttachment.ofOrNull(url)?.let(::PendingUpload) ?: NoVideo

/** 미저장 선택에서 파생된 썸네일만 갱신한다. 선택이 사라졌다면 늦은 결과를 버린다. */
/** 미저장 선택에서 파생된 썸네일만 갱신한다. 선택이 사라졌다면 늦은 결과를 버린다. 빈 문자열은 썸네일 없음이다. */
internal fun withSelectionThumbnail(url: String): EditableMemorialVideo =
when (this) {
NoVideo -> this
is Uploaded -> this
is PendingUpload -> PendingUpload(video.copy(thumbnailUrl = url))
is PendingUpload -> PendingUpload(video.copy(thumbnailUrl = url.ifBlank { null }))
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ class EditableMemorialVideoTest {
assertEquals(serverVideo, afterLateThumbnail.displayed)
}

@Test
fun `빈 썸네일 결과는 썸네일 없음이다`() {
val video =
EditableMemorialVideo
.empty()
.withSelection(pickedVideo.url)
.withSelectionThumbnail("")

assertEquals(MemorialVideoAttachment(url = pickedVideo.url, thumbnailUrl = null), video.displayed)
}

@Test
fun `썸네일을 떼도 출처는 남는다`() {
assertEquals(
Expand Down
Loading