feat: ドメイン層に認可的なものを追加

This commit is contained in:
usbharu 2024-06-08 16:37:57 +09:00
parent 86daf1041b
commit 54e3af2253
15 changed files with 302 additions and 153 deletions

View File

@ -16,15 +16,23 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class DeleteLocalPostApplicationService(private val postRepository: PostRepository) { class DeleteLocalPostApplicationService(
suspend fun delete(postId: Long) { private val postRepository: PostRepository,
private val userDetailRepository: UserDetailRepository,
private val actorRepository: ActorRepository,
) {
suspend fun delete(postId: Long, userDetailId: Long) {
val findById = postRepository.findById(PostId(postId))!! val findById = postRepository.findById(PostId(postId))!!
findById.delete() val user = userDetailRepository.findById(userDetailId)!!
val actor = actorRepository.findById(user.actorId)!!
findById.delete(actor)
postRepository.save(findById) postRepository.save(findById)
} }
} }

View File

@ -47,15 +47,19 @@ class RegisterLocalPostApplicationService(
val actorId = (userDetailRepository.findById(command.userDetailId) val actorId = (userDetailRepository.findById(command.userDetailId)
?: throw IllegalStateException("actor not found")).actorId ?: throw IllegalStateException("actor not found")).actorId
val post = postFactory.createLocal(actorId, val actor = actorRepository.findById(actorId)!!
actorRepository.findById(actorId)!!.name,
command.overview?.let { PostOverview(it) }, val post = postFactory.createLocal(
command.content, actor = actor,
command.visibility, actorName = actor.name,
command.repostId?.let { PostId(it) }, overview = command.overview?.let { PostOverview(it) },
command.replyId?.let { PostId(it) }, content = command.content,
command.sensitive, visibility = command.visibility,
command.mediaIds.map { MediaId(it) }) repostId = command.repostId?.let { PostId(it) },
replyId = command.replyId?.let { PostId(it) },
sensitive = command.sensitive,
mediaIds = command.mediaIds.map { MediaId(it) },
)
postRepository.save(post) postRepository.save(post)

View File

@ -16,30 +16,45 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.CommandExecutor
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class UpdateLocalNoteApplicationService( class UpdateLocalNoteApplicationService(
private val transaction: Transaction, transaction: Transaction,
private val postRepository: PostRepository, private val postRepository: PostRepository,
private val postContentFactoryImpl: PostContentFactoryImpl, private val postContentFactoryImpl: PostContentFactoryImpl,
) { private val userDetailRepository: UserDetailRepository,
suspend fun update(updateLocalNote: UpdateLocalNote) { private val actorRepository: ActorRepository,
transaction.transaction { ) : AbstractApplicationService<UpdateLocalNote, Unit>(transaction, logger) {
val post = postRepository.findById(PostId(updateLocalNote.postId))!!
post.content = postContentFactoryImpl.create(updateLocalNote.content) override suspend fun internalExecute(command: UpdateLocalNote, executor: CommandExecutor) {
post.overview = updateLocalNote.overview?.let { PostOverview(it) } require(executor is UserDetailGettableCommandExecutor)
post.addMediaIds(updateLocalNote.mediaIds.map { MediaId(it) })
post.sensitive = updateLocalNote.sensitive
postRepository.save(post) val userDetail = userDetailRepository.findById(executor.userDetailId)!!
} val actor = actorRepository.findById(userDetail.actorId)!!
val post = postRepository.findById(PostId(command.postId))!!
post.setContent(postContentFactoryImpl.create(command.content), actor)
post.setOverview(command.overview?.let { PostOverview(it) }, actor)
post.addMediaIds(command.mediaIds.map { MediaId(it) }, actor)
post.setSensitive(command.sensitive, actor)
postRepository.save(post)
}
companion object {
private val logger = LoggerFactory.getLogger(UpdateLocalNoteApplicationService::class.java)
} }
} }

View File

@ -19,3 +19,7 @@ package dev.usbharu.hideout.core.application.shared
interface CommandExecutor { interface CommandExecutor {
val executor: String val executor: String
} }
interface UserDetailGettableCommandExecutor : CommandExecutor {
val userDetailId: Long
}

View File

@ -16,20 +16,21 @@
package dev.usbharu.hideout.core.domain.event.post package dev.usbharu.hideout.core.domain.event.post
import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
class PostDomainEventFactory(private val post: Post) { class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) {
fun createEvent(postEvent: PostEvent): DomainEvent { fun createEvent(postEvent: PostEvent): DomainEvent {
return DomainEvent.create( return DomainEvent.create(
postEvent.eventName, postEvent.eventName,
PostEventBody(post) PostEventBody(post, actor)
) )
} }
} }
class PostEventBody(post: Post) : DomainEventBody(mapOf("post" to post)) class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor))
enum class PostEvent(val eventName: String) { enum class PostEvent(val eventName: String) {
delete("PostDelete"), delete("PostDelete"),

View File

@ -52,8 +52,18 @@ class Actor(
moveTo: ActorId? = null, moveTo: ActorId? = null,
emojiIds: Set<EmojiId>, emojiIds: Set<EmojiId>,
deleted: Boolean, deleted: Boolean,
roles: Set<Role>,
) : DomainEventStorable() { ) : DomainEventStorable() {
var roles = roles
private set
fun setRole(roles: Set<Role>, actor: Actor) {
require(actor.roles.contains(Role.ADMINISTRATOR).not())
this.roles = roles
}
var suspend = suspend var suspend = suspend
set(value) { set(value) {
if (field != value && value) { if (field != value && value) {

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.domain.model.actor
enum class Role {
LOCAL, MODERATOR, ADMINISTRATOR, REMOTE
}

View File

@ -18,9 +18,12 @@ package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory
import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.event.post.PostEvent
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.ActorId
import dev.usbharu.hideout.core.domain.model.actor.Role
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post.Companion.Action.*
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
import java.net.URI import java.net.URI
import java.time.Instant import java.time.Instant
@ -28,7 +31,7 @@ import java.time.Instant
class Post( class Post(
val id: PostId, val id: PostId,
actorId: ActorId, actorId: ActorId,
overview: PostOverview? = null, overview: PostOverview?,
content: PostContent, content: PostContent,
val createdAt: Instant, val createdAt: Instant,
visibility: Visibility, visibility: Visibility,
@ -39,13 +42,12 @@ class Post(
val apId: URI, val apId: URI,
deleted: Boolean, deleted: Boolean,
mediaIds: List<MediaId>, mediaIds: List<MediaId>,
visibleActors: Set<ActorId> = emptySet(), visibleActors: Set<ActorId>,
hide: Boolean = false, hide: Boolean,
moveTo: PostId? = null, moveTo: PostId?,
) : DomainEventStorable() { ) : DomainEventStorable() {
var actorId = actorId val actorId = actorId
private set
get() { get() {
if (deleted) { if (deleted) {
return ActorId.ghost return ActorId.ghost
@ -54,27 +56,35 @@ class Post(
} }
var visibility = visibility var visibility = visibility
set(value) { private set
require(visibility != Visibility.DIRECT)
require(value != Visibility.DIRECT)
require(field.ordinal >= value.ordinal)
require(deleted.not()) fun setVisibility(visibility: Visibility, actor: Actor) {
if (field != value) { require(isAllow(actor, UPDATE, this))
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) require(this.visibility != Visibility.DIRECT)
} require(visibility != Visibility.DIRECT)
field = value require(this.visibility.ordinal >= visibility.ordinal)
require(deleted.not())
if (this.visibility != visibility) {
addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
} }
this.visibility = visibility
}
var visibleActors = visibleActors var visibleActors = visibleActors
set(value) { private set
require(deleted.not())
if (visibility == Visibility.DIRECT) { fun setVisibleActors(visibleActors: Set<ActorId>, actor: Actor) {
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update))
field = field.plus(value) require(isAllow(actor, UPDATE, this))
} require(deleted.not())
if (visibility == Visibility.DIRECT) {
addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
this.visibleActors = this.visibleActors.plus(visibleActors)
} }
}
var content = content var content = content
get() { get() {
@ -83,13 +93,16 @@ class Post(
} }
return field return field
} }
set(value) { private set
require(deleted.not())
if (field != value) { fun setContent(content: PostContent, actor: Actor) {
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) require(isAllow(actor, UPDATE, this))
} require(deleted.not())
field = value if (this.content != content) {
addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
} }
this.content = content
}
var overview = overview var overview = overview
get() { get() {
@ -98,22 +111,28 @@ class Post(
} }
return field return field
} }
set(value) { private set
require(deleted.not())
if (field != value) { fun setOverview(overview: PostOverview?, actor: Actor) {
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) require(isAllow(actor, UPDATE, this))
} require(deleted.not())
field = value if (this.overview != overview) {
addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
} }
this.overview = overview
}
var sensitive = sensitive var sensitive = sensitive
set(value) { private set
require(deleted.not())
if (field != value) { fun setSensitive(sensitive: Boolean, actor: Actor) {
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) isAllow(actor, UPDATE, this)
} require(deleted.not())
field = value if (this.sensitive != sensitive) {
addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
} }
this.sensitive = sensitive
}
val text: String val text: String
get() { get() {
@ -140,18 +159,20 @@ class Post(
} }
private set private set
fun addMediaIds(mediaIds: List<MediaId>) { fun addMediaIds(mediaIds: List<MediaId>, actor: Actor) {
require(isAllow(actor, UPDATE, this))
require(deleted.not()) require(deleted.not())
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update))
this.mediaIds = this.mediaIds.plus(mediaIds).distinct() this.mediaIds = this.mediaIds.plus(mediaIds).distinct()
} }
var deleted = deleted var deleted = deleted
private set private set
fun delete() { fun delete(actor: Actor) {
isAllow(actor, DELETE, this)
if (deleted.not()) { if (deleted.not()) {
addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.delete)) addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.delete))
content = PostContent.empty content = PostContent.empty
overview = null overview = null
mediaIds = emptyList() mediaIds = emptyList()
@ -185,7 +206,8 @@ class Post(
var moveTo = moveTo var moveTo = moveTo
private set private set
fun moveTo(moveTo: PostId) { fun moveTo(moveTo: PostId, actor: Actor) {
require(isAllow(actor, MOVE, this))
require(this.moveTo == null) require(this.moveTo == null)
this.moveTo = moveTo this.moveTo = moveTo
} }
@ -203,6 +225,27 @@ class Post(
return id.hashCode() return id.hashCode()
} }
fun reconstructWith(mediaIds: List<MediaId>, emojis: List<EmojiId>, visibleActors: Set<ActorId>): Post {
return Post(
id = id,
actorId = actorId,
overview = overview,
content = PostContent(this.content.text, this.content.content, emojis),
createdAt = createdAt,
visibility = visibility,
url = url,
repostId = repostId,
replyId = replyId,
sensitive = sensitive,
apId = apId,
deleted = deleted,
mediaIds = mediaIds,
visibleActors = visibleActors,
hide = hide,
moveTo = moveTo
)
}
companion object { companion object {
fun create( fun create(
id: PostId, id: PostId,
@ -221,14 +264,25 @@ class Post(
visibleActors: Set<ActorId> = emptySet(), visibleActors: Set<ActorId> = emptySet(),
hide: Boolean = false, hide: Boolean = false,
moveTo: PostId? = null, moveTo: PostId? = null,
actor: Actor,
): Post { ): Post {
require(actor.deleted.not())
require(actor.moveTo == null)
val visibility1 = if (actor.suspend && visibility == Visibility.PUBLIC) {
Visibility.UNLISTED
} else {
visibility
}
val post = Post( val post = Post(
id, id,
actorId, actorId,
overview, overview,
content, content,
createdAt, createdAt,
visibility, visibility1,
url, url,
repostId, repostId,
replyId, replyId,
@ -243,5 +297,30 @@ class Post(
post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.create)) post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.create))
return post return post
} }
fun isAllow(actor: Actor, action: Action, resource: Post): Boolean {
return when (action) {
UPDATE -> {
if (actor.deleted) {
return true
}
resource.actorId == actor.id || actor.roles.contains(Role.ADMINISTRATOR) || actor.roles.contains(
Role.MODERATOR
)
}
MOVE -> resource.actorId == actor.id && actor.deleted.not()
DELETE -> resource.actorId == actor.id ||
actor.roles.contains(Role.ADMINISTRATOR) ||
actor.roles.contains(Role.MODERATOR)
}
}
enum class Action {
UPDATE,
MOVE,
DELETE,
}
} }
} }

View File

@ -16,8 +16,8 @@
package dev.usbharu.hideout.core.domain.shared.domainevent package dev.usbharu.hideout.core.domain.shared.domainevent
abstract class DomainEventBody(val map: Map<String, Any>) { abstract class DomainEventBody(val map: Map<String, Any?>) {
fun toMap(): Map<String, Any> { fun toMap(): Map<String, Any?> {
return map return map
} }
} }

View File

@ -58,7 +58,8 @@ class ActorResultRowMapper : ResultRowMapper<Actor> {
.filter { it.isNotEmpty() } .filter { it.isNotEmpty() }
.map { EmojiId(it.toLong()) } .map { EmojiId(it.toLong()) }
.toSet(), .toSet(),
deleted = resultRow[Actors.deleted] deleted = resultRow[Actors.deleted],
roles = emptySet()
) )
} }
} }

View File

@ -39,26 +39,25 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper<Post>) :
.first() .first()
.let(postResultRowMapper::map) .let(postResultRowMapper::map)
.apply { .apply {
addMediaIds( reconstructWith(
it.mapNotNull { resultRow: ResultRow -> mediaIds = it.mapNotNull { resultRow: ResultRow ->
resultRow resultRow
.getOrNull(PostsMedia.mediaId) .getOrNull(PostsMedia.mediaId)
?.let { mediaId -> MediaId(mediaId) } ?.let { mediaId -> MediaId(mediaId) }
} },
) emojis = it
content = content.copy(emojiIds = it .mapNotNull { resultRow: ResultRow ->
.mapNotNull { resultRow: ResultRow -> resultRow
.getOrNull(PostsEmojis.emojiId)
?.let { emojiId -> EmojiId(emojiId) }
},
visibleActors = it.mapNotNull { resultRow: ResultRow ->
resultRow resultRow
.getOrNull(PostsEmojis.emojiId) .getOrNull(PostsVisibleActors.actorId)
?.let { emojiId -> EmojiId(emojiId) } ?.let { actorId -> ActorId(actorId) }
} }.toSet()
) )
visibleActors = it.mapNotNull { resultRow: ResultRow ->
resultRow
.getOrNull(PostsVisibleActors.actorId)
?.let { actorId -> ActorId(actorId) }
}.toSet()
clearDomainEvents()
} }
} }
} }

View File

@ -60,7 +60,8 @@ class ActorFactoryImpl(
lastPostAt = null, lastPostAt = null,
suspend = false, suspend = false,
emojiIds = emptySet(), emojiIds = emptySet(),
deleted = false deleted = false,
roles = emptySet()
) )
} }
} }

View File

@ -17,7 +17,7 @@
package dev.usbharu.hideout.core.infrastructure.factory package dev.usbharu.hideout.core.infrastructure.factory
import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorName import dev.usbharu.hideout.core.domain.model.actor.ActorName
import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Post
@ -36,7 +36,7 @@ class PostFactoryImpl(
private val applicationConfig: ApplicationConfig, private val applicationConfig: ApplicationConfig,
) { ) {
suspend fun createLocal( suspend fun createLocal(
actorId: ActorId, actor: Actor,
actorName: ActorName, actorName: ActorName,
overview: PostOverview?, overview: PostOverview?,
content: String, content: String,
@ -49,19 +49,20 @@ class PostFactoryImpl(
val id = idGenerateService.generateId() val id = idGenerateService.generateId()
val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName.name + "/posts/" + id) val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName.name + "/posts/" + id)
return Post.create( return Post.create(
PostId(id), id = PostId(id),
actorId, actorId = actor.id,
overview, overview = overview,
postContentFactoryImpl.create(content), content = postContentFactoryImpl.create(content),
Instant.now(), createdAt = Instant.now(),
visibility, visibility = visibility,
url, url = url,
repostId, repostId = repostId,
replyId, replyId = replyId,
sensitive, sensitive = sensitive,
url, apId = url,
false, deleted = false,
mediaIds, mediaIds = mediaIds,
actor = actor,
) )
} }
} }

View File

@ -20,7 +20,7 @@ object TestActorFactory {
inbox: URI = URI.create("https://example.com/$id/inbox"), inbox: URI = URI.create("https://example.com/$id/inbox"),
outbox: URI = URI.create("https://example.com/$id/outbox"), outbox: URI = URI.create("https://example.com/$id/outbox"),
uri: URI = URI.create("https://example.com/$id"), uri: URI = URI.create("https://example.com/$id"),
publicKey: ActorPublicKey, publicKey: ActorPublicKey = ActorPublicKey(""),
privateKey: ActorPrivateKey? = null, privateKey: ActorPrivateKey? = null,
createdAt: Instant = Instant.now(), createdAt: Instant = Instant.now(),
keyId: String = "https://example.com/$id#key-id", keyId: String = "https://example.com/$id#key-id",
@ -65,6 +65,7 @@ object TestActorFactory {
moveTo = moveTo, moveTo = moveTo,
emojiIds = emojiIds, emojiIds = emojiIds,
deleted = deleted, deleted = deleted,
roles = emptySet()
) )
} }
} }

View File

@ -2,6 +2,8 @@ package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.event.post.PostEvent
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.ActorPublicKey
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
@ -29,100 +31,102 @@ class PostTest {
fun visibilityがDIRECTのとき変更できない() { fun visibilityがDIRECTのとき変更できない() {
val post = TestPostFactory.create(visibility = Visibility.DIRECT) val post = TestPostFactory.create(visibility = Visibility.DIRECT)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.PUBLIC post.setVisibility(Visibility.PUBLIC, actor)
} }
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.UNLISTED post.setVisibility(Visibility.UNLISTED, actor)
} }
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.FOLLOWERS post.setVisibility(Visibility.FOLLOWERS, actor)
} }
} }
@Test @Test
fun visibilityを小さくすることはできないPUBLIC() { fun visibilityを小さくすることはできないPUBLIC() {
val post = TestPostFactory.create(visibility = Visibility.PUBLIC) val post = TestPostFactory.create(visibility = Visibility.PUBLIC)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.DIRECT post.setVisibility(Visibility.DIRECT, actor)
} }
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.UNLISTED post.setVisibility(Visibility.UNLISTED, actor)
} }
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.FOLLOWERS post.setVisibility(Visibility.FOLLOWERS, actor)
} }
} }
@Test @Test
fun visibilityを小さくすることはできないUNLISTED() { fun visibilityを小さくすることはできないUNLISTED() {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED) val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.DIRECT post.setVisibility(Visibility.DIRECT, actor)
} }
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.FOLLOWERS post.setVisibility(Visibility.FOLLOWERS, actor)
} }
} }
@Test @Test
fun visibilityを小さくすることはできないFOLLOWERS() { fun visibilityを小さくすることはできないFOLLOWERS() {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.DIRECT post.setVisibility(Visibility.DIRECT, actor)
} }
} }
@Test @Test
fun visibilityをDIRECTにあとからすることはできない() { fun visibilityをDIRECTにあとからすることはできない() {
val post = TestPostFactory.create(visibility = Visibility.DIRECT) val post = TestPostFactory.create(visibility = Visibility.DIRECT)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.DIRECT post.setVisibility(Visibility.DIRECT, actor)
} }
} }
@Test @Test
fun visibilityを大きくすることができるFOLLOWERS() { fun visibilityを大きくすることができるFOLLOWERS() {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertDoesNotThrow { assertDoesNotThrow {
post.visibility = Visibility.UNLISTED post.setVisibility(Visibility.UNLISTED, actor)
} }
val post2 = TestPostFactory.create(visibility = Visibility.FOLLOWERS) val post2 = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
assertDoesNotThrow { assertDoesNotThrow {
post2.visibility = Visibility.PUBLIC post2.setVisibility(Visibility.PUBLIC, actor)
} }
} }
@Test @Test
fun visibilityを大きくすることができるUNLISTED() { fun visibilityを大きくすることができるUNLISTED() {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED) val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertDoesNotThrow { assertDoesNotThrow {
post.visibility = Visibility.PUBLIC post.setVisibility(Visibility.PUBLIC, actor)
} }
} }
@Test @Test
fun deletedがtrueのときvisibilityを変更できない() { fun deletedがtrueのときvisibilityを変更できない() {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED, deleted = true) val post = TestPostFactory.create(visibility = Visibility.UNLISTED, deleted = true)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibility = Visibility.PUBLIC post.setVisibility(Visibility.PUBLIC, actor)
} }
} }
@Test @Test
fun visibilityが変更されない限りドメインイベントは発生しない() { fun visibilityが変更されない限りドメインイベントは発生しない() {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED) val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.visibility = Visibility.UNLISTED post.setVisibility(Visibility.UNLISTED, actor)
assertEmpty(post) assertEmpty(post)
} }
@ -130,7 +134,8 @@ class PostTest {
@Test @Test
fun visibilityが変更されるとupdateイベントが発生する() { fun visibilityが変更されるとupdateイベントが発生する() {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED) val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
post.visibility = Visibility.PUBLIC val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.setVisibility(Visibility.PUBLIC, actor)
assertContainsEvent(post, PostEvent.update.eventName) assertContainsEvent(post, PostEvent.update.eventName)
} }
@ -138,51 +143,51 @@ class PostTest {
@Test @Test
fun deletedがtrueのときvisibleActorsを変更できない() { fun deletedがtrueのときvisibleActorsを変更できない() {
val post = TestPostFactory.create(deleted = true) val post = TestPostFactory.create(deleted = true)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.visibleActors = setOf(ActorId(100)) post.setVisibleActors(setOf(ActorId(100)), actor)
} }
} }
@Test @Test
fun ゔvisibilityがDIRECT以外の時visibleActorsを変更できない() { fun ゔvisibilityがDIRECT以外の時visibleActorsを変更できない() {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.visibleActors = setOf(ActorId(100)) post.setVisibleActors(setOf(ActorId(100)), actor)
assertEmpty(post) assertEmpty(post)
val post2 = TestPostFactory.create(visibility = Visibility.UNLISTED) val post2 = TestPostFactory.create(visibility = Visibility.UNLISTED)
post2.visibleActors = setOf(ActorId(100)) post2.setVisibleActors(setOf(ActorId(100)), actor)
assertEmpty(post2) assertEmpty(post2)
val post3 = TestPostFactory.create(visibility = Visibility.PUBLIC) val post3 = TestPostFactory.create(visibility = Visibility.PUBLIC)
post3.visibleActors = setOf(ActorId(100)) post3.setVisibleActors(setOf(ActorId(100)), actor)
assertEmpty(post3) assertEmpty(post3)
} }
@Test @Test
fun visibilityがDIRECTの時visibleActorsを変更できる() { fun visibilityがDIRECTの時visibleActorsを変更できる() {
val post = TestPostFactory.create(visibility = Visibility.DIRECT) val post = TestPostFactory.create(visibility = Visibility.DIRECT)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.visibleActors = setOf(ActorId(100)) post.setVisibleActors(setOf(ActorId(100)), actor)
assertEquals(setOf(ActorId(100)), post.visibleActors) assertEquals(setOf(ActorId(100)), post.visibleActors)
} }
@Test @Test
fun visibleActorsから削除されることはない() { fun visibleActorsから削除されることはない() {
val post = TestPostFactory.create(visibility = Visibility.DIRECT, visibleActors = listOf(100)) val post = TestPostFactory.create(visibility = Visibility.DIRECT, visibleActors = listOf(100))
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.visibleActors = setOf(ActorId(200)) post.setVisibleActors(setOf(ActorId(200)), actor)
assertEquals(setOf(ActorId(100), ActorId(200)), post.visibleActors) assertEquals(setOf(ActorId(100), ActorId(200)), post.visibleActors)
} }
@Test @Test
fun visibleActorsに追加された時updateイベントが発生する() { fun visibleActorsに追加された時updateイベントが発生する() {
val post = TestPostFactory.create(visibility = Visibility.DIRECT) val post = TestPostFactory.create(visibility = Visibility.DIRECT)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.visibleActors = setOf(ActorId(100)) post.setVisibleActors(setOf(ActorId(100)), actor)
assertContainsEvent(post, PostEvent.update.eventName) assertContainsEvent(post, PostEvent.update.eventName)
} }
@ -197,17 +202,17 @@ class PostTest {
@Test @Test
fun deletedがtrueの時contentをセットできない() { fun deletedがtrueの時contentをセットできない() {
val post = TestPostFactory.create(deleted = true) val post = TestPostFactory.create(deleted = true)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.content = PostContent("test", "test", emptyList()) post.setContent(PostContent("test", "test", emptyList()), actor)
} }
} }
@Test @Test
fun contentの内容が変更されたらupdateイベントが発生する() { fun contentの内容が変更されたらupdateイベントが発生する() {
val post = TestPostFactory.create() val post = TestPostFactory.create()
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.content = PostContent("test", "test", emptyList()) post.setContent(PostContent("test", "test", emptyList()), actor)
assertContainsEvent(post, PostEvent.update.eventName) assertContainsEvent(post, PostEvent.update.eventName)
} }
@ -228,19 +233,19 @@ class PostTest {
@Test @Test
fun deletedがtrueのときセットできない() { fun deletedがtrueのときセットできない() {
val post = TestPostFactory.create(deleted = true) val post = TestPostFactory.create(deleted = true)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
post.overview = PostOverview("aaaa") post.setOverview(PostOverview("aaaa"), actor)
} }
} }
@Test @Test
fun deletedがfalseのときセットできる() { fun deletedがfalseのときセットできる() {
val post = TestPostFactory.create(deleted = false) val post = TestPostFactory.create(deleted = false)
val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
val overview = PostOverview("aaaa") val overview = PostOverview("aaaa")
assertDoesNotThrow { assertDoesNotThrow {
post.overview = overview post.setOverview(overview, actor)
} }
assertEquals(overview, post.overview) assertEquals(overview, post.overview)
@ -250,11 +255,10 @@ class PostTest {
@Test @Test
fun overviewの内容が更新されなかった時イベントが発生しない() { fun overviewの内容が更新されなかった時イベントが発生しない() {
val post = TestPostFactory.create(overview = "aaaa") val post = TestPostFactory.create(overview = "aaaa")
post.overview = PostOverview("aaaa") val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey(""))
post.setOverview(PostOverview("aaaa"), actor)
assertEmpty(post) assertEmpty(post)
} }
} }