From 5f6b8d4cf5f735738437737c41eb26c170662110 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 29 Oct 2026 23:20:21 +0900 Subject: [PATCH] =?UTF-8?q?feat(subbanner):=20=EC=84=9C=EB=B8=8C=20?= =?UTF-8?q?=EB=B0=B0=EB=84=88=20=EB=8F=84=EB=A9=94=EC=9D=B8=EA=B3=BC=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EB=B6=88=EB=B3=80=EC=8B=9D=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/v2/subbanner/domain/SubBanner.kt | 20 +++++++ .../v2/subbanner/domain/SubBannerPlacement.kt | 6 +++ .../v2/subbanner/domain/SubBannerTest.kt | 53 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBanner.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerPlacement.kt create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerTest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBanner.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBanner.kt new file mode 100644 index 00000000..c60a081a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBanner.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.v2.subbanner.domain + +import java.time.LocalDateTime + +data class SubBanner( + val id: Long? = null, + val placement: SubBannerPlacement, + val imagePath: String, + val webUrl: String?, + val androidDeepLink: String?, + val iosDeepLink: String?, + val startDate: LocalDateTime?, + val endDate: LocalDateTime? +) { + init { + require(!webUrl.isNullOrBlank() || !androidDeepLink.isNullOrBlank() || !iosDeepLink.isNullOrBlank()) { + "At least one link must be nonblank" + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerPlacement.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerPlacement.kt new file mode 100644 index 00000000..c562ea1f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerPlacement.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.v2.subbanner.domain + +enum class SubBannerPlacement { + HOME_RECOMMENDATION, + CONTENT_RECOMMENDATION +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerTest.kt new file mode 100644 index 00000000..e7905a8c --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/subbanner/domain/SubBannerTest.kt @@ -0,0 +1,53 @@ +package kr.co.vividnext.sodalive.v2.subbanner.domain + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class SubBannerTest { + @Test + @DisplayName("세 링크 중 하나 이상이 있는 7개 조합은 허용하고 모두 null이면 거부한다") + fun shouldRejectAllEmptyLinksAndAllowSevenNonEmptyCombinations() { + for (mask in 1..7) { + val webUrl = if (mask and 1 != 0) "https://example.com/banner?next=%2Fhome" else null + val androidDeepLink = if (mask and 2 != 0) "sodalive://content/123?from=banner" else null + val iosDeepLink = if (mask and 4 != 0) "sodalive-ios://content/123#detail" else null + + val banner = banner(webUrl, androidDeepLink, iosDeepLink) + + assertEquals(webUrl, banner.webUrl) + assertEquals(androidDeepLink, banner.androidDeepLink) + assertEquals(iosDeepLink, banner.iosDeepLink) + } + + assertThrows(IllegalArgumentException::class.java) { banner(null, null, null) } + } + + @Test + @DisplayName("null과 빈 문자열 및 공백만으로 된 모든 조합을 거부한다") + fun shouldRejectAllBlankLinksAndAllowAnyNonblankLink() { + val emptyLinks = listOf(null, "", " ", "\t\n") + for (webUrl in emptyLinks) { + for (androidDeepLink in emptyLinks) { + for (iosDeepLink in emptyLinks) { + assertThrows(IllegalArgumentException::class.java) { banner(webUrl, androidDeepLink, iosDeepLink) } + } + } + } + + assertEquals("https://example.com", banner("https://example.com", " ", "").webUrl) + assertEquals("sodalive://home", banner(" ", "sodalive://home", "").androidDeepLink) + assertEquals("sodalive-ios://home", banner("", " ", "sodalive-ios://home").iosDeepLink) + } + + private fun banner(webUrl: String?, androidDeepLink: String?, iosDeepLink: String?): SubBanner = SubBanner( + placement = SubBannerPlacement.HOME_RECOMMENDATION, + imagePath = "sub-banner/image.png", + webUrl = webUrl, + androidDeepLink = androidDeepLink, + iosDeepLink = iosDeepLink, + startDate = null, + endDate = null + ) +}