mirror of https://github.com/usbharu/Hideout.git
test: APNoteServiceImplのテストを追加
This commit is contained in:
parent
f2fe870888
commit
8e4a0d6584
|
@ -1,34 +1,58 @@
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class) @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.service.ap
|
package dev.usbharu.hideout.service.ap
|
||||||
|
|
||||||
import dev.usbharu.hideout.config.ApplicationConfig
|
import dev.usbharu.hideout.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.config.CharacterLimit
|
import dev.usbharu.hideout.config.CharacterLimit
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Image
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Key
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Note
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
|
import dev.usbharu.hideout.exception.ap.FailedToGetActivityPubResourceException
|
||||||
import dev.usbharu.hideout.query.FollowerQueryService
|
import dev.usbharu.hideout.query.FollowerQueryService
|
||||||
import dev.usbharu.hideout.query.MediaQueryService
|
import dev.usbharu.hideout.query.MediaQueryService
|
||||||
|
import dev.usbharu.hideout.query.PostQueryService
|
||||||
import dev.usbharu.hideout.query.UserQueryService
|
import dev.usbharu.hideout.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.repository.PostRepository
|
||||||
|
import dev.usbharu.hideout.service.ap.APNoteServiceImpl.Companion.public
|
||||||
import dev.usbharu.hideout.service.ap.job.ApNoteJobServiceImpl
|
import dev.usbharu.hideout.service.ap.job.ApNoteJobServiceImpl
|
||||||
|
import dev.usbharu.hideout.service.ap.resource.APResourceResolveService
|
||||||
|
import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
|
import dev.usbharu.hideout.service.post.PostService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.mock.*
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
|
import io.ktor.client.utils.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.http.content.*
|
||||||
|
import io.ktor.util.*
|
||||||
|
import io.ktor.util.date.*
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
import org.mockito.Mockito.anyLong
|
import org.mockito.Mockito.anyLong
|
||||||
import org.mockito.Mockito.eq
|
|
||||||
import org.mockito.kotlin.*
|
import org.mockito.kotlin.*
|
||||||
import utils.JsonObjectMapper.objectMapper
|
import utils.JsonObjectMapper.objectMapper
|
||||||
|
import utils.PostBuilder
|
||||||
import utils.TestTransaction
|
import utils.TestTransaction
|
||||||
|
import utils.UserBuilder
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class APNoteServiceImplTest {
|
class APNoteServiceImplTest {
|
||||||
|
|
||||||
|
@ -58,8 +82,7 @@ class APNoteServiceImplTest {
|
||||||
publicKey = "",
|
publicKey = "",
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
keyId = "a"
|
keyId = "a"
|
||||||
),
|
), userBuilder.of(
|
||||||
userBuilder.of(
|
|
||||||
3L,
|
3L,
|
||||||
"follower2",
|
"follower2",
|
||||||
"follower2.example.com",
|
"follower2.example.com",
|
||||||
|
@ -95,8 +118,7 @@ class APNoteServiceImplTest {
|
||||||
onBlocking { findFollowersById(eq(1L)) } doReturn followers
|
onBlocking { findFollowersById(eq(1L)) } doReturn followers
|
||||||
}
|
}
|
||||||
val jobQueueParentService = mock<JobQueueParentService>()
|
val jobQueueParentService = mock<JobQueueParentService>()
|
||||||
val activityPubNoteService =
|
val activityPubNoteService = APNoteServiceImpl(
|
||||||
APNoteServiceImpl(
|
|
||||||
jobQueueParentService = jobQueueParentService,
|
jobQueueParentService = jobQueueParentService,
|
||||||
postRepository = mock(),
|
postRepository = mock(),
|
||||||
apUserService = mock(),
|
apUserService = mock(),
|
||||||
|
@ -110,13 +132,7 @@ class APNoteServiceImplTest {
|
||||||
postBuilder = postBuilder
|
postBuilder = postBuilder
|
||||||
)
|
)
|
||||||
val postEntity = postBuilder.of(
|
val postEntity = postBuilder.of(
|
||||||
1L,
|
1L, 1L, null, "test text", 1L, Visibility.PUBLIC, "https://example.com"
|
||||||
1L,
|
|
||||||
null,
|
|
||||||
"test text",
|
|
||||||
1L,
|
|
||||||
Visibility.PUBLIC,
|
|
||||||
"https://example.com"
|
|
||||||
)
|
)
|
||||||
activityPubNoteService.createNote(postEntity)
|
activityPubNoteService.createNote(postEntity)
|
||||||
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
|
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
|
||||||
|
@ -124,18 +140,281 @@ class APNoteServiceImplTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createPostJob 新しい投稿のJob`() {
|
fun `fetchNote(String,String) ノートが既に存在する場合はDBから取得したものを返す`() = runTest {
|
||||||
runTest {
|
val url = "https://example.com/note"
|
||||||
val mediaQueryService = mock<MediaQueryService> {
|
val post = PostBuilder.of()
|
||||||
onBlocking { findByPostId(anyLong()) } doReturn emptyList()
|
|
||||||
|
val postQueryService = mock<PostQueryService> {
|
||||||
|
onBlocking { findByUrl(eq(url)) } doReturn post
|
||||||
|
}
|
||||||
|
val user = UserBuilder.localUserOf(id = post.userId)
|
||||||
|
val userQueryService = mock<UserQueryService> {
|
||||||
|
onBlocking { findById(eq(post.userId)) } doReturn user
|
||||||
|
}
|
||||||
|
val apNoteServiceImpl = APNoteServiceImpl(
|
||||||
|
jobQueueParentService = mock(),
|
||||||
|
postRepository = mock(),
|
||||||
|
apUserService = mock(),
|
||||||
|
userQueryService = userQueryService,
|
||||||
|
followerQueryService = mock(),
|
||||||
|
postQueryService = postQueryService,
|
||||||
|
mediaQueryService = mock(),
|
||||||
|
objectMapper = objectMapper,
|
||||||
|
postService = mock(),
|
||||||
|
apResourceResolveService = mock(),
|
||||||
|
postBuilder = Post.PostBuilder(CharacterLimit())
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = apNoteServiceImpl.fetchNote(url)
|
||||||
|
|
||||||
|
val expected = Note(
|
||||||
|
name = "Post",
|
||||||
|
id = post.apId,
|
||||||
|
attributedTo = user.url,
|
||||||
|
content = post.text,
|
||||||
|
published = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||||
|
to = listOfNotNull(public, user.followers),
|
||||||
|
sensitive = post.sensitive,
|
||||||
|
cc = listOfNotNull(public, user.followers),
|
||||||
|
inReplyTo = null
|
||||||
|
)
|
||||||
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
val httpClient = HttpClient(
|
@Test
|
||||||
MockEngine { httpRequestData ->
|
fun `fetchNote(String,String) ノートがDBに存在しない場合リモートに取得しにいく`() = runTest {
|
||||||
assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString())
|
val url = "https://example.com/note"
|
||||||
respondOk()
|
val post = PostBuilder.of()
|
||||||
|
|
||||||
|
val postQueryService = mock<PostQueryService> {
|
||||||
|
onBlocking { findByUrl(eq(url)) } doThrow FailedToGetResourcesException()
|
||||||
|
onBlocking { findByApId(eq(post.apId)) } doReturn post
|
||||||
}
|
}
|
||||||
|
val user = UserBuilder.localUserOf(id = post.userId)
|
||||||
|
val userQueryService = mock<UserQueryService> {
|
||||||
|
onBlocking { findById(eq(post.userId)) } doReturn user
|
||||||
|
}
|
||||||
|
val note = Note(
|
||||||
|
name = "Post",
|
||||||
|
id = post.apId,
|
||||||
|
attributedTo = user.url,
|
||||||
|
content = post.text,
|
||||||
|
published = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||||
|
to = listOfNotNull(public, user.followers),
|
||||||
|
sensitive = post.sensitive,
|
||||||
|
cc = listOfNotNull(public, user.followers),
|
||||||
|
inReplyTo = null
|
||||||
)
|
)
|
||||||
|
val apResourceResolveService = mock<APResourceResolveService> {
|
||||||
|
onBlocking { resolve<Note>(eq(url), any(), isNull<Long>()) } doReturn note
|
||||||
|
}
|
||||||
|
val apNoteServiceImpl = APNoteServiceImpl(
|
||||||
|
jobQueueParentService = mock(),
|
||||||
|
postRepository = mock(),
|
||||||
|
apUserService = mock(),
|
||||||
|
userQueryService = userQueryService,
|
||||||
|
followerQueryService = mock(),
|
||||||
|
postQueryService = postQueryService,
|
||||||
|
mediaQueryService = mock(),
|
||||||
|
objectMapper = objectMapper,
|
||||||
|
postService = mock(),
|
||||||
|
apResourceResolveService = apResourceResolveService,
|
||||||
|
postBuilder = Post.PostBuilder(CharacterLimit())
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = apNoteServiceImpl.fetchNote(url)
|
||||||
|
|
||||||
|
assertEquals(note, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(InternalAPI::class)
|
||||||
|
@Test
|
||||||
|
fun `fetchNote(String,String) ノートをリモートから取得した際にエラーが返ってきたらFailedToGetActivityPubResourceExceptionがthrowされる`() =
|
||||||
|
runTest {
|
||||||
|
val url = "https://example.com/note"
|
||||||
|
val post = PostBuilder.of()
|
||||||
|
|
||||||
|
val postQueryService = mock<PostQueryService> {
|
||||||
|
onBlocking { findByUrl(eq(url)) } doThrow FailedToGetResourcesException()
|
||||||
|
onBlocking { findByApId(eq(post.apId)) } doReturn post
|
||||||
|
}
|
||||||
|
val user = UserBuilder.localUserOf(id = post.userId)
|
||||||
|
val userQueryService = mock<UserQueryService> {
|
||||||
|
onBlocking { findById(eq(post.userId)) } doReturn user
|
||||||
|
}
|
||||||
|
val note = Note(
|
||||||
|
name = "Post",
|
||||||
|
id = post.apId,
|
||||||
|
attributedTo = user.url,
|
||||||
|
content = post.text,
|
||||||
|
published = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||||
|
to = listOfNotNull(public, user.followers),
|
||||||
|
sensitive = post.sensitive,
|
||||||
|
cc = listOfNotNull(public, user.followers),
|
||||||
|
inReplyTo = null
|
||||||
|
)
|
||||||
|
val apResourceResolveService = mock<APResourceResolveService> {
|
||||||
|
val responseData = HttpResponseData(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
GMTDate(),
|
||||||
|
Headers.Empty,
|
||||||
|
HttpProtocolVersion.HTTP_1_1,
|
||||||
|
NullBody,
|
||||||
|
Dispatchers.IO
|
||||||
|
)
|
||||||
|
onBlocking { resolve<Note>(eq(url), any(), isNull<Long>()) } doThrow ClientRequestException(
|
||||||
|
DefaultHttpResponse(
|
||||||
|
HttpClientCall(
|
||||||
|
HttpClient(), HttpRequestData(
|
||||||
|
Url("http://example.com"),
|
||||||
|
HttpMethod.Get,
|
||||||
|
Headers.Empty,
|
||||||
|
EmptyContent,
|
||||||
|
Job(null),
|
||||||
|
Attributes()
|
||||||
|
), responseData
|
||||||
|
), responseData
|
||||||
|
), ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val apNoteServiceImpl = APNoteServiceImpl(
|
||||||
|
jobQueueParentService = mock(),
|
||||||
|
postRepository = mock(),
|
||||||
|
apUserService = mock(),
|
||||||
|
userQueryService = userQueryService,
|
||||||
|
followerQueryService = mock(),
|
||||||
|
postQueryService = postQueryService,
|
||||||
|
mediaQueryService = mock(),
|
||||||
|
objectMapper = objectMapper,
|
||||||
|
postService = mock(),
|
||||||
|
apResourceResolveService = apResourceResolveService,
|
||||||
|
postBuilder = Post.PostBuilder(CharacterLimit())
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThrows<FailedToGetActivityPubResourceException> { apNoteServiceImpl.fetchNote(url) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetchNote(Note,String) DBに無いNoteは保存される`() = runTest {
|
||||||
|
val user = UserBuilder.localUserOf()
|
||||||
|
val generateId = TwitterSnowflakeIdGenerateService.generateId()
|
||||||
|
val post = PostBuilder.of(id = generateId, userId = user.id)
|
||||||
|
val postQueryService = mock<PostQueryService> {
|
||||||
|
onBlocking { findByApId(eq(post.apId)) } doThrow FailedToGetResourcesException()
|
||||||
|
}
|
||||||
|
val postRepository = mock<PostRepository> {
|
||||||
|
onBlocking { generateId() } doReturn generateId
|
||||||
|
}
|
||||||
|
val person = Person(
|
||||||
|
name = user.name,
|
||||||
|
id = user.url,
|
||||||
|
preferredUsername = user.name,
|
||||||
|
summary = user.name,
|
||||||
|
inbox = user.inbox,
|
||||||
|
outbox = user.outbox,
|
||||||
|
url = user.url,
|
||||||
|
icon = Image(
|
||||||
|
name = user.url + "/icon.png", mediaType = "image/png", url = user.url + "/icon.png"
|
||||||
|
),
|
||||||
|
publicKey = Key(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "Public Key",
|
||||||
|
id = user.keyId,
|
||||||
|
owner = user.url,
|
||||||
|
publicKeyPem = user.publicKey
|
||||||
|
),
|
||||||
|
endpoints = mapOf("sharedInbox" to "https://example.com/inbox"),
|
||||||
|
following = user.following,
|
||||||
|
followers = user.followers
|
||||||
|
)
|
||||||
|
val apUserService = mock<APUserService> {
|
||||||
|
onBlocking { fetchPersonWithEntity(eq(user.url), anyOrNull()) } doReturn (person to user)
|
||||||
|
}
|
||||||
|
val postService = mock<PostService>()
|
||||||
|
val apNoteServiceImpl = APNoteServiceImpl(
|
||||||
|
jobQueueParentService = mock(),
|
||||||
|
postRepository = postRepository,
|
||||||
|
apUserService = apUserService,
|
||||||
|
userQueryService = mock(),
|
||||||
|
followerQueryService = mock(),
|
||||||
|
postQueryService = postQueryService,
|
||||||
|
mediaQueryService = mock(),
|
||||||
|
objectMapper = objectMapper,
|
||||||
|
postService = postService,
|
||||||
|
apResourceResolveService = mock(),
|
||||||
|
postBuilder = postBuilder
|
||||||
|
)
|
||||||
|
|
||||||
|
val note = Note(
|
||||||
|
name = "Post",
|
||||||
|
id = post.apId,
|
||||||
|
attributedTo = user.url,
|
||||||
|
content = post.text,
|
||||||
|
published = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||||
|
to = listOfNotNull(public, user.followers),
|
||||||
|
sensitive = post.sensitive,
|
||||||
|
cc = listOfNotNull(public, user.followers),
|
||||||
|
inReplyTo = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val fetchNote = apNoteServiceImpl.fetchNote(note, null)
|
||||||
|
verify(postService, times(1)).createRemote(
|
||||||
|
eq(
|
||||||
|
PostBuilder.of(
|
||||||
|
id = generateId, userId = user.id, createdAt = post.createdAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(note, fetchNote)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetchNote DBに存在する場合取得して返す`() = runTest {
|
||||||
|
|
||||||
|
val user = UserBuilder.localUserOf()
|
||||||
|
val post = PostBuilder.of(userId = user.id)
|
||||||
|
|
||||||
|
val postQueryService = mock<PostQueryService> {
|
||||||
|
onBlocking { findByApId(eq(post.apId)) } doReturn post
|
||||||
|
}
|
||||||
|
val userQueryService = mock<UserQueryService> {
|
||||||
|
onBlocking { findById(eq(user.id)) } doReturn user
|
||||||
|
}
|
||||||
|
val apNoteServiceImpl = APNoteServiceImpl(
|
||||||
|
jobQueueParentService = mock(),
|
||||||
|
postRepository = mock(),
|
||||||
|
apUserService = mock(),
|
||||||
|
userQueryService = userQueryService,
|
||||||
|
followerQueryService = mock(),
|
||||||
|
postQueryService = postQueryService,
|
||||||
|
mediaQueryService = mock(),
|
||||||
|
objectMapper = objectMapper,
|
||||||
|
postService = mock(),
|
||||||
|
apResourceResolveService = mock(),
|
||||||
|
postBuilder = postBuilder
|
||||||
|
)
|
||||||
|
|
||||||
|
val note = Note(
|
||||||
|
name = "Post",
|
||||||
|
id = post.apId,
|
||||||
|
attributedTo = user.url,
|
||||||
|
content = post.text,
|
||||||
|
published = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||||
|
to = listOfNotNull(public, user.followers),
|
||||||
|
sensitive = post.sensitive,
|
||||||
|
cc = listOfNotNull(public, user.followers),
|
||||||
|
inReplyTo = null
|
||||||
|
)
|
||||||
|
|
||||||
|
val fetchNote = apNoteServiceImpl.fetchNote(note, null)
|
||||||
|
assertEquals(note, fetchNote)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `createPostJob 新しい投稿のJob`() {
|
||||||
|
runTest {
|
||||||
val activityPubNoteService = ApNoteJobServiceImpl(
|
val activityPubNoteService = ApNoteJobServiceImpl(
|
||||||
|
|
||||||
userQueryService = mock(),
|
userQueryService = mock(),
|
||||||
|
@ -147,19 +426,15 @@ class APNoteServiceImplTest {
|
||||||
activityPubNoteService.createNoteJob(
|
activityPubNoteService.createNoteJob(
|
||||||
JobProps(
|
JobProps(
|
||||||
data = mapOf<String, Any>(
|
data = mapOf<String, Any>(
|
||||||
DeliverPostJob.actor.name to "https://follower.example.com",
|
DeliverPostJob.actor.name to "https://follower.example.com", DeliverPostJob.post.name to """{
|
||||||
DeliverPostJob.post.name to """{
|
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"userId": 1,
|
"userId": 1,
|
||||||
"text": "test text",
|
"text": "test text",
|
||||||
"createdAt": 132525324,
|
"createdAt": 132525324,
|
||||||
"visibility": 0,
|
"visibility": 0,
|
||||||
"url": "https://example.com"
|
"url": "https://example.com"
|
||||||
}""",
|
}""", DeliverPostJob.inbox.name to "https://follower.example.com/inbox", DeliverPostJob.media.name to "[]"
|
||||||
DeliverPostJob.inbox.name to "https://follower.example.com/inbox",
|
), json = Json
|
||||||
DeliverPostJob.media.name to "[]"
|
|
||||||
),
|
|
||||||
json = Json
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue