test: タイムラインのテストを追加

This commit is contained in:
usbharu 2024-08-07 18:47:24 +09:00
parent 046350ef84
commit f96c16b2a3
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
6 changed files with 410 additions and 14 deletions

View File

@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.model.timelineobject
import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterId
class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String) data class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String)

View File

@ -43,8 +43,6 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po
// 文字だけのHTMLなどはここでpタグで囲む // 文字だけのHTMLなどはここでpタグで囲む
val flattenHtml = unsafeElement.childNodes().mapNotNull { val flattenHtml = unsafeElement.childNodes().mapNotNull {
println(it.toString())
println(it.javaClass)
if (it is Element) { if (it is Element) {
it it
} else if (it is TextNode) { } else if (it is TextNode) {
@ -59,8 +57,6 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po
val safeHtml = policyFactory.sanitize(unsafeHtml) val safeHtml = policyFactory.sanitize(unsafeHtml)
println(safeHtml)
val safeDocument = val safeDocument =
Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "") Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "")
@ -97,6 +93,9 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po
} }
val elements = Elements(formattedHtml) val elements = Elements(formattedHtml)
val document1 = Document("")
document1.outputSettings().syntax(Document.OutputSettings.Syntax.xml)
document1.insertChildren(0, elements)
return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements)) return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements))
} }

View File

@ -263,7 +263,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
) )
} }
abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor> protected abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
} }

View File

@ -1,11 +1,13 @@
package dev.usbharu.hideout.core.infrastructure.other package dev.usbharu.hideout.core.infrastructure.other
import dev.usbharu.hideout.core.config.HtmlSanitizeConfig import dev.usbharu.hideout.core.config.HtmlSanitizeConfig
import dev.usbharu.hideout.core.domain.service.post.FormattedPostContent
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks import org.mockito.InjectMocks
import org.mockito.Spy import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoExtension
import kotlin.test.assertEquals
@ExtendWith(MockitoExtension::class) @ExtendWith(MockitoExtension::class)
class DefaultPostContentFormatterTest { class DefaultPostContentFormatterTest {
@ -17,40 +19,57 @@ class DefaultPostContentFormatterTest {
@Test @Test
fun 文字だけのHTMLをPで囲む() { fun 文字だけのHTMLをPで囲む() {
formatter.format("a") val actual = formatter.format("a")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
} }
@Test @Test
fun エレメントはそのまま() { fun エレメントはそのまま() {
formatter.format("<p>a</p>") val actual = formatter.format("<p>a</p>")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
} }
@Test @Test
fun コメントは無視() { fun コメントは無視() {
formatter.format("<!-- aa -->") val actual = formatter.format("<!-- aa -->")
assertEquals(FormattedPostContent("", ""), actual)
} }
@Test @Test
fun brタグを改行に() { fun brタグを改行に() {
formatter.format("<p>a<br></p>") val actual = formatter.format("<p>a<br></p>")
assertEquals(FormattedPostContent("<p>a<br /></p>", "a\n"), actual)
} }
@Test @Test
fun brタグ2連続を段落に() { fun brタグ2連続を段落に() {
val format = formatter.format("<p>a<br><br>a</p>") val format = formatter.format("<p>a<br><br>a</p>")
println(format) assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
}
@Test
fun brタグ3連続以上を段落にして改行2つに変換() {
val format = formatter.format("<p>a<br><br><br>a</p>")
assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
} }
@Test @Test
fun aタグは許可される() { fun aタグは許可される() {
val format = formatter.format("<a href=\"https://example.com\">p</a>") val format = formatter.format("<a href=\"https://example.com\">p</a>")
println(format) assertEquals(FormattedPostContent("<a href=\"https://example.com\">p</a>", "p"), format)
} }
@Test @Test
fun pの中のaタグも許可される() { fun pの中のaタグも許可される() {
formatter.format("<p><a href=\"https://example.com\">a</a></p>") val actual = formatter.format("<p><a href=\"https://example.com\">a</a></p>")
assertEquals(FormattedPostContent("<p><a href=\"https://example.com\">a</a></p>", "a"), actual)
} }
} }

View File

@ -0,0 +1,30 @@
package dev.usbharu.hideout.core.infrastructure.other
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class TwitterSnowflakeIdGenerateServiceTest {
@Test
fun noDuplicateTest() = runBlocking {
val mutex = Mutex()
val mutableListOf = mutableListOf<Long>()
coroutineScope {
repeat(500000) {
launch(Dispatchers.IO) {
val id = TwitterSnowflakeIdGenerateService.generateId()
mutex.withLock {
mutableListOf.add(id)
}
}
}
}
assertEquals(0, mutableListOf.size - mutableListOf.toSet().size)
}
}

View File

@ -0,0 +1,348 @@
package dev.usbharu.hideout.core.infrastructure.timeline
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.domain.model.timeline.*
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
@ExtendWith(MockitoExtension::class)
class DefaultTimelineStoreTest {
@InjectMocks
lateinit var timelineStore: DefaultTimelineStore
@Mock
lateinit var timelineRepository: TimelineRepository
@Mock
lateinit var timelineRelationshipRepository: TimelineRelationshipRepository
@Mock
lateinit var filterRepository: FilterRepository
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var filterDomainService: FilterDomainService
@Mock
lateinit var internalTimelineObjectRepository: InternalTimelineObjectRepository
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val defaultTimelineStoreConfig = DefaultTimelineStoreConfig(500)
@Spy
val idGenerateService = TwitterSnowflakeIdGenerateService
@Test
fun addPost() = runTest {
val post = TestPostFactory.create()
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.replyId).isNull()
assertThat(it.replyActorId).isNull()
assertThat(it.repostId).isNull()
assertThat(it.repostActorId).isNull()
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
@Test
fun `addPost direct投稿は追加されない`() = runTest {
val post = TestPostFactory.create(visibility = Visibility.DIRECT)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがpublicでpostがUNLISTEDの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがpublicでpostがFOLLOWERSの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがUNLISTEDでpostがFOLLOWERSの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.UNLISTED,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがPRIVATEでpostがFOLLOWERSの時追加される() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.replyId).isNull()
assertThat(it.replyActorId).isNull()
assertThat(it.repostId).isNull()
assertThat(it.repostActorId).isNull()
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
}