diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt index e35f37d1..5a8e088c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt @@ -47,6 +47,7 @@ class ApReactionJobServiceImpl( val inbox = props[DeliverRemoveReactionJob.inbox] val actor = props[DeliverRemoveReactionJob.actor] val like = objectMapper.readValue(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 diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/object/note/ApNoteJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/object/note/ApNoteJobServiceImpl.kt index ba2804f5..94a1cfae 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/object/note/ApNoteJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/object/note/ApNoteJobServiceImpl.kt @@ -33,21 +33,21 @@ class ApNoteJobServiceImpl( objectMapper.readValue>( 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( diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt index 03ee3d48..134432dd 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt @@ -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( - 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 - ) - ) - } - } } diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImplTest.kt new file mode 100644 index 00000000..a0fc8d6d --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImplTest.kt @@ -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() + val user = UserBuilder.localUserOf() + val userQueryService = mock { + 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( + 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) + ) + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImplTest.kt new file mode 100644 index 00000000..cb3e85d2 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImplTest.kt @@ -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 { + onBlocking { findByUrl(localUser.url) } doReturn localUser + } + val apRequestService = mock() + 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 { + onBlocking { findByUrl(localUser.url) } doReturn localUser + } + val apRequestService = mock() + 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::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)) + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt index 8f77d9ff..83225972 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt @@ -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() - 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 { + 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("https", 0) + apResourceResolveService.resolve("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() - 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 { + 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("https", 0) - apResourceResolveService.resolve("https", 0) - apResourceResolveService.resolve("https", 0) - apResourceResolveService.resolve("https", 0) + apResourceResolveService.resolve("https", 0) + apResourceResolveService.resolve("https", 0) + apResourceResolveService.resolve("https", 0) + apResourceResolveService.resolve("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() + 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 { + 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("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("https", 0) }, + async { apResourceResolveService.resolve("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() + 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 { + onBlocking { + apGet( + any(), + eq(user), + eq(APObject::class.java) + ) + } doReturn APObject( + emptyList() + ) + } + val apResourceResolveService = - APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager()) + APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager()) - apResourceResolveService.resolve("abcd", 0) - apResourceResolveService.resolve("1234", 0) - apResourceResolveService.resolve("aaaa", 0) + apResourceResolveService.resolve("abcd", 0) + apResourceResolveService.resolve("1234", 0) + apResourceResolveService.resolve("aaaa", 0) - assertEquals(3, count) + verify(apRequestService, times(3)).apGet( + any(), + eq(user), + eq(APObject::class.java) + ) } diff --git a/src/test/kotlin/utils/UserBuilder.kt b/src/test/kotlin/utils/UserBuilder.kt index f5ea6688..3674ac7e 100644 --- a/src/test/kotlin/utils/UserBuilder.kt +++ b/src/test/kotlin/utils/UserBuilder.kt @@ -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 ) }