feat(creator-channel): FanTalk 탭 repository를 추가한다
This commit is contained in:
+227
@@ -0,0 +1,227 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.adapter.out.persistence
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
||||
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.member.block.BlockMember
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.context.annotation.Import
|
||||
import java.time.LocalDateTime
|
||||
import javax.persistence.EntityManager
|
||||
|
||||
@DataJpaTest(
|
||||
properties = [
|
||||
"spring.cache.type=none",
|
||||
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;NON_KEYWORDS=VALUE"
|
||||
]
|
||||
)
|
||||
@Import(QueryDslConfig::class)
|
||||
class DefaultCreatorChannelFanTalkQueryRepositoryTest @Autowired constructor(
|
||||
private val entityManager: EntityManager,
|
||||
queryFactory: JPAQueryFactory
|
||||
) {
|
||||
private val repository = DefaultCreatorChannelFanTalkQueryRepository(queryFactory)
|
||||
|
||||
@Test
|
||||
@DisplayName("활성 회원은 role과 닉네임을 조회하고 비활성 회원은 조회하지 않는다")
|
||||
fun shouldFindOnlyActiveCreator() {
|
||||
val viewer = saveMember("creator-lookup-viewer", MemberRole.USER)
|
||||
val activeCreator = saveMember("active-fantalk-creator", MemberRole.CREATOR)
|
||||
val inactiveCreator = saveMember("inactive-fantalk-creator", MemberRole.CREATOR, isActive = false)
|
||||
val nonCreator = saveMember("fantalk-non-creator", MemberRole.USER)
|
||||
flushAndClear()
|
||||
|
||||
val activeRecord = repository.findCreator(activeCreator.id!!, viewer.id!!)
|
||||
val inactiveRecord = repository.findCreator(inactiveCreator.id!!, viewer.id!!)
|
||||
val nonCreatorRecord = repository.findCreator(nonCreator.id!!, viewer.id!!)
|
||||
|
||||
assertNotNull(activeRecord)
|
||||
assertEquals(activeCreator.id, activeRecord!!.creatorId)
|
||||
assertEquals(MemberRole.CREATOR, activeRecord.role)
|
||||
assertEquals(activeCreator.nickname, activeRecord.nickname)
|
||||
assertNull(inactiveRecord)
|
||||
assertEquals(MemberRole.USER, nonCreatorRecord!!.role)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("조회자와 크리에이터 사이 양방향 활성 차단만 차단 상태로 조회한다")
|
||||
fun shouldFindActiveBlockInBothDirections() {
|
||||
val viewer = saveMember("fantalk-block-viewer", MemberRole.USER)
|
||||
val creator = saveMember("fantalk-block-creator", MemberRole.CREATOR)
|
||||
val otherCreator = saveMember("fantalk-other-creator", MemberRole.CREATOR)
|
||||
saveBlock(viewer, creator, isActive = true)
|
||||
saveBlock(otherCreator, viewer, isActive = false)
|
||||
flushAndClear()
|
||||
|
||||
assertTrue(repository.existsBlockedBetween(viewer.id!!, creator.id!!))
|
||||
assertTrue(repository.existsBlockedBetween(creator.id!!, viewer.id!!))
|
||||
assertFalse(repository.existsBlockedBetween(viewer.id!!, otherCreator.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("최상위 FanTalk 수와 목록은 활성 루트 글만 세고 작성자 차단을 제외한다")
|
||||
fun shouldCountAndFindOnlyVisibleTopLevelFanTalks() {
|
||||
val viewer = saveMember("fantalk-list-viewer", MemberRole.USER)
|
||||
val creator = saveMember("fantalk-list-creator", MemberRole.CREATOR)
|
||||
val otherCreator = saveMember("fantalk-list-other-creator", MemberRole.CREATOR)
|
||||
val visibleWriter = saveMember("visible-writer", MemberRole.USER, profileImage = "visible.png")
|
||||
val blockedWriter = saveMember("blocked-writer", MemberRole.USER)
|
||||
val writerBlockingViewer = saveMember("writer-blocking-viewer", MemberRole.USER)
|
||||
val now = LocalDateTime.of(2026, 6, 22, 12, 0)
|
||||
val older = saveCheers(visibleWriter, creator, "older", isActive = true, createdAt = now.minusHours(2))
|
||||
val newer = saveCheers(visibleWriter, creator, "newer", isActive = true, createdAt = now.minusHours(1))
|
||||
saveCheers(visibleWriter, creator, "inactive", isActive = false, createdAt = now)
|
||||
saveCheers(visibleWriter, otherCreator, "other creator", isActive = true, createdAt = now)
|
||||
saveCheers(visibleWriter, creator, "reply", isActive = true, createdAt = now, parent = older)
|
||||
saveCheers(blockedWriter, creator, "viewer blocked", isActive = true, createdAt = now.plusHours(1))
|
||||
saveCheers(writerBlockingViewer, creator, "writer blocked", isActive = true, createdAt = now.plusHours(2))
|
||||
saveBlock(viewer, blockedWriter, isActive = true)
|
||||
saveBlock(writerBlockingViewer, viewer, isActive = true)
|
||||
flushAndClear()
|
||||
|
||||
val count = repository.countFanTalks(creator.id!!, viewer.id!!)
|
||||
val firstPage = repository.findFanTalks(creator.id!!, viewer.id!!, offset = 0, limit = 1)
|
||||
val secondPage = repository.findFanTalks(creator.id!!, viewer.id!!, offset = 1, limit = 2)
|
||||
|
||||
assertEquals(2, count)
|
||||
assertEquals(listOf(newer.id), firstPage.map { it.fanTalkId })
|
||||
assertEquals(listOf(older.id), secondPage.map { it.fanTalkId })
|
||||
assertEquals(visibleWriter.id, firstPage.first().writerId)
|
||||
assertEquals(visibleWriter.nickname, firstPage.first().writerNickname)
|
||||
assertEquals(visibleWriter.profileImage, firstPage.first().writerProfileImagePath)
|
||||
assertEquals("newer", firstPage.first().content)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("최상위 FanTalk 목록은 createdAt desc, id desc 순서로 정렬한다")
|
||||
fun shouldOrderFanTalksByCreatedAtDescAndIdDesc() {
|
||||
val viewer = saveMember("fantalk-order-viewer", MemberRole.USER)
|
||||
val creator = saveMember("fantalk-order-creator", MemberRole.CREATOR)
|
||||
val writer = saveMember("fantalk-order-writer", MemberRole.USER)
|
||||
val sameCreatedAt = LocalDateTime.of(2026, 6, 22, 12, 0)
|
||||
val first = saveCheers(writer, creator, "first", isActive = true, createdAt = sameCreatedAt)
|
||||
val second = saveCheers(writer, creator, "second", isActive = true, createdAt = sameCreatedAt)
|
||||
val newest = saveCheers(writer, creator, "newest", isActive = true, createdAt = sameCreatedAt.plusMinutes(1))
|
||||
flushAndClear()
|
||||
|
||||
val records = repository.findFanTalks(creator.id!!, viewer.id!!, offset = 0, limit = 10)
|
||||
|
||||
assertEquals(listOf(newest.id, second.id, first.id), records.map { it.fanTalkId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("크리에이터 답글은 지정한 부모의 활성 크리에이터 작성 답글만 오래된 순으로 조회한다")
|
||||
fun shouldFindOnlyActiveCreatorRepliesForRequestedParents() {
|
||||
val creator = saveMember("reply-creator", MemberRole.CREATOR)
|
||||
val writer = saveMember("reply-writer", MemberRole.USER)
|
||||
val otherCreator = saveMember("reply-other-creator", MemberRole.CREATOR)
|
||||
val now = LocalDateTime.of(2026, 6, 22, 12, 0)
|
||||
val parent = saveCheers(writer, creator, "parent", isActive = true, createdAt = now.minusHours(3))
|
||||
val otherParent = saveCheers(writer, creator, "other parent", isActive = true, createdAt = now.minusHours(2))
|
||||
val newerReply = saveCheers(creator, creator, "newer reply", isActive = true, createdAt = now, parent = parent)
|
||||
val olderReply = saveCheers(
|
||||
creator,
|
||||
creator,
|
||||
"older reply",
|
||||
isActive = true,
|
||||
createdAt = now.minusMinutes(1),
|
||||
parent = parent
|
||||
)
|
||||
saveCheers(writer, creator, "fan reply", isActive = true, createdAt = now.plusMinutes(1), parent = parent)
|
||||
saveCheers(creator, creator, "inactive reply", isActive = false, createdAt = now.plusMinutes(2), parent = parent)
|
||||
saveCheers(
|
||||
creator,
|
||||
otherCreator,
|
||||
"other creator reply",
|
||||
isActive = true,
|
||||
createdAt = now.plusMinutes(3),
|
||||
parent = parent
|
||||
)
|
||||
saveCheers(
|
||||
creator,
|
||||
creator,
|
||||
"other parent reply",
|
||||
isActive = true,
|
||||
createdAt = now.plusMinutes(4),
|
||||
parent = otherParent
|
||||
)
|
||||
flushAndClear()
|
||||
|
||||
val replies = repository.findCreatorReplies(creator.id!!, listOf(parent.id!!))
|
||||
val emptyReplies = repository.findCreatorReplies(creator.id!!, emptyList())
|
||||
|
||||
assertEquals(listOf(olderReply.id, newerReply.id), replies.map { it.fanTalkId })
|
||||
assertEquals(parent.id, replies.first().parentFanTalkId)
|
||||
assertEquals(creator.id, replies.first().writerId)
|
||||
assertEquals(creator.nickname, replies.first().writerNickname)
|
||||
assertEquals(creator.profileImage, replies.first().writerProfileImagePath)
|
||||
assertEquals("older reply", replies.first().content)
|
||||
assertTrue(emptyReplies.isEmpty())
|
||||
}
|
||||
|
||||
private fun saveMember(
|
||||
nickname: String,
|
||||
role: MemberRole,
|
||||
isActive: Boolean = true,
|
||||
profileImage: String? = "$nickname.png"
|
||||
): Member {
|
||||
val member = Member(
|
||||
email = "$nickname@test.com",
|
||||
password = "password",
|
||||
nickname = nickname,
|
||||
profileImage = profileImage,
|
||||
role = role,
|
||||
isActive = isActive
|
||||
)
|
||||
entityManager.persist(member)
|
||||
return member
|
||||
}
|
||||
|
||||
private fun saveBlock(member: Member, blockedMember: Member, isActive: Boolean): BlockMember {
|
||||
val block = BlockMember(isActive = isActive)
|
||||
block.member = member
|
||||
block.blockedMember = blockedMember
|
||||
entityManager.persist(block)
|
||||
return block
|
||||
}
|
||||
|
||||
private fun saveCheers(
|
||||
member: Member,
|
||||
creator: Member,
|
||||
cheers: String,
|
||||
isActive: Boolean,
|
||||
createdAt: LocalDateTime,
|
||||
parent: CreatorCheers? = null
|
||||
): CreatorCheers {
|
||||
val creatorCheers = CreatorCheers(cheers = cheers, languageCode = "ko", isActive = isActive)
|
||||
creatorCheers.member = member
|
||||
creatorCheers.creator = creator
|
||||
creatorCheers.parent = parent
|
||||
entityManager.persist(creatorCheers)
|
||||
entityManager.flush()
|
||||
updateCreatedAt("CreatorCheers", creatorCheers.id!!, createdAt)
|
||||
return creatorCheers
|
||||
}
|
||||
|
||||
private fun updateCreatedAt(entityName: String, id: Long, createdAt: LocalDateTime) {
|
||||
entityManager.createQuery("update $entityName e set e.createdAt = :createdAt where e.id = :id")
|
||||
.setParameter("createdAt", createdAt)
|
||||
.setParameter("id", id)
|
||||
.executeUpdate()
|
||||
}
|
||||
|
||||
private fun flushAndClear() {
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user