diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImpl.kt index 8146a597..9cea9c9f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/ap/job/ApNoteJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/job/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/main/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImpl.kt index 45736662..58d85ec3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/ap/job/ApReactionJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/job/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/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt index 1c8bf1f6..ec885f0d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt @@ -1,4 +1,4 @@ -@file:OptIn(ExperimentalCoroutinesApi::class) @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:OptIn(ExperimentalCoroutinesApi::class) package dev.usbharu.hideout.service.ap @@ -20,7 +20,6 @@ import dev.usbharu.hideout.query.PostQueryService 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.resource.APResourceResolveService import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService import dev.usbharu.hideout.service.job.JobQueueParentService @@ -35,12 +34,10 @@ import io.ktor.http.* import io.ktor.http.content.* import io.ktor.util.* import io.ktor.util.date.* -import kjob.core.job.JobProps import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -48,7 +45,6 @@ import org.mockito.Mockito.anyLong import org.mockito.kotlin.* import utils.JsonObjectMapper.objectMapper import utils.PostBuilder -import utils.TestTransaction import utils.UserBuilder import java.net.URL import java.time.Instant @@ -412,31 +408,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 87f5a553..2450480b 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 @@ -6,25 +6,19 @@ import dev.usbharu.hideout.domain.model.ap.Object import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.repository.UserRepository -import io.ktor.client.* -import io.ktor.client.engine.mock.* +import dev.usbharu.hideout.service.ap.APRequestService 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 @ExtendWith(MockitoExtension::class) -@Disabled + class APResourceResolveServiceImplTest { val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) @@ -33,109 +27,89 @@ 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(Object::class.java) + ) + } doReturn dev.usbharu.hideout.domain.model.ap.Object( + emptyList() + ) + } val apResourceResolveService = - APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager()) + APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager()) apResourceResolveService.resolve("https", 0) - assertEquals(1, count) + verify(apRequestService, times(1)).apGet(eq("https"), eq(user), eq(Object::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(Object::class.java) + ) + } doReturn dev.usbharu.hideout.domain.model.ap.Object( + 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) - assertEquals(1, count) + verify(apRequestService, times(1)).apGet( + eq("https"), + eq(user), + eq(Object::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(Object::class.java) + ) + } doReturn dev.usbharu.hideout.domain.model.ap.Object( + emptyList() ) - ) - + } val apResourceResolveService = - APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager()) + APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager()) repeat(10) { awaitAll( @@ -153,45 +127,47 @@ class APResourceResolveServiceImplTest { ) } - assertEquals(1, count) + verify(apRequestService, times(1)).apGet( + eq("https"), + eq(user), + eq(Object::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(Object::class.java) + ) + } doReturn dev.usbharu.hideout.domain.model.ap.Object( + emptyList() + ) + } + val apResourceResolveService = - APResourceResolveServiceImpl(mock(), userRepository, InMemoryCacheManager()) + APResourceResolveServiceImpl(apRequestService, userRepository, InMemoryCacheManager()) apResourceResolveService.resolve("abcd", 0) apResourceResolveService.resolve("1234", 0) apResourceResolveService.resolve("aaaa", 0) - assertEquals(3, count) + verify(apRequestService, times(3)).apGet( + any(), + eq(user), + eq(Object::class.java) + ) } diff --git a/src/test/kotlin/utils/UserBuilder.kt b/src/test/kotlin/utils/UserBuilder.kt index d3294786..b76ce056 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 ) }