test: お引越しのテストを追加

This commit is contained in:
usbharu 2024-08-08 17:29:05 +09:00
parent f96c16b2a3
commit f7de5b03f1
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
8 changed files with 337 additions and 33 deletions

View File

@ -0,0 +1,3 @@
package dev.usbharu.hideout.core.application.actor
data class MigrationLocalActor(val from: Long, val to: Long)

View File

@ -16,39 +16,59 @@
package dev.usbharu.hideout.core.application.actor package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
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.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId 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.ActorRepository
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class MigrationLocalActorApplicationService( class MigrationLocalActorApplicationService(
private val transaction: Transaction,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService,
) { transaction: Transaction,
suspend fun migration(from: Long, to: Long, executor: ActorId) { private val userDetailRepository: UserDetailRepository,
transaction.transaction<Unit> { ) : LocalUserAbstractApplicationService<MigrationLocalActor, Unit>(transaction, logger) {
val fromActorId = ActorId(from)
val toActorId = ActorId(to)
val fromActor = actorRepository.findById(fromActorId)!! override suspend fun internalExecute(command: MigrationLocalActor, principal: FromApi) {
val toActor = actorRepository.findById(toActorId)!! if (command.from != principal.actorId.id) {
throw PermissionDeniedException()
}
val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) val userDetail = userDetailRepository.findById(principal.userDetailId)
when (canAccountMigration) { ?: throw InternalServerException("User detail ${principal.userDetailId} not found.")
is AlreadyMoved -> TODO()
is CanAccountMigration -> { val fromActorId = ActorId(command.from)
val toActorId = ActorId(command.to)
val fromActor =
actorRepository.findById(fromActorId) ?: throw IllegalArgumentException("Actor ${command.from} not found.")
val toActor =
actorRepository.findById(toActorId) ?: throw IllegalArgumentException("Actor ${command.to} not found.")
val canAccountMigration =
localActorMigrationCheckDomainService.canAccountMigration(userDetail, fromActor, toActor)
if (canAccountMigration.canMigration) {
fromActor.moveTo = toActorId fromActor.moveTo = toActorId
actorRepository.save(fromActor) actorRepository.save(fromActor)
} else when (canAccountMigration) {
is AlreadyMoved -> throw IllegalArgumentException(canAccountMigration.message)
is CanAccountMigration -> throw InternalServerException()
is CircularReferences -> throw IllegalArgumentException(canAccountMigration.message)
is SelfReferences -> throw IllegalArgumentException("Self references are not supported")
is AlsoKnownAsNotFound -> throw IllegalArgumentException(canAccountMigration.message)
is MigrationCoolDown -> throw IllegalArgumentException(canAccountMigration.message)
}
} }
is CircularReferences -> TODO() companion object {
is SelfReferences -> TODO() private val logger = LoggerFactory.getLogger(MigrationLocalActorApplicationService::class.java)
is AlsoKnownAsNotFound -> TODO()
}
}
} }
} }

View File

@ -49,7 +49,6 @@ class RegisterLocalActorApplicationService(
override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI { override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI {
if (actorDomainService.usernameAlreadyUse(command.name)) { if (actorDomainService.usernameAlreadyUse(command.name)) {
// todo 適切な例外を考える
throw IllegalArgumentException("Username already exists") throw IllegalArgumentException("Username already exists")
} }
val instance = instanceRepository.findByUrl(applicationConfig.url.toURI()) val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())
@ -74,6 +73,6 @@ class RegisterLocalActorApplicationService(
} }
companion object { companion object {
val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java) private val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java)
} }
} }

View File

@ -17,9 +17,10 @@
package dev.usbharu.hideout.core.domain.service.actor.local package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
interface LocalActorMigrationCheckDomainService { interface LocalActorMigrationCheckDomainService {
suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck
} }
sealed class AccountMigrationCheck( sealed class AccountMigrationCheck(
@ -34,4 +35,6 @@ sealed class AccountMigrationCheck(
class AlreadyMoved(val message: String) : AccountMigrationCheck(false) class AlreadyMoved(val message: String) : AccountMigrationCheck(false)
class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false)
class MigrationCoolDown(val message: String) : AccountMigrationCheck(false)
} }

View File

@ -17,11 +17,23 @@
package dev.usbharu.hideout.core.domain.service.actor.local package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.Instant
import kotlin.time.Duration.Companion.days
import kotlin.time.toJavaDuration
@Service @Service
class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService { class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService {
override suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck { override suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck {
val lastMigration = userDetail.lastMigration
if (lastMigration != null) {
val instant = lastMigration.plus(30.days.toJavaDuration())
if (instant.isAfter(Instant.now())) {
return AccountMigrationCheck.MigrationCoolDown("You can migration at $instant.")
}
}
if (to == from) { if (to == from) {
return AccountMigrationCheck.SelfReferences() return AccountMigrationCheck.SelfReferences()
} }

View File

@ -0,0 +1,171 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.Actor
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.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
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 MigrationLocalActorApplicationServiceTest {
@InjectMocks
lateinit var service: MigrationLocalActorApplicationService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Spy
val transaction = TestTransaction
@Test
fun pricinpalのactorとfromのactorが違うと失敗() = runTest {
assertThrows<PermissionDeniedException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com"))
)
}
}
@Test
fun fromのactorが見つからなかったら失敗() = runTest {
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun toのactorが見つからなかったら失敗() = runTest {
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1))
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun userDetailが見つからなかったら失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun canMigrationがtrueならmoveToを書き込む() = runTest {
val from = TestActorFactory.create(1)
val to = TestActorFactory.create(2)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(from)
whenever(actorRepository.findById(ActorId(2))).doReturn(to)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
whenever(
localActorMigrationCheckDomainService.canAccountMigration(
userDetail,
from,
to
)
).doReturn(AccountMigrationCheck.CanAccountMigration())
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
argumentCaptor<Actor> {
verify(actorRepository, times(1)).save(capture())
val first = allValues.first()
assertEquals(first.moveTo, to.id)
}
}
@Test
fun canMigrationがfalseなら例外() = runTest {
val from = TestActorFactory.create(1)
val to = TestActorFactory.create(2)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(from)
whenever(actorRepository.findById(ActorId(2))).doReturn(to)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
whenever(
localActorMigrationCheckDomainService.canAccountMigration(
userDetail,
from,
to
)
).doReturn(
AccountMigrationCheck.AlreadyMoved("Message"),
AccountMigrationCheck.CircularReferences("Message"),
AccountMigrationCheck.SelfReferences(),
AccountMigrationCheck.AlsoKnownAsNotFound("Message"),
AccountMigrationCheck.MigrationCoolDown("Message")
)
repeat(5) {
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
verify(actorRepository, never()).save(any())
}
}

View File

@ -0,0 +1,78 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService
import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
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 utils.TestTransaction
import java.net.URL
@ExtendWith(MockitoExtension::class)
class RegisterLocalActorApplicationServiceTest {
@InjectMocks
lateinit var service: RegisterLocalActorApplicationService
@Mock
lateinit var actorDomainService: LocalActorDomainService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var actorFactoryImpl: ActorFactoryImpl
@Mock
lateinit var instanceRepository: InstanceRepository
@Mock
lateinit var userDetailDomainService: UserDetailDomainService
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Spy
val transaction = TestTransaction
@Spy
val applicationConfig = ApplicationConfig(URL("http://example.com"))
@Spy
val idGenerateService = TwitterSnowflakeIdGenerateService
@Test
fun usernameがすでに使われていた場合失敗() = runTest {
whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(true)
assertThrows<IllegalArgumentException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
@Test
fun ローカルインスタンスが見つからない場合失敗() = runTest {
whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(false)
assertThrows<InternalServerException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
}

View File

@ -2,6 +2,9 @@ package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Assertions.assertInstanceOf
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -12,10 +15,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create() val to = TestActorFactory.create()
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, from) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, from)
assertInstanceOf(AccountMigrationCheck.SelfReferences::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.SelfReferences::class.java, canAccountMigration)
} }
@ -25,10 +31,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(moveTo = 100) val to = TestActorFactory.create(moveTo = 100)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration)
} }
@ -37,10 +46,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 自分自身が引っ越している場合は引っ越しできない() = runTest { fun 自分自身が引っ越している場合は引っ越しできない() = runTest {
val from = TestActorFactory.create(moveTo = 100) val from = TestActorFactory.create(moveTo = 100)
val to = TestActorFactory.create() val to = TestActorFactory.create()
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration)
} }
@ -49,10 +61,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 引越し先のalsoKnownAsに引越し元が含まれてない場合失敗する() = runTest { fun 引越し先のalsoKnownAsに引越し元が含まれてない場合失敗する() = runTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(alsoKnownAs = setOf(ActorId(100))) val to = TestActorFactory.create(alsoKnownAs = setOf(ActorId(100)))
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlsoKnownAsNotFound::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlsoKnownAsNotFound::class.java, canAccountMigration)
} }
@ -61,10 +76,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 正常に設定されている場合は成功する() = runTest { fun 正常に設定されている場合は成功する() = runTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100))) val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100)))
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration)
} }