test #426

Merged
klaus merged 415 commits from test into main 2026-06-27 00:35:30 +00:00
3 changed files with 193 additions and 0 deletions
Showing only changes of commit 33b3d3e41b - Show all commits

View File

@@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.application
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.v2.api.audio.recommendation.dto.AudioRecommendationsResponse
import kr.co.vividnext.sodalive.v2.audio.recommendation.application.AudioRecommendationQueryService
import org.springframework.stereotype.Component
@Component
class AudioRecommendationFacade(
private val queryService: AudioRecommendationQueryService
) {
fun getRecommendations(member: Member?): AudioRecommendationsResponse {
return AudioRecommendationsResponse.from(queryService.getRecommendations(member))
}
}

View File

@@ -0,0 +1,99 @@
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.dto
import com.fasterxml.jackson.annotation.JsonProperty
import kr.co.vividnext.sodalive.v2.api.common.dto.RecommendationBannerResponse
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioCard
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendations
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.CommentedAudio
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.OriginalSeries
data class AudioRecommendationsResponse(
val banners: List<RecommendationBannerResponse>,
val originalSeries: List<OriginalSeriesResponse>,
val latestAudios: List<AudioCardResponse>,
val newAndHotAudios: List<AudioCardResponse>,
val freeAudios: List<AudioCardResponse>,
val pointAudios: List<AudioCardResponse>,
val mostCommentedAudios: List<CommentedAudioResponse>,
val recommendedAudios: List<AudioCardResponse>
) {
companion object {
fun from(recommendations: AudioRecommendations): AudioRecommendationsResponse {
return AudioRecommendationsResponse(
banners = recommendations.banners.map(RecommendationBannerResponse::from),
originalSeries = recommendations.originalSeries.map(OriginalSeriesResponse::from),
latestAudios = recommendations.latestAudios.map(AudioCardResponse::from),
newAndHotAudios = recommendations.newAndHotAudios.map(AudioCardResponse::from),
freeAudios = recommendations.freeAudios.map(AudioCardResponse::from),
pointAudios = recommendations.pointAudios.map(AudioCardResponse::from),
mostCommentedAudios = recommendations.mostCommentedAudios.map(CommentedAudioResponse::from),
recommendedAudios = recommendations.recommendedAudios.map(AudioCardResponse::from)
)
}
}
}
data class OriginalSeriesResponse(
val seriesId: Long,
val coverImageUrl: String?
) {
companion object {
fun from(series: OriginalSeries): OriginalSeriesResponse {
return OriginalSeriesResponse(series.seriesId, series.coverImageUrl)
}
}
}
data class AudioCardResponse(
val audioContentId: Long,
val title: String,
val duration: String?,
val imageUrl: String?,
val price: Int,
@JsonProperty("isAdult")
val isAdult: Boolean,
@JsonProperty("isPointAvailable")
val isPointAvailable: Boolean,
@JsonProperty("isFirstContent")
val isFirstContent: Boolean,
@JsonProperty("isOriginalSeries")
val isOriginalSeries: Boolean,
val creatorNickname: String
) {
companion object {
fun from(audio: AudioCard): AudioCardResponse {
return AudioCardResponse(
audioContentId = audio.audioContentId,
title = audio.title,
duration = audio.duration,
imageUrl = audio.imageUrl,
price = audio.price,
isAdult = audio.isAdult,
isPointAvailable = audio.isPointAvailable,
isFirstContent = audio.isFirstContent,
isOriginalSeries = audio.isOriginalSeries,
creatorNickname = audio.creatorNickname
)
}
}
}
data class CommentedAudioResponse(
val audioContentId: Long,
val title: String,
val imageUrl: String?,
val latestComment: String,
val latestCommentWriterProfileImageUrl: String
) {
companion object {
fun from(audio: CommentedAudio): CommentedAudioResponse {
return CommentedAudioResponse(
audioContentId = audio.audioContentId,
title = audio.title,
imageUrl = audio.imageUrl,
latestComment = audio.latestComment,
latestCommentWriterProfileImageUrl = audio.latestCommentWriterProfileImageUrl
)
}
}
}

View File

@@ -0,0 +1,79 @@
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.application
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kr.co.vividnext.sodalive.v2.audio.recommendation.application.AudioRecommendationQueryService
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioCard
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendations
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.CommentedAudio
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.OriginalSeries
import kr.co.vividnext.sodalive.v2.common.domain.RecommendationBanner
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.Mockito
class AudioRecommendationFacadeTest {
private val objectMapper = jacksonObjectMapper()
private val queryService = Mockito.mock(AudioRecommendationQueryService::class.java)
private val facade = AudioRecommendationFacade(queryService)
@Test
@DisplayName("facade는 도메인 추천 결과를 모든 공개 응답 필드로 변환한다")
fun shouldConvertDomainRecommendationsToResponse() {
Mockito.doReturn(domain()).`when`(queryService).getRecommendations(null)
val response = facade.getRecommendations(null)
assertEquals(1, response.banners.size)
assertEquals(1, response.originalSeries.size)
assertEquals(1, response.latestAudios.size)
assertEquals(1, response.newAndHotAudios.size)
assertEquals(1, response.freeAudios.size)
assertEquals(1, response.pointAudios.size)
assertEquals(1, response.mostCommentedAudios.size)
assertEquals(1, response.recommendedAudios.size)
assertEquals(false, response.latestAudios[0].isOriginalSeries)
val json = objectMapper.readTree(objectMapper.writeValueAsString(response))
assertEquals(false, json["latestAudios"][0]["isAdult"].asBoolean())
assertEquals(true, json["latestAudios"][0]["isPointAvailable"].asBoolean())
assertEquals(true, json["latestAudios"][0]["isFirstContent"].asBoolean())
assertEquals(false, json["latestAudios"][0]["isOriginalSeries"].asBoolean())
assertEquals(false, json["latestAudios"][0].has("adult"))
assertEquals(false, json["latestAudios"][0].has("pointAvailable"))
assertEquals("latest comment", json["mostCommentedAudios"][0]["latestComment"].asText())
}
private fun domain(): AudioRecommendations {
val card = AudioCard(
audioContentId = 1L,
title = "audio",
duration = "00:01",
imageUrl = "https://cdn.test/audio.png",
price = 0,
isAdult = false,
isPointAvailable = true,
isFirstContent = true,
isOriginalSeries = false,
creatorNickname = "creator"
)
return AudioRecommendations(
banners = listOf(RecommendationBanner("https://cdn.test/banner.png", null, null, null, "https://link.test")),
originalSeries = listOf(OriginalSeries(2L, "https://cdn.test/series.png")),
latestAudios = listOf(card),
newAndHotAudios = listOf(card.copy(audioContentId = 3L)),
freeAudios = listOf(card.copy(audioContentId = 4L)),
pointAudios = listOf(card.copy(audioContentId = 5L)),
mostCommentedAudios = listOf(
CommentedAudio(
audioContentId = 6L,
title = "commented",
imageUrl = "https://cdn.test/commented.png",
latestComment = "latest comment",
latestCommentWriterProfileImageUrl = "https://cdn.test/profile.png"
)
),
recommendedAudios = listOf(card.copy(audioContentId = 7L))
)
}
}