Merge branch 'develop' into feature/refactor

# Conflicts:
#	src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt
#	src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt
This commit is contained in:
usbharu 2023-11-01 13:38:45 +09:00
commit ab6f8b4f15
7 changed files with 322 additions and 168 deletions

View File

@ -47,6 +47,7 @@ class ApReactionJobServiceImpl(
val inbox = props[DeliverRemoveReactionJob.inbox]
val actor = props[DeliverRemoveReactionJob.actor]
val like = objectMapper.readValue<Like>(props[DeliverRemoveReactionJob.like])
val id = props[DeliverRemoveReactionJob.id]
val signer = userQueryService.findByUrl(actor)
@ -56,7 +57,7 @@ class ApReactionJobServiceImpl(
name = "Undo Reaction",
actor = actor,
`object` = like,
id = "${applicationConfig.url}/undo/note/${like.id}",
id = "${applicationConfig.url}/undo/note/$id",
published = Instant.now()
),
signer

View File

@ -33,21 +33,21 @@ class ApNoteJobServiceImpl(
objectMapper.readValue<List<Media>>(
props[DeliverPostJob.media]
)
val note = Note(
name = "Note",
id = postEntity.url,
attributedTo = actor,
content = postEntity.text,
published = Instant.ofEpochMilli(postEntity.createdAt).toString(),
to = listOf(APNoteServiceImpl.public, "$actor/follower"),
attachment = mediaList.map { Document(mediaType = "image/jpeg", url = it.url) }
)
val inbox = props[DeliverPostJob.inbox]
logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)
transaction.transaction {
val signer = userQueryService.findByUrl(actor)
val note = Note(
name = "Note",
id = postEntity.url,
attributedTo = actor,
content = postEntity.text,
published = Instant.ofEpochMilli(postEntity.createdAt).toString(),
to = listOfNotNull(APNoteServiceImpl.public, signer.followers),
attachment = mediaList.map { Document(mediaType = "image/jpeg", url = it.url) }
)
val inbox = props[DeliverPostJob.inbox]
logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)
apRequestService.apPost(
inbox,
Create(

View File

@ -414,31 +414,5 @@ class APNoteServiceImplTest {
assertEquals(note, fetchNote)
}
@Test
fun `createPostJob 新しい投稿のJob`() {
runTest {
val activityPubNoteService = ApNoteJobServiceImpl(
userQueryService = mock(),
objectMapper = objectMapper,
apRequestService = mock(),
transaction = TestTransaction,
applicationConfig = ApplicationConfig(URL("https://example.com"))
)
activityPubNoteService.createNoteJob(
JobProps(
data = mapOf<String, Any>(
DeliverPostJob.actor.name to "https://follower.example.com", DeliverPostJob.post.name to """{
"id": 1,
"userId": 1,
"text": "test text",
"createdAt": 132525324,
"visibility": 0,
"url": "https://example.com"
}""", DeliverPostJob.inbox.name to "https://follower.example.com/inbox", DeliverPostJob.media.name to "[]"
), json = Json
)
)
}
}
}

View File

@ -0,0 +1,78 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package dev.usbharu.hideout.service.ap.job
import dev.usbharu.hideout.config.ApplicationConfig
import dev.usbharu.hideout.domain.model.ap.Create
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.ap.APNoteServiceImpl
import dev.usbharu.hideout.service.ap.APRequestService
import kjob.core.job.JobProps
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Test
import org.mockito.kotlin.*
import utils.JsonObjectMapper
import utils.TestTransaction
import utils.UserBuilder
import java.net.URL
import java.time.Instant
class ApNoteJobServiceImplTest {
@Test
fun `createPostJob 新しい投稿のJob`() = runTest {
val apRequestService = mock<APRequestService>()
val user = UserBuilder.localUserOf()
val userQueryService = mock<UserQueryService> {
onBlocking { findByUrl(eq(user.url)) } doReturn user
}
val activityPubNoteService = ApNoteJobServiceImpl(
userQueryService = userQueryService,
objectMapper = JsonObjectMapper.objectMapper,
apRequestService = apRequestService,
transaction = TestTransaction,
applicationConfig = ApplicationConfig(URL("https://example.com"))
)
val remoteUserOf = UserBuilder.remoteUserOf()
activityPubNoteService.createNoteJob(
JobProps(
data = mapOf<String, Any>(
DeliverPostJob.actor.name to user.url,
DeliverPostJob.post.name to """{
"id": 1,
"userId": ${user.id},
"text": "test text",
"createdAt": 132525324,
"visibility": 0,
"url": "https://example.com"
}""",
DeliverPostJob.inbox.name to remoteUserOf.inbox,
DeliverPostJob.media.name to "[]"
), json = Json
)
)
val note = Note(
name = "Note",
id = "https://example.com",
attributedTo = user.url,
content = "test text",
published = Instant.ofEpochMilli(132525324).toString(),
to = listOfNotNull(APNoteServiceImpl.public, user.followers)
)
val create = Create(
name = "Create Note",
`object` = note,
actor = note.attributedTo,
id = "https://example.com/create/note/1"
)
verify(apRequestService, times(1)).apPost(
eq(remoteUserOf.inbox),
eq(create),
eq(user)
)
}
}

View File

@ -0,0 +1,128 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package dev.usbharu.hideout.service.ap.job
import dev.usbharu.hideout.config.ApplicationConfig
import dev.usbharu.hideout.domain.model.ap.Like
import dev.usbharu.hideout.domain.model.ap.Undo
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.ap.APRequestService
import kjob.core.job.JobProps
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.*
import utils.JsonObjectMapper.objectMapper
import utils.UserBuilder
import java.net.URL
import java.time.Instant
class ApReactionJobServiceImplTest {
@Test
fun `reactionJob Likeが配送される`() = runTest {
val localUser = UserBuilder.localUserOf()
val remoteUser = UserBuilder.remoteUserOf()
val userQueryService = mock<UserQueryService> {
onBlocking { findByUrl(localUser.url) } doReturn localUser
}
val apRequestService = mock<APRequestService>()
val apReactionJobServiceImpl = ApReactionJobServiceImpl(
userQueryService = userQueryService,
apRequestService = apRequestService,
applicationConfig = ApplicationConfig(URL("https://example.com")),
objectMapper = objectMapper
)
val postUrl = "${remoteUser.url}/posts/1234"
apReactionJobServiceImpl.reactionJob(
JobProps(
data = mapOf(
DeliverReactionJob.inbox.name to remoteUser.inbox,
DeliverReactionJob.actor.name to localUser.url,
DeliverReactionJob.postUrl.name to postUrl,
DeliverReactionJob.id.name to "1234",
DeliverReactionJob.reaction.name to "",
),
json = Json
)
)
val body = Like(
name = "Like",
actor = localUser.url,
`object` = postUrl,
id = "https://example.com/like/note/1234",
content = ""
)
verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser))
}
@Test
fun `removeReactionJob LikeのUndoが配送される`() = runTest {
val localUser = UserBuilder.localUserOf()
val remoteUser = UserBuilder.remoteUserOf()
val userQueryService = mock<UserQueryService> {
onBlocking { findByUrl(localUser.url) } doReturn localUser
}
val apRequestService = mock<APRequestService>()
val apReactionJobServiceImpl = ApReactionJobServiceImpl(
userQueryService = userQueryService,
apRequestService = apRequestService,
applicationConfig = ApplicationConfig(URL("https://example.com")),
objectMapper = objectMapper
)
val postUrl = "${remoteUser.url}/posts/1234"
val like = Like(
name = "Like",
actor = remoteUser.url,
`object` = postUrl,
id = "https://example.com/like/note/1234",
content = ""
)
val now = Instant.now()
val body = mockStatic(Instant::class.java).use {
it.`when`<Instant>(Instant::now).thenReturn(now)
apReactionJobServiceImpl.removeReactionJob(
JobProps(
data = mapOf(
DeliverRemoveReactionJob.inbox.name to remoteUser.inbox,
DeliverRemoveReactionJob.actor.name to localUser.url,
DeliverRemoveReactionJob.id.name to "1234",
DeliverRemoveReactionJob.like.name to objectMapper.writeValueAsString(like),
),
json = Json
)
)
Undo(
name = "Undo Reaction",
actor = localUser.url,
`object` = like,
id = "https://example.com/undo/note/1234",
published = now
)
}
verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser))
}
}

View File

@ -1,6 +1,6 @@
package dev.usbharu.hideout.service.ap.resource
import dev.usbharu.hideout.activitypub.domain.model.`object`.Object
import dev.usbharu.hideout.activitypub.service.common.APRequestService
import dev.usbharu.hideout.activitypub.service.common.APResourceResolveServiceImpl
import dev.usbharu.hideout.activitypub.service.common.InMemoryCacheManager
import dev.usbharu.hideout.activitypub.service.common.resolve
@ -9,192 +9,165 @@ import dev.usbharu.hideout.application.config.CharacterLimit
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.model.user.User
import dev.usbharu.hideout.core.domain.model.user.UserRepository
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.kotlin.*
import utils.UserBuilder
import java.net.URL
import java.time.Instant
import kotlin.test.assertEquals
import dev.usbharu.hideout.activitypub.domain.model.`object`.Object as APObject
@ExtendWith(MockitoExtension::class)
@Disabled
class APResourceResolveServiceImplTest {
val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com")))
val postBuilder = Post.PostBuilder(CharacterLimit())
class APResourceResolveServiceImplTest {
@Test
fun `単純な一回のリクエスト`() = runTest {
var count = 0
val httpClient = HttpClient(MockEngine { request ->
count++
respondOk("{}")
})
val userRepository = mock<UserRepository>()
whenever(userRepository.findById(any())).doReturn(
userBuilder.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now(),
keyId = ""
val user = UserBuilder.localUserOf()
whenever(userRepository.findById(any())) doReturn user
val apRequestService = mock<APRequestService> {
onBlocking {
apGet(
eq("https"),
eq(user),
eq(APObject::class.java)
)
} doReturn APObject(
emptyList()
)
)
}
val apResourceResolveService =
APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager())
APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager())
apResourceResolveService.resolve<Object>("https", 0)
apResourceResolveService.resolve<APObject>("https", 0)
assertEquals(1, count)
verify(apRequestService, times(1)).apGet(eq("https"), eq(user), eq(APObject::class.java))
}
@Test
fun 複数回の同じリクエストが重複して発行されない() = runTest {
var count = 0
val httpClient = HttpClient(MockEngine { request ->
count++
respondOk("{}")
})
val userRepository = mock<UserRepository>()
whenever(userRepository.findById(any())).doReturn(
userBuilder.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now(),
keyId = ""
val user = UserBuilder.localUserOf()
whenever(userRepository.findById(any())) doReturn user
val apRequestService = mock<APRequestService> {
onBlocking {
apGet(
eq("https"),
eq(user),
eq(APObject::class.java)
)
} doReturn APObject(
emptyList()
)
)
}
val apResourceResolveService =
APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager())
APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager())
apResourceResolveService.resolve<Object>("https", 0)
apResourceResolveService.resolve<Object>("https", 0)
apResourceResolveService.resolve<Object>("https", 0)
apResourceResolveService.resolve<Object>("https", 0)
apResourceResolveService.resolve<APObject>("https", 0)
apResourceResolveService.resolve<APObject>("https", 0)
apResourceResolveService.resolve<APObject>("https", 0)
apResourceResolveService.resolve<APObject>("https", 0)
assertEquals(1, count)
verify(apRequestService, times(1)).apGet(
eq("https"),
eq(user),
eq(APObject::class.java)
)
}
@Test
fun 複数回の同じリクエストが同時に発行されても重複して発行されない() = runTest {
var count = 0
val httpClient = HttpClient(MockEngine { request ->
count++
respondOk("{}")
})
val userRepository = mock<UserRepository>()
val user = UserBuilder.localUserOf()
whenever(userRepository.findById(any())).doReturn(
userBuilder.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now(),
keyId = ""
whenever(userRepository.findById(any())) doReturn user
val apRequestService = mock<APRequestService> {
onBlocking {
apGet(
eq("https"),
eq(user),
eq(APObject::class.java)
)
} doReturn APObject(
emptyList()
)
)
}
val apResourceResolveService =
APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager())
APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager())
repeat(10) {
awaitAll(
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<Object>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
async { apResourceResolveService.resolve<APObject>("https", 0) },
)
}
assertEquals(1, count)
verify(apRequestService, times(1)).apGet(
eq("https"),
eq(user),
eq(APObject::class.java)
)
}
@Test
fun 関係のないリクエストは発行する() = runTest {
var count = 0
val httpClient = HttpClient(MockEngine { request ->
count++
respondOk("{}")
})
val userRepository = mock<UserRepository>()
val user = UserBuilder.localUserOf()
whenever(userRepository.findById(any())).doReturn(
userBuilder.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now(),
keyId = ""
)
user
)
val apRequestService = mock<APRequestService> {
onBlocking {
apGet(
any(),
eq(user),
eq(APObject::class.java)
)
} doReturn APObject(
emptyList()
)
}
val apResourceResolveService =
APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager())
APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager())
apResourceResolveService.resolve<Object>("abcd", 0)
apResourceResolveService.resolve<Object>("1234", 0)
apResourceResolveService.resolve<Object>("aaaa", 0)
apResourceResolveService.resolve<APObject>("abcd", 0)
apResourceResolveService.resolve<APObject>("1234", 0)
apResourceResolveService.resolve<APObject>("aaaa", 0)
assertEquals(3, count)
verify(apRequestService, times(3)).apGet(
any(),
eq(user),
eq(APObject::class.java)
)
}

View File

@ -44,8 +44,8 @@ object UserBuilder {
privateKey = privateKey,
createdAt = createdAt,
keyId = keyId,
followers = following,
following = followers
followers = followers,
following = following
)
}
@ -78,8 +78,8 @@ object UserBuilder {
privateKey = null,
createdAt = createdAt,
keyId = keyId,
followers = following,
following = followers
followers = followers,
following = following
)
}