test #426
@@ -286,6 +286,7 @@ class HomeRecommendationFacade(
|
|||||||
|
|
||||||
private fun HomeAiCharacterRecommendationRecord.toItem() = HomeAiCharacterItem(
|
private fun HomeAiCharacterRecommendationRecord.toItem() = HomeAiCharacterItem(
|
||||||
characterId = characterId,
|
characterId = characterId,
|
||||||
|
creatorId = creatorId,
|
||||||
name = name,
|
name = name,
|
||||||
description = description,
|
description = description,
|
||||||
profileImage = imageUrl(cloudFrontHost, profileImage),
|
profileImage = imageUrl(cloudFrontHost, profileImage),
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ data class HomeFirstAudioContentItem(
|
|||||||
|
|
||||||
data class HomeAiCharacterItem(
|
data class HomeAiCharacterItem(
|
||||||
val characterId: Long,
|
val characterId: Long,
|
||||||
|
val creatorId: Long,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val profileImage: String?,
|
val profileImage: String?,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.api.home
|
package kr.co.vividnext.sodalive.v2.api.home
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.MemberAdapter
|
import kr.co.vividnext.sodalive.member.MemberAdapter
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberKind
|
||||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreference
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreference
|
||||||
@@ -11,7 +13,9 @@ import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
|||||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowingRepository
|
import kr.co.vividnext.sodalive.member.following.CreatorFollowingRepository
|
||||||
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
||||||
import kr.co.vividnext.sodalive.v2.api.home.application.HomeRecommendationFacade
|
import kr.co.vividnext.sodalive.v2.api.home.application.HomeRecommendationFacade
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshot
|
||||||
import kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryService
|
import kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryService
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
@@ -481,6 +485,29 @@ class HomeRecommendationControllerTest @Autowired constructor(
|
|||||||
.andExpect(jsonPath("$.data.size").value(20))
|
.andExpect(jsonPath("$.data.size").value(20))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("AI 캐릭터 추천은 홈 통합과 전체보기 응답에 characterId와 creatorId를 함께 노출한다")
|
||||||
|
fun shouldExposeCreatorIdOnAiCharacterRecommendations() {
|
||||||
|
val member = saveMember("ai-character-page-viewer", MemberRole.USER)
|
||||||
|
val character = saveAiCharacter("ai-character-api")
|
||||||
|
saveRecommendationSnapshot(character.id!!)
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/v2/home/recommendations"))
|
||||||
|
.andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.data.aiCharacters[0].characterId").value(character.id))
|
||||||
|
.andExpect(jsonPath("$.data.aiCharacters[0].creatorId").value(character.creatorMember!!.id))
|
||||||
|
|
||||||
|
mockMvc.perform(
|
||||||
|
get("/api/v2/home/recommendations/ai-characters")
|
||||||
|
.with(user(MemberAdapter(member)))
|
||||||
|
)
|
||||||
|
.andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.data.items[0].characterId").value(character.id))
|
||||||
|
.andExpect(jsonPath("$.data.items[0].creatorId").value(character.creatorMember!!.id))
|
||||||
|
}
|
||||||
|
|
||||||
private fun saveMember(seed: String, role: MemberRole): Member {
|
private fun saveMember(seed: String, role: MemberRole): Member {
|
||||||
return memberRepository.saveAndFlush(
|
return memberRepository.saveAndFlush(
|
||||||
Member(
|
Member(
|
||||||
@@ -519,4 +546,37 @@ class HomeRecommendationControllerTest @Autowired constructor(
|
|||||||
entityManager.persist(room)
|
entityManager.persist(room)
|
||||||
return room
|
return room
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveAiCharacter(name: String): ChatCharacter {
|
||||||
|
val creatorMember = Member(
|
||||||
|
email = null,
|
||||||
|
password = "",
|
||||||
|
nickname = name,
|
||||||
|
role = MemberRole.CREATOR,
|
||||||
|
memberKind = MemberKind.AI_CHARACTER
|
||||||
|
)
|
||||||
|
entityManager.persist(creatorMember)
|
||||||
|
val character = ChatCharacter(
|
||||||
|
characterUUID = "$name-uuid",
|
||||||
|
name = name,
|
||||||
|
description = "description",
|
||||||
|
systemPrompt = "system",
|
||||||
|
isActive = true
|
||||||
|
)
|
||||||
|
character.creatorMember = creatorMember
|
||||||
|
entityManager.persist(character)
|
||||||
|
return character
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveRecommendationSnapshot(characterId: Long) {
|
||||||
|
entityManager.persist(
|
||||||
|
RecommendationSnapshot(
|
||||||
|
sectionType = RecommendedSectionType.AI_CHARACTER,
|
||||||
|
targetId = characterId,
|
||||||
|
score = 100.0,
|
||||||
|
snapshotAt = LocalDateTime.of(2026, 6, 1, 23, 59, 59),
|
||||||
|
randomTieBreaker = 0.1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class HomeRecommendationResponseTest {
|
|||||||
aiCharacters = listOf(
|
aiCharacters = listOf(
|
||||||
HomeAiCharacterItem(
|
HomeAiCharacterItem(
|
||||||
characterId = 3L,
|
characterId = 3L,
|
||||||
|
creatorId = 13L,
|
||||||
name = "character",
|
name = "character",
|
||||||
description = "description",
|
description = "description",
|
||||||
profileImage = "https://cdn.test/profile/character.png",
|
profileImage = "https://cdn.test/profile/character.png",
|
||||||
@@ -38,6 +39,7 @@ class HomeRecommendationResponseTest {
|
|||||||
),
|
),
|
||||||
HomeAiCharacterItem(
|
HomeAiCharacterItem(
|
||||||
characterId = 4L,
|
characterId = 4L,
|
||||||
|
creatorId = 14L,
|
||||||
name = "character-without-image",
|
name = "character-without-image",
|
||||||
description = "description",
|
description = "description",
|
||||||
profileImage = null,
|
profileImage = null,
|
||||||
@@ -86,6 +88,7 @@ class HomeRecommendationResponseTest {
|
|||||||
assertFalse(json["firstAudioContents"][0].has("pointAvailable"))
|
assertFalse(json["firstAudioContents"][0].has("pointAvailable"))
|
||||||
assertFalse(json["firstAudioContents"][0].has("releaseDate"))
|
assertFalse(json["firstAudioContents"][0].has("releaseDate"))
|
||||||
assertEquals("https://cdn.test/profile/character.png", json["aiCharacters"][0]["profileImage"].asText())
|
assertEquals("https://cdn.test/profile/character.png", json["aiCharacters"][0]["profileImage"].asText())
|
||||||
|
assertEquals(13L, json["aiCharacters"][0]["creatorId"].asLong())
|
||||||
assertEquals(true, json["aiCharacters"][1]["profileImage"].isNull)
|
assertEquals(true, json["aiCharacters"][1]["profileImage"].isNull)
|
||||||
assertEquals(5L, json["popularCommunityPosts"][0]["postId"].asLong())
|
assertEquals(5L, json["popularCommunityPosts"][0]["postId"].asLong())
|
||||||
assertEquals("https://cdn.test/community/image.png", json["popularCommunityPosts"][0]["imageUrl"].asText())
|
assertEquals("https://cdn.test/community/image.png", json["popularCommunityPosts"][0]["imageUrl"].asText())
|
||||||
|
|||||||
Reference in New Issue
Block a user