feat(aicharacter): 크리에이터 회원 연결을 추가한다
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
package kr.co.vividnext.sodalive.chat.character.service
|
||||
|
||||
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
|
||||
import kr.co.vividnext.sodalive.chat.character.image.CharacterImageRepository
|
||||
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterGoalRepository
|
||||
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepository
|
||||
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository
|
||||
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterTagRepository
|
||||
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterValueRepository
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberKind
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertSame
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
import java.util.Optional
|
||||
|
||||
class ChatCharacterCreatorMemberServiceTest {
|
||||
private lateinit var memberRepository: MemberRepository
|
||||
private lateinit var chatCharacterRepository: ChatCharacterRepository
|
||||
private lateinit var creatorMemberService: ChatCharacterCreatorMemberService
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
memberRepository = Mockito.mock(MemberRepository::class.java)
|
||||
chatCharacterRepository = Mockito.mock(ChatCharacterRepository::class.java)
|
||||
creatorMemberService = ChatCharacterCreatorMemberService(memberRepository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ChatCharacter creatorMember 관계와 repository 메서드를 사용할 수 있다`() {
|
||||
val member = createMember(memberKind = MemberKind.AI_CHARACTER)
|
||||
val chatCharacter = createCharacter().apply { creatorMember = member }
|
||||
|
||||
Mockito.`when`(chatCharacterRepository.findByCreatorMemberId(1L)).thenReturn(chatCharacter)
|
||||
Mockito.`when`(chatCharacterRepository.existsByCreatorMemberId(1L)).thenReturn(true)
|
||||
|
||||
assertSame(member, chatCharacter.creatorMember)
|
||||
assertSame(chatCharacter, chatCharacterRepository.findByCreatorMemberId(1L))
|
||||
assertEquals(true, chatCharacterRepository.existsByCreatorMemberId(1L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AI 캐릭터용 Member를 생성하고 표시 정보를 복사한다`() {
|
||||
val chatCharacter = createCharacter(
|
||||
name = "소다",
|
||||
description = "AI 캐릭터 설명",
|
||||
imagePath = "characters/1/profile.png"
|
||||
)
|
||||
Mockito.`when`(memberRepository.save(Mockito.any(Member::class.java))).thenAnswer { invocation ->
|
||||
(invocation.arguments[0] as Member).apply { id = 10L }
|
||||
}
|
||||
|
||||
val member = creatorMemberService.ensureAiCharacterCreatorMember(chatCharacter)
|
||||
|
||||
assertSame(member, chatCharacter.creatorMember)
|
||||
assertNull(member.email)
|
||||
assertEquals("", member.password)
|
||||
assertEquals(MemberRole.CREATOR, member.role)
|
||||
assertEquals(MemberKind.AI_CHARACTER, member.memberKind)
|
||||
assertEquals("소다", member.nickname)
|
||||
assertEquals("characters/1/profile.png", member.profileImage)
|
||||
assertEquals("AI 캐릭터 설명", member.introduce)
|
||||
Mockito.verify(memberRepository).save(member)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AI 캐릭터용 Member 표시 정보를 동기화한다`() {
|
||||
val member = createMember(memberKind = MemberKind.AI_CHARACTER).apply {
|
||||
nickname = "old-name"
|
||||
profileImage = "old/profile.png"
|
||||
introduce = "old-description"
|
||||
}
|
||||
val chatCharacter = createCharacter(
|
||||
name = "new-name",
|
||||
description = "new-description",
|
||||
imagePath = "new/profile.png"
|
||||
).apply { creatorMember = member }
|
||||
|
||||
creatorMemberService.syncAiCharacterCreatorMemberDisplayFields(chatCharacter)
|
||||
|
||||
assertEquals("new-name", member.nickname)
|
||||
assertEquals("new/profile.png", member.profileImage)
|
||||
assertEquals("new-description", member.introduce)
|
||||
Mockito.verify(memberRepository).save(member)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `동기화 대상 creatorMember가 없으면 저장 실패를 위해 예외를 던진다`() {
|
||||
val chatCharacter = createCharacter(
|
||||
id = 1L,
|
||||
name = "missing-creator-member",
|
||||
description = "description"
|
||||
)
|
||||
|
||||
val exception = assertThrows(SodaException::class.java) {
|
||||
creatorMemberService.syncAiCharacterCreatorMemberDisplayFields(chatCharacter)
|
||||
}
|
||||
|
||||
assertEquals("common.error.invalid_request", exception.messageKey)
|
||||
Mockito.verifyNoInteractions(memberRepository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `사람 크리에이터 Member 표시 정보는 덮어쓰지 않는다`() {
|
||||
val member = createMember(memberKind = MemberKind.HUMAN).apply {
|
||||
nickname = "human-name"
|
||||
profileImage = "human/profile.png"
|
||||
introduce = "human-description"
|
||||
}
|
||||
val chatCharacter = createCharacter(
|
||||
name = "ai-name",
|
||||
description = "ai-description",
|
||||
imagePath = "ai/profile.png"
|
||||
).apply { creatorMember = member }
|
||||
|
||||
creatorMemberService.syncAiCharacterCreatorMemberDisplayFields(chatCharacter)
|
||||
|
||||
assertEquals("human-name", member.nickname)
|
||||
assertEquals("human/profile.png", member.profileImage)
|
||||
assertEquals("human-description", member.introduce)
|
||||
Mockito.verifyNoInteractions(memberRepository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `캐릭터 생성 시 저장 전에 AI 캐릭터용 Member 생성을 요청한다`() {
|
||||
val service = createChatCharacterService()
|
||||
Mockito.`when`(chatCharacterRepository.save(Mockito.any(ChatCharacter::class.java))).thenAnswer { invocation ->
|
||||
(invocation.arguments[0] as ChatCharacter).apply { id = 1L }
|
||||
}
|
||||
|
||||
val chatCharacter = service.createChatCharacter(
|
||||
characterUUID = "character-1",
|
||||
name = "created-name",
|
||||
description = "created-description",
|
||||
systemPrompt = "system-prompt"
|
||||
)
|
||||
|
||||
val inOrder = Mockito.inOrder(creatorMemberService, chatCharacterRepository)
|
||||
inOrder.verify(creatorMemberService).ensureAiCharacterCreatorMember(chatCharacter)
|
||||
inOrder.verify(chatCharacterRepository).save(chatCharacter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `캐릭터 수정 시 AI 캐릭터용 Member 표시 정보 동기화를 요청한다`() {
|
||||
val service = createChatCharacterService()
|
||||
val chatCharacter = createCharacter(id = 1L).apply {
|
||||
creatorMember = createMember(memberKind = MemberKind.AI_CHARACTER)
|
||||
}
|
||||
Mockito.`when`(chatCharacterRepository.findById(1L)).thenReturn(Optional.of(chatCharacter))
|
||||
Mockito.`when`(chatCharacterRepository.save(Mockito.any(ChatCharacter::class.java))).thenAnswer { it.arguments[0] }
|
||||
|
||||
service.updateChatCharacterWithDetails(
|
||||
imagePath = "updated/profile.png",
|
||||
request = kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterUpdateRequest(
|
||||
id = 1L,
|
||||
name = "updated-name",
|
||||
description = "updated-description"
|
||||
)
|
||||
)
|
||||
|
||||
assertEquals("updated-name", chatCharacter.name)
|
||||
assertEquals("updated-description", chatCharacter.description)
|
||||
assertEquals("updated/profile.png", chatCharacter.imagePath)
|
||||
Mockito.verify(creatorMemberService).syncAiCharacterCreatorMemberDisplayFields(chatCharacter)
|
||||
}
|
||||
|
||||
private fun createChatCharacterService(): ChatCharacterService {
|
||||
creatorMemberService = Mockito.mock(ChatCharacterCreatorMemberService::class.java)
|
||||
return ChatCharacterService(
|
||||
chatCharacterRepository = chatCharacterRepository,
|
||||
tagRepository = Mockito.mock(ChatCharacterTagRepository::class.java),
|
||||
valueRepository = Mockito.mock(ChatCharacterValueRepository::class.java),
|
||||
hobbyRepository = Mockito.mock(ChatCharacterHobbyRepository::class.java),
|
||||
goalRepository = Mockito.mock(ChatCharacterGoalRepository::class.java),
|
||||
popularCharacterQuery = Mockito.mock(PopularCharacterQuery::class.java),
|
||||
imageRepository = Mockito.mock(CharacterImageRepository::class.java),
|
||||
creatorMemberService = creatorMemberService,
|
||||
imageHost = "https://cdn.example.com"
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCharacter(
|
||||
id: Long? = null,
|
||||
name: String = "character-name",
|
||||
description: String = "character-description",
|
||||
imagePath: String? = null
|
||||
): ChatCharacter {
|
||||
val character = ChatCharacter(
|
||||
characterUUID = "character-uuid",
|
||||
name = name,
|
||||
description = description,
|
||||
systemPrompt = "system-prompt"
|
||||
)
|
||||
character.id = id
|
||||
character.imagePath = imagePath
|
||||
return character
|
||||
}
|
||||
|
||||
private fun createMember(memberKind: MemberKind): Member {
|
||||
return Member(
|
||||
email = if (memberKind == MemberKind.HUMAN) "human@example.com" else null,
|
||||
password = "password",
|
||||
nickname = "member-name",
|
||||
role = MemberRole.CREATOR,
|
||||
memberKind = memberKind
|
||||
).apply { id = 1L }
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCo
|
||||
import kr.co.vividnext.sodalive.i18n.Lang
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberKind
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.member.block.BlockMember
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
||||
@@ -1793,6 +1794,17 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
||||
}
|
||||
|
||||
private fun saveCharacter(name: String, isActive: Boolean, originalWork: OriginalWork? = null): ChatCharacter {
|
||||
val creatorMember = Member(
|
||||
email = null,
|
||||
password = "",
|
||||
nickname = name,
|
||||
role = MemberRole.CREATOR,
|
||||
memberKind = MemberKind.AI_CHARACTER,
|
||||
isActive = isActive
|
||||
)
|
||||
creatorMember.introduce = "description"
|
||||
entityManager.persist(creatorMember)
|
||||
|
||||
val character = ChatCharacter(
|
||||
characterUUID = "$name-uuid",
|
||||
name = name,
|
||||
@@ -1801,6 +1813,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
||||
isActive = isActive
|
||||
)
|
||||
character.originalWork = originalWork
|
||||
character.creatorMember = creatorMember
|
||||
entityManager.persist(character)
|
||||
return character
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user