diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt new file mode 100644 index 00000000..e9d17e68 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt @@ -0,0 +1,144 @@ +package dev.usbharu.hideout.core.service.post + +import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService +import dev.usbharu.hideout.application.config.CharacterLimit +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.query.PostQueryService +import dev.usbharu.hideout.core.service.timeline.TimelineService +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.exposed.exceptions.ExposedSQLException +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.mockStatic +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import org.springframework.dao.DuplicateKeyException +import utils.PostBuilder +import utils.UserBuilder +import java.time.Instant + +@ExtendWith(MockitoExtension::class) +class PostServiceImplTest { + + @Mock + private lateinit var postRepository: PostRepository + + @Mock + private lateinit var userRepository: UserRepository + + @Mock + private lateinit var timelineService: TimelineService + + @Mock + private lateinit var postQueryService: PostQueryService + + @Spy + private var postBuilder: Post.PostBuilder = Post.PostBuilder(CharacterLimit()) + + @Mock + private lateinit var apSendCreateService: ApSendCreateService + + @InjectMocks + private lateinit var postServiceImpl: PostServiceImpl + + @Test + fun `createLocal 正常にpostを作成できる`() = runTest { + + val now = Instant.now() + val post = PostBuilder.of(createdAt = now.toEpochMilli()) + + whenever(postRepository.save(eq(post))).doReturn(true) + whenever(postRepository.generateId()).doReturn(post.id) + whenever(userRepository.findById(eq(post.userId))).doReturn(UserBuilder.localUserOf(id = post.userId)) + whenever(timelineService.publishTimeline(eq(post), eq(true))).doReturn(Unit) + + mockStatic(Instant::class.java, Mockito.CALLS_REAL_METHODS).use { + + it.`when`(Instant::now).doReturn(now) + val createLocal = postServiceImpl.createLocal( + PostCreateDto( + post.text, + post.overview, + post.visibility, + post.repostId, + post.replyId, + post.userId, + post.mediaIds + ) + ) + + assertThat(createLocal).isEqualTo(post) + } + + verify(postRepository, times(1)).save(eq(post)) + verify(timelineService, times(1)).publishTimeline(eq(post), eq(true)) + verify(apSendCreateService, times(1)).createNote(eq(post)) + } + + @Test + fun `createRemote 正常にリモートのpostを作成できる`() = runTest { + val post = PostBuilder.of() + + whenever(postRepository.save(eq(post))).doReturn(true) + whenever(timelineService.publishTimeline(eq(post), eq(false))).doReturn(Unit) + + val createLocal = postServiceImpl.createRemote(post) + + assertThat(createLocal).isEqualTo(post) + + + verify(postRepository, times(1)).save(eq(post)) + verify(timelineService, times(1)).publishTimeline(eq(post), eq(false)) + } + + @Test + fun `createRemote 既に作成されていた場合はそのまま帰す`() = runTest { + val post = PostBuilder.of() + + whenever(postRepository.save(eq(post))).doReturn(false) + + val createLocal = postServiceImpl.createRemote(post) + + assertThat(createLocal).isEqualTo(post) + + verify(postRepository, times(1)).save(eq(post)) + verify(timelineService, times(0)).publishTimeline(any(), any()) + } + + @Test + fun `createRemote 既に作成されていることを検知できず例外が発生した場合はDBから取得して返す`() = runTest { + val post = PostBuilder.of() + + whenever(postRepository.save(eq(post))).doAnswer { throw ExposedSQLException(null, emptyList(), mock()) } + whenever(postQueryService.findByApId(eq(post.apId))).doReturn(post) + + val createLocal = postServiceImpl.createRemote(post) + + assertThat(createLocal).isEqualTo(post) + + verify(postRepository, times(1)).save(eq(post)) + verify(timelineService, times(0)).publishTimeline(any(), any()) + } + + @Test + fun `createRemote 既に作成されていることを検知出来ずタイムラインにpush出来なかった場合何もしない`() = runTest { + val post = PostBuilder.of() + + whenever(postRepository.save(eq(post))).doReturn(true) + whenever(timelineService.publishTimeline(eq(post), eq(false))).doThrow(DuplicateKeyException::class) + + val createLocal = postServiceImpl.createRemote(post) + + assertThat(createLocal).isEqualTo(post) + + verify(postRepository, times(1)).save(eq(post)) + verify(timelineService, times(1)).publishTimeline(eq(post), eq(false)) + } +} diff --git a/src/test/kotlin/utils/UserBuilder.kt b/src/test/kotlin/utils/UserBuilder.kt index 3674ac7e..b62a3cb5 100644 --- a/src/test/kotlin/utils/UserBuilder.kt +++ b/src/test/kotlin/utils/UserBuilder.kt @@ -20,15 +20,15 @@ object UserBuilder { screenName: String = name, description: String = "This user is test user.", password: String = "password-$id", - inbox: String = "https://$domain/$id/inbox", - outbox: String = "https://$domain/$id/outbox", - url: String = "https://$domain/$id/", + inbox: String = "https://$domain/users/$id/inbox", + outbox: String = "https://$domain/users/$id/outbox", + url: String = "https://$domain/users/$id", publicKey: String = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", privateKey: String = "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----", createdAt: Instant = Instant.now(), - keyId: String = "https://$domain/$id#pubkey", - followers: String = "https://$domain/$id/followers", - following: String = "https://$domain/$id/following" + keyId: String = "https://$domain/users/$id#pubkey", + followers: String = "https://$domain/users/$id/followers", + following: String = "https://$domain/users/$id/following" ): User { return userBuilder.of( id = id,