From 3ab86fc5112f93d5239b3bf5c843440d09b06375 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:06:43 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RegisterApplicationApplicationService.kt | 97 ++++++------ .../UserDeleteFilterApplicationService.kt | 15 +- .../filter/UserGetFilterApplicationService.kt | 16 +- ...erAcceptFollowRequestApplicationService.kt | 9 +- .../core/domain/model/filter/Filter.kt | 14 ++ ...gisterApplicationApplicationServiceTest.kt | 84 ++++++++++ .../UserDeleteFilterApplicationServiceTest.kt | 89 +++++++++++ .../UserGetFilterApplicationServiceTest.kt | 86 +++++++++++ ...RegisterLocalPostApplicationServiceTest.kt | 33 +++- ...ceptFollowRequestApplicationServiceTest.kt | 81 ++++++++++ .../timeline/DefaultTimelineStoreTest.kt | 146 ++++++++++++++++++ .../mastodon/interfaces/api/SpringAppApi.kt | 3 +- 12 files changed, 606 insertions(+), 67 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt index 104cd3e0..10890e85 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt @@ -16,14 +16,17 @@ package dev.usbharu.hideout.core.application.application +import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.application.Application import dev.usbharu.hideout.core.domain.model.application.ApplicationId import dev.usbharu.hideout.core.domain.model.application.ApplicationName import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator +import org.slf4j.LoggerFactory import org.springframework.security.oauth2.core.AuthorizationGrantType import org.springframework.security.oauth2.core.ClientAuthenticationMethod import org.springframework.security.oauth2.server.authorization.client.RegisteredClient @@ -39,53 +42,57 @@ class RegisterApplicationApplicationService( private val passwordEncoder: PasswordEncoder, private val secureTokenGenerator: SecureTokenGenerator, private val registeredClientRepository: RegisteredClientRepository, - private val transaction: Transaction, + transaction: Transaction, private val applicationRepository: ApplicationRepository, -) { - suspend fun register(registerApplication: RegisterApplication): RegisteredApplication { - return transaction.transaction { - val id = idGenerateService.generateId() - val clientSecret = secureTokenGenerator.generate() - val registeredClient = RegisteredClient - .withId(id.toString()) - .clientId(id.toString()) - .clientSecret(passwordEncoder.encode(clientSecret)) - .clientName(registerApplication.name) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .apply { - if (registerApplication.useRefreshToken) { - authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - } else { - tokenSettings( - TokenSettings - .builder() - .accessTokenTimeToLive(Duration.ofSeconds(31536000000)) - .build() - ) - } - } - .redirectUris { set -> - set.addAll(registerApplication.redirectUris.map { it.toString() }) - } - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .scopes { it.addAll(registerApplication.scopes) } - .build() - registeredClientRepository.save(registeredClient) +) : AbstractApplicationService(transaction, logger) { - val application = Application(ApplicationId(id), ApplicationName(registerApplication.name)) + override suspend fun internalExecute(command: RegisterApplication, principal: Principal): RegisteredApplication { + val id = idGenerateService.generateId() + val clientSecret = secureTokenGenerator.generate() + val registeredClient = RegisteredClient + .withId(id.toString()) + .clientId(id.toString()) + .clientSecret(passwordEncoder.encode(clientSecret)) + .clientName(command.name) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .apply { + if (command.useRefreshToken) { + authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + } else { + tokenSettings( + TokenSettings + .builder() + .accessTokenTimeToLive(Duration.ofSeconds(31536000000)) + .build() + ) + } + } + .redirectUris { set -> + set.addAll(command.redirectUris.map { it.toString() }) + } + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .scopes { it.addAll(command.scopes) } + .build() + registeredClientRepository.save(registeredClient) - applicationRepository.save(application) - RegisteredApplication( - id = id, - name = registerApplication.name, - clientSecret = clientSecret, - clientId = id.toString(), - redirectUris = registerApplication.redirectUris - ) - } + val application = Application(ApplicationId(id), ApplicationName(command.name)) + + applicationRepository.save(application) + return RegisteredApplication( + id = id, + name = command.name, + clientSecret = clientSecret, + clientId = id.toString(), + redirectUris = command.redirectUris + ) + } + + + companion object { + private val logger = LoggerFactory.getLogger(RegisterApplicationApplicationService::class.java) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt index 1d6dbb28..9333ec83 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt @@ -16,22 +16,27 @@ package dev.usbharu.hideout.core.application.filter -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: DeleteFilter, principal: Principal) { - val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("not found") + override suspend fun internalExecute(command: DeleteFilter, principal: FromApi) { + val filter = + filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("not found") + if (filter.userDetailId != principal.userDetailId) { + throw PermissionDeniedException() + } filterRepository.delete(filter) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt index fc7ea255..cecb5a5d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt @@ -16,23 +16,27 @@ package dev.usbharu.hideout.core.application.filter -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: GetFilter, principal: Principal): Filter { - val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("Not Found") - + override suspend fun internalExecute(command: GetFilter, principal: FromApi): Filter { + val filter = + filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("Not Found") + if (filter.userDetailId != principal.userDetailId) { + throw PermissionDeniedException() + } return Filter.of(filter) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt index a80497e6..1f17a0df 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction @@ -23,7 +24,6 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.support.principal.FromApi -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -32,18 +32,17 @@ class UserAcceptFollowRequestApplicationService( private val relationshipRepository: RelationshipRepository, transaction: Transaction, private val actorRepository: ActorRepository, - private val userDetailRepository: UserDetailRepository, ) : LocalUserAbstractApplicationService(transaction, logger) { override suspend fun internalExecute(command: AcceptFollowRequest, principal: FromApi) { - val userDetail = userDetailRepository.findById(principal.userDetailId)!! - val actor = actorRepository.findById(userDetail.actorId)!! + val actor = actorRepository.findById(principal.actorId) + ?: throw InternalServerException("Actor ${principal.actorId} not found") val targetId = ActorId(command.sourceActorId) val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) - ?: throw Exception("Follow request not found") + ?: throw InternalServerException("Follow request not found") relationship.acceptFollowRequest() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index dc26a715..9a41fa9e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -56,6 +56,20 @@ class Filter( ) } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Filter + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + companion object { fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean { return when (action) { diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt new file mode 100644 index 00000000..e1f73b83 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt @@ -0,0 +1,84 @@ +package dev.usbharu.hideout.core.application.application + +import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService +import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityPasswordEncoder +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.oauth2.core.AuthorizationGrantType +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository +import utils.TestTransaction +import java.net.URI +import java.time.Duration + +@ExtendWith(MockitoExtension::class) +class RegisterApplicationApplicationServiceTest { + @InjectMocks + lateinit var service: RegisterApplicationApplicationService + + @Mock + lateinit var secureTokenGenerator: SecureTokenGenerator + + @Mock + lateinit var registeredClientRepository: RegisteredClientRepository + + @Mock + lateinit var applicationRepository: ApplicationRepository + + @Spy + val idGenerateService = TwitterSnowflakeIdGenerateService + + @Spy + val passwordEncoder = SpringSecurityPasswordEncoder(BCryptPasswordEncoder()) + + @Spy + val transaction = TestTransaction + + @Test + fun applicationを作成できる() = runTest { + whenever(secureTokenGenerator.generate()).doReturn("very-secure-token") + + service.execute( + RegisterApplication("test", setOf(URI.create("https://example.com")), false, setOf("write")), + Anonymous + ) + + argumentCaptor { + verify(registeredClientRepository).save(capture()) + val first = allValues.first() + assertThat(first.tokenSettings.accessTokenTimeToLive).isGreaterThanOrEqualTo(Duration.ofSeconds(31536000000)) + + } + } + + @Test + fun refreshTokenを有効化してapplicationを作成できる() = runTest { + whenever(secureTokenGenerator.generate()).doReturn("very-secure-token") + + service.execute( + RegisterApplication("test", setOf(URI.create("https://example.com")), true, setOf("write")), + Anonymous + ) + + argumentCaptor { + verify(registeredClientRepository).save(capture()) + val first = allValues.first() + assertThat(first.authorizationGrantTypes).contains(AuthorizationGrantType.REFRESH_TOKEN) + + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt new file mode 100644 index 00000000..3928ea45 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt @@ -0,0 +1,89 @@ +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.filter.Filter +import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserDeleteFilterApplicationServiceTest { + + @InjectMocks + lateinit var service: UserDeleteFilterApplicationService + + @Spy + val transaction = TestTransaction + + @Mock + lateinit var filterRepository: FilterRepository + + @Test + fun フィルターを削除できる() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + service.execute( + DeleteFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + + verify(filterRepository, times(1)).delete(eq(filter)) + } + + @Test + fun フィルターが見つからない場合失敗() = runTest { + assertThrows { + service.execute( + DeleteFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + } + } + + @Test + fun フィルターのオーナー以外は失敗() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + assertThrows { + service.execute( + DeleteFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + + verify(filterRepository, never()).delete(any()) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt new file mode 100644 index 00000000..b3b2db82 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt @@ -0,0 +1,86 @@ +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.filter.Filter +import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserGetFilterApplicationServiceTest { + @InjectMocks + lateinit var service: UserGetFilterApplicationService + + @Mock + lateinit var filterRepository: FilterRepository + + @Spy + val transaction = TestTransaction + + @Test + fun オーナーのみ取得できる() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + service.execute( + GetFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + } + + @Test + fun オーナー以外は失敗() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + + assertThrows { + service.execute( + GetFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + } + + @Test + fun フィルターが見つからない場合失敗() = runTest { + assertThrows { + service.execute( + GetFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt index 9cd8cb4e..40cc7162 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt @@ -1,9 +1,11 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.support.acct.Acct import dev.usbharu.hideout.core.domain.model.support.principal.FromApi @@ -11,14 +13,13 @@ import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Spy import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever +import org.mockito.kotlin.* import utils.TestTransaction @ExtendWith(MockitoExtension::class) @@ -41,17 +42,39 @@ class RegisterLocalPostApplicationServiceTest { @Test fun postを作成できる() = runTest { val actor = TestActorFactory.create(id = 1) + val post = TestPostFactory.create() whenever(actorRepository.findById(ActorId(1))) doReturn actor whenever( postFactoryImpl.createLocal( eq(actor), + anyValueClass(), + isNull(), + eq("content test"), + eq(Visibility.PUBLIC), + isNull(), + isNull(), + eq(false), + eq(emptyList()) ) - ) + ).doReturn(post) service.execute( - RegisterLocalPost("content test", null, Visibility.PUBLIC, null, false, emptyList<>()), FromApi( + RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi( ActorId(1), UserDetailId(1), Acct("test", "example.com") ) ) + + verify(postRepository, times(1)).save(eq(post)) + } + + @Test + fun actorが見つからないと失敗() = runTest { + assertThrows { + service.execute( + RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi( + ActorId(1), UserDetailId(1), Acct("test", "example.com") + ) + ) + } } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt new file mode 100644 index 00000000..8f47c41f --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt @@ -0,0 +1,81 @@ +package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserAcceptFollowRequestApplicationServiceTest { + @InjectMocks + lateinit var service: UserAcceptFollowRequestApplicationService + + @Mock + lateinit var relationshipRepository: RelationshipRepository + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val transaction = TestTransaction + + @Test + fun actorが見つからない場合失敗() = runTest { + assertThrows { + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + } + } + + @Test + fun relationshipが見つからない場合失敗() = runTest { + whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2)) + + assertThrows { + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + } + } + + @Test + fun フォローリクエストを承認できる() = runTest { + whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2)) + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn( + Relationship( + actorId = ActorId(1), targetActorId = ActorId + (2), + following = false, + blocking = false, + muting = false, + followRequesting = true, mutingFollowRequest = false + ) + ) + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + + argumentCaptor { + verify(relationshipRepository).save(capture()) + val first = allValues.first() + + assertFalse(first.followRequesting) + assertTrue(first.following) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt index 66eeb1a0..ca243c63 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt @@ -345,4 +345,150 @@ class DefaultTimelineStoreTest { } } } + + @Test + fun repostがあるときはRepostを含めたTimelineObjectが作成される() = runTest { + val repost = TestPostFactory.create() + val post = TestPostFactory.create(repostId = repost.id.id) + + whenever(postRepository.findById(repost.id)).doReturn(repost) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.replyId).isNull() + assertThat(it.replyActorId).isNull() + assertThat(it.repostId).isEqualTo(repost.id) + assertThat(it.repostActorId).isEqualTo(repost.actorId) + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } + + @Test + fun replyがあるときはReplyを含めたTimeineObjectが作成される() = runTest { + val reply = TestPostFactory.create() + val post = TestPostFactory.create(replyId = reply.id.id) + + whenever(postRepository.findById(reply.id)).doReturn(reply) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.repostId).isNull() + assertThat(it.repostActorId).isNull() + assertThat(it.replyId).isEqualTo(reply.id) + assertThat(it.replyActorId).isEqualTo(reply.actorId) + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } } \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt index cc870055..af17a724 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt @@ -18,6 +18,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.application.RegisterApplication import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest @@ -35,7 +36,7 @@ class SpringAppApi(private val registerApplicationApplicationService: RegisterAp false, appsRequest.scopes?.split(" ").orEmpty().toSet().ifEmpty { setOf("read") } ) - val registeredApplication = registerApplicationApplicationService.register(registerApplication) + val registeredApplication = registerApplicationApplicationService.execute(registerApplication, Anonymous) return ResponseEntity.ok( Application( registeredApplication.name,