From 5cf1f7d909e35bfae49021a43c2a0f00493ddb0c Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 12 Jun 2026 11:39:50 +0900 Subject: [PATCH] =?UTF-8?q?test(aicharacter):=20=ED=81=AC=EB=A6=AC?= =?UTF-8?q?=EC=97=90=EC=9D=B4=ED=84=B0=20=ED=9A=8C=EC=9B=90=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=EA=B2=80=EC=A6=9D=EC=9D=84=20=EB=B3=B4=EA=B0=95?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...cterCreatorMemberServiceIntegrationTest.kt | 171 ++++++++++++++++++ .../ChatCharacterCreatorMemberServiceTest.kt | 105 +---------- 2 files changed, 172 insertions(+), 104 deletions(-) create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceIntegrationTest.kt diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceIntegrationTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceIntegrationTest.kt new file mode 100644 index 00000000..4eeb8780 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceIntegrationTest.kt @@ -0,0 +1,171 @@ +package kr.co.vividnext.sodalive.chat.character.service + +import kr.co.vividnext.sodalive.chat.character.ChatCharacter +import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository +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 kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.annotation.Transactional +import javax.persistence.EntityManager + +@SpringBootTest +@Transactional +@ContextConfiguration(initializers = [EmbeddedRedisInitializer::class]) +class ChatCharacterCreatorMemberServiceIntegrationTest @Autowired constructor( + private val creatorMemberService: ChatCharacterCreatorMemberService, + private val memberRepository: MemberRepository, + private val chatCharacterRepository: ChatCharacterRepository, + private val entityManager: EntityManager +) { + @Test + fun `ChatCharacter creatorMember 관계와 repository 메서드를 사용할 수 있다`() { + val member = memberRepository.save(createMember(memberKind = MemberKind.AI_CHARACTER)) + val chatCharacter = chatCharacterRepository.save(createCharacter().apply { creatorMember = member }) + entityManager.flush() + entityManager.clear() + + val found = chatCharacterRepository.findByCreatorMemberId(member.id!!) + + assertNotNull(found) + assertEquals(chatCharacter.id, found!!.id) + assertEquals(true, chatCharacterRepository.existsByCreatorMemberId(member.id!!)) + } + + @Test + fun `AI 캐릭터용 Member를 생성하고 표시 정보를 복사한다`() { + val chatCharacter = createCharacter( + name = "소다", + description = "AI 캐릭터 설명", + imagePath = "characters/1/profile.png" + ) + + val member = creatorMemberService.ensureAiCharacterCreatorMember(chatCharacter) + chatCharacterRepository.save(chatCharacter) + entityManager.flush() + entityManager.clear() + + val savedMember = memberRepository.findById(member.id!!).orElseThrow() + val savedCharacter = chatCharacterRepository.findByCreatorMemberId(member.id!!) + assertNotNull(savedCharacter) + assertNull(savedMember.email) + assertEquals("", savedMember.password) + assertEquals(MemberRole.CREATOR, savedMember.role) + assertEquals(MemberKind.AI_CHARACTER, savedMember.memberKind) + assertEquals("소다", savedMember.nickname) + assertEquals("characters/1/profile.png", savedMember.profileImage) + assertEquals("AI 캐릭터 설명", savedMember.introduce) + } + + @Test + fun `AI 캐릭터용 Member 표시 정보를 동기화한다`() { + val member = memberRepository.save( + createMember(memberKind = MemberKind.AI_CHARACTER).apply { + nickname = "old-name" + profileImage = "old/profile.png" + introduce = "old-description" + } + ) + val chatCharacter = chatCharacterRepository.save( + createCharacter( + name = "new-name", + description = "new-description", + imagePath = "new/profile.png" + ).apply { creatorMember = member } + ) + entityManager.flush() + entityManager.clear() + + val savedCharacter = chatCharacterRepository.findById(chatCharacter.id!!).orElseThrow() + creatorMemberService.syncAiCharacterCreatorMemberDisplayFields(savedCharacter) + entityManager.flush() + entityManager.clear() + + val savedMember = memberRepository.findById(member.id!!).orElseThrow() + assertEquals("new-name", savedMember.nickname) + assertEquals("new/profile.png", savedMember.profileImage) + assertEquals("new-description", savedMember.introduce) + } + + @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) + } + + @Test + fun `사람 크리에이터 Member 표시 정보는 덮어쓰지 않는다`() { + val member = memberRepository.save( + createMember(memberKind = MemberKind.HUMAN).apply { + nickname = "human-name" + profileImage = "human/profile.png" + introduce = "human-description" + } + ) + val chatCharacter = chatCharacterRepository.save( + createCharacter( + name = "ai-name", + description = "ai-description", + imagePath = "ai/profile.png" + ).apply { creatorMember = member } + ) + entityManager.flush() + entityManager.clear() + + val savedCharacter = chatCharacterRepository.findById(chatCharacter.id!!).orElseThrow() + creatorMemberService.syncAiCharacterCreatorMemberDisplayFields(savedCharacter) + entityManager.flush() + entityManager.clear() + + val savedMember = memberRepository.findById(member.id!!).orElseThrow() + assertEquals("human-name", savedMember.nickname) + assertEquals("human/profile.png", savedMember.profileImage) + assertEquals("human-description", savedMember.introduce) + } + + 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 = 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-${System.nanoTime()}@example.com" else null, + password = if (memberKind == MemberKind.HUMAN) "password" else "", + nickname = "member-name", + role = MemberRole.CREATOR, + memberKind = memberKind + ) + } +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceTest.kt index 7847bf60..0fbb389d 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterCreatorMemberServiceTest.kt @@ -7,125 +7,23 @@ import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepo 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) + creatorMemberService = Mockito.mock(ChatCharacterCreatorMemberService::class.java) } @Test @@ -172,7 +70,6 @@ class ChatCharacterCreatorMemberServiceTest { } private fun createChatCharacterService(): ChatCharacterService { - creatorMemberService = Mockito.mock(ChatCharacterCreatorMemberService::class.java) return ChatCharacterService( chatCharacterRepository = chatCharacterRepository, tagRepository = Mockito.mock(ChatCharacterTagRepository::class.java),