feat(home-following): 최근 소식 발행 service를 추가한다

This commit is contained in:
2026-06-26 02:48:29 +09:00
parent 8b5c872b45
commit e598d2058d
2 changed files with 285 additions and 0 deletions
@@ -0,0 +1,139 @@
package kr.co.vividnext.sodalive.v2.home.following.application
import kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType
import kr.co.vividnext.sodalive.v2.home.following.port.out.HomeFollowingNewsInboxPort
import kr.co.vividnext.sodalive.v2.home.following.port.out.HomeFollowingNewsInboxRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.time.LocalDateTime
class HomeFollowingNewsPublishServiceTest {
@Test
@DisplayName("커뮤니티 게시글 발행은 현재 활성 팔로워에게만 inbox record를 생성한다")
fun shouldPublishCommunityPostCreatedToActiveFollowers() {
val inboxPort = FakeHomeFollowingNewsInboxPort(activeFollowerIds = listOf(1L, 2L))
val service = HomeFollowingNewsPublishService(inboxPort)
val occurredAtUtc = LocalDateTime.of(2026, 6, 25, 1, 2, 3)
service.publishCommunityPostCreated(
postId = 100L,
creatorId = 9L,
creatorNickname = "creator",
creatorProfileImagePath = "profile.png",
title = "새 커뮤니티 글",
body = "본문",
thumbnailImagePath = "post.png",
occurredAtUtc = occurredAtUtc,
isAdult = true
)
assertEquals(9L, inboxPort.findActiveFollowerIdsCreatorId)
assertEquals(listOf(1L, 2L), inboxPort.records.map { it.memberId })
val record = inboxPort.records.first()
assertEquals(FollowingNewsType.COMMUNITY_POST.name, record.newsType)
assertEquals("COMMUNITY_POST:100", record.sourceKey)
assertEquals(100L, record.targetId)
assertEquals(occurredAtUtc, record.visibleFromAtUtc)
assertEquals("post.png", record.thumbnailImagePath)
assertEquals(true, record.isAdult)
}
@Test
@DisplayName("오디오 콘텐츠 발행은 공개 시각을 visibleFromAtUtc로 저장한다")
fun shouldPublishContentUploadedWithVisibleFromAtUtc() {
val inboxPort = FakeHomeFollowingNewsInboxPort(activeFollowerIds = listOf(3L))
val service = HomeFollowingNewsPublishService(inboxPort)
val occurredAtUtc = LocalDateTime.of(2026, 6, 25, 2, 0)
val visibleFromAtUtc = LocalDateTime.of(2026, 6, 25, 9, 0)
service.publishContentUploaded(
contentId = 200L,
creatorId = 8L,
creatorNickname = "audio-creator",
creatorProfileImagePath = null,
title = "오디오 제목",
body = "오디오 설명",
thumbnailImagePath = "cover.jpg",
occurredAtUtc = occurredAtUtc,
visibleFromAtUtc = visibleFromAtUtc,
isAdult = false
)
val record = inboxPort.records.single()
assertEquals(FollowingNewsType.AUDIO_CONTENT.name, record.newsType)
assertEquals("AUDIO_CONTENT:200", record.sourceKey)
assertEquals(occurredAtUtc, record.occurredAtUtc)
assertEquals(visibleFromAtUtc, record.visibleFromAtUtc)
assertEquals("cover.jpg", record.thumbnailImagePath)
}
@Test
@DisplayName("발행 record의 title과 body는 inbox 컬럼 길이에 맞게 잘린다")
fun shouldTruncateTitleAndBodyToInboxColumnLimits() {
val inboxPort = FakeHomeFollowingNewsInboxPort(activeFollowerIds = listOf(5L))
val service = HomeFollowingNewsPublishService(inboxPort)
service.publishContentUploaded(
contentId = 201L,
creatorId = 8L,
creatorNickname = "audio-creator",
creatorProfileImagePath = null,
title = "가".repeat(300),
body = "나".repeat(1_200),
thumbnailImagePath = null,
occurredAtUtc = LocalDateTime.of(2026, 6, 25, 2, 0),
visibleFromAtUtc = LocalDateTime.of(2026, 6, 25, 9, 0),
isAdult = false
)
val record = inboxPort.records.single()
assertEquals(255, record.title.length)
assertEquals(1_000, record.body.length)
}
@Test
@DisplayName("크리에이터 랭킹 발행은 rank와 스냅샷 노출 시각을 저장한다")
fun shouldPublishCreatorRankingVisibleWithRankAndVisibleFromAtUtc() {
val inboxPort = FakeHomeFollowingNewsInboxPort(activeFollowerIds = listOf(4L))
val service = HomeFollowingNewsPublishService(inboxPort)
val aggregationStartAtUtc = LocalDateTime.of(2026, 5, 31, 15, 0)
val visibleFromAtUtc = LocalDateTime.of(2026, 6, 8, 0, 0)
service.publishCreatorRankingVisible(
creatorId = 7L,
creatorNickname = "ranker",
creatorProfileImagePath = "ranker.png",
aggregationStartAtUtc = aggregationStartAtUtc,
visibleFromAtUtc = visibleFromAtUtc,
rank = 2
)
val record = inboxPort.records.single()
assertEquals(FollowingNewsType.CREATOR_RANKING.name, record.newsType)
assertEquals("CREATOR_RANKING:7:2026-05-31T15:00", record.sourceKey)
assertEquals(7L, record.targetId)
assertEquals(visibleFromAtUtc, record.occurredAtUtc)
assertEquals(visibleFromAtUtc, record.visibleFromAtUtc)
assertEquals(2, record.rank)
}
}
private class FakeHomeFollowingNewsInboxPort(
private val activeFollowerIds: List<Long>
) : HomeFollowingNewsInboxPort {
val records = mutableListOf<HomeFollowingNewsInboxRecord>()
var findActiveFollowerIdsCreatorId: Long? = null
override fun insertIgnoreAll(records: List<HomeFollowingNewsInboxRecord>): Int {
this.records.addAll(records)
return records.size
}
override fun deactivateByMemberIdAndCreatorId(memberId: Long, creatorId: Long): Long = 0
override fun findActiveFollowerIds(creatorId: Long): List<Long> {
findActiveFollowerIdsCreatorId = creatorId
return activeFollowerIds
}
}