From 7c106f7cf8f3f0a7fc4c7a0f4d307d6ea3d22b47 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Thu, 14 Jan 2021 23:38:59 +0100 Subject: [PATCH] Refactoring of CommentVoter --- src/main/kotlin/Application.kt | 14 +-- src/main/kotlin/KoinModule.kt | 2 + .../component/comment/generic/Comment.kt | 8 +- .../component/comment/generic/CommentVoter.kt | 84 +++++-------- .../routes/CreateCommentChildrenRequest.kt | 7 +- .../comment/generic/routes/EditComment.kt | 10 +- .../routes/GetCommentChildrenRequest.kt | 7 +- .../comment/generic/routes/GetOneComment.kt | 9 +- src/main/kotlin/routes/CommentArticle.kt | 15 ++- src/main/kotlin/routes/CommentConstitution.kt | 15 ++- src/main/kotlin/voter/ConstitutionVoter.kt | 16 +-- src/main/kotlin/voter/VoterModule.kt | 55 ++++++++ .../kotlin/security/voter/CommentVoterTest.kt | 119 +++++------------- 13 files changed, 169 insertions(+), 192 deletions(-) diff --git a/src/main/kotlin/Application.kt b/src/main/kotlin/Application.kt index a00759c..7b58bba 100644 --- a/src/main/kotlin/Application.kt +++ b/src/main/kotlin/Application.kt @@ -16,7 +16,6 @@ import fr.dcproject.component.citizen.routes.changeMyPassword import fr.dcproject.component.citizen.routes.findCitizen import fr.dcproject.component.citizen.routes.getCurrentCitizen import fr.dcproject.component.citizen.routes.getOneCitizen -import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.component.comment.generic.routes.createCommentChildren import fr.dcproject.component.comment.generic.routes.editComment import fr.dcproject.component.comment.generic.routes.getChildrenComments @@ -80,7 +79,6 @@ fun Application.module(env: Env = PROD) { install(AuthorizationVoter) { voters = listOf( ConstitutionVoter(), - CommentVoter(), VoteVoter(), FollowVoter(), OpinionVoter(), @@ -170,17 +168,17 @@ fun Application.module(env: Env = PROD) { getCurrentCitizen(get()) changeMyPassword(get(), get()) /* Comment */ - editComment(get()) - getOneComment(get()) - createCommentChildren(get()) - getChildrenComments(get()) + editComment(get(), get()) + getOneComment(get(), get()) + createCommentChildren(get(), get()) + getChildrenComments(get(), get()) /* TODO */ auth(get(), get(), get()) constitution(get()) followArticle(get()) followConstitution(get()) - commentArticle(get()) - commentConstitution(get()) + commentArticle(get(), get()) + commentConstitution(get(), get()) voteArticle(get(), get(), get()) voteConstitution(get()) opinionArticle(get()) diff --git a/src/main/kotlin/KoinModule.kt b/src/main/kotlin/KoinModule.kt index c030e27..cb593d3 100644 --- a/src/main/kotlin/KoinModule.kt +++ b/src/main/kotlin/KoinModule.kt @@ -14,6 +14,7 @@ import fr.dcproject.component.article.ArticleVoter import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenVoter import fr.dcproject.component.comment.article.CommentArticleRepository +import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.event.publisher.Publisher import fr.dcproject.messages.Mailer import fr.dcproject.messages.NotificationEmailSender @@ -119,6 +120,7 @@ val KoinModule = module { // Voters single { ArticleVoter(get()) } single { CitizenVoter() } + single { CommentVoter() } // Elasticsearch Client single { diff --git a/src/main/kotlin/component/comment/generic/Comment.kt b/src/main/kotlin/component/comment/generic/Comment.kt index c37171f..7830a48 100644 --- a/src/main/kotlin/component/comment/generic/Comment.kt +++ b/src/main/kotlin/component/comment/generic/Comment.kt @@ -16,6 +16,7 @@ class CommentForView( val childrenCount: Int? = null, override val deletedAt: DateTime? = null ) : ExtraI, + CommentWithParentI, CommentForUpdate(id, createdBy, target, content, parent, deletedAt), CommentWithTargetI, EntityCreatedBy by EntityCreatedByImp(createdBy), @@ -40,9 +41,10 @@ open class CommentForUpdate( override val createdBy: C, override val target: T, open var content: String, - open val parent: CommentParent? = null, + override val parent: CommentParent? = null, override val deletedAt: DateTime? = null ) : CommentParent(id, deletedAt, target), + CommentWithParentI, ExtraI, CommentWithTargetI, EntityCreatedAt by EntityCreatedAtImp(), @@ -72,6 +74,10 @@ interface CommentParentI : CommentI, EntityDeletedAt, CommentWithTa interface CommentWithTargetI : CommentI, TargetI, AsTarget +interface CommentWithParentI { + val parent: CommentParent? +} + open class CommentRef(id: UUID = UUID.randomUUID()) : CommentI, TargetRef(id) interface CommentI : EntityI diff --git a/src/main/kotlin/component/comment/generic/CommentVoter.kt b/src/main/kotlin/component/comment/generic/CommentVoter.kt index 42d6584..8008c3a 100644 --- a/src/main/kotlin/component/comment/generic/CommentVoter.kt +++ b/src/main/kotlin/component/comment/generic/CommentVoter.kt @@ -1,61 +1,41 @@ package fr.dcproject.component.comment.generic -import fr.dcproject.citizenOrNull -import fr.dcproject.voter.NoRuleDefinedException -import fr.dcproject.voter.NoSubjectDefinedException -import fr.ktorVoter.* +import fr.dcproject.component.citizen.CitizenI +import fr.dcproject.entity.AsTarget +import fr.dcproject.voter.Voter +import fr.dcproject.voter.VoterResponse +import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityDeletedAt -import io.ktor.application.* -class CommentVoter : Voter { - enum class Action : ActionI { - CREATE, - UPDATE, - VIEW, - DELETE +class CommentVoter : Voter() { + fun canView(subjects: List, citizen: CitizenI?): VoterResponse + where S : CommentI, + S : EntityDeletedAt = canAll(subjects) { canView(it, citizen) } + + fun canView(subject: S, citizen: CitizenI?): VoterResponse + where S : CommentI, + S : EntityDeletedAt = when { + subject.isDeleted() -> denied("Your cannot view a deleted comment", "comment.view.deleted") + else -> granted() } - override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI { - if (!(action is Action && subject is CommentI?)) return abstain() + fun canCreate(subject: S, citizen: CitizenI?): VoterResponse + where S : CommentI, + S : EntityCreatedBy, + S : CommentWithParentI<*>, + S : AsTarget<*> = when { + citizen == null -> denied("You must be connected to create user", "comment.create.notConnected") + subject.createdBy.id != citizen.id -> denied("You cannot create a comment with other user than yours", "comment.create.wrongUser") + subject.parent?.isDeleted() ?: false -> denied("You cannot create a comment on deleted parent", "comment.create.deletedParent") + subject.target.let { it is EntityDeletedAt && it.isDeleted() } -> denied("You cannot create a comment on deleted target", "comment.create.deletedTarget") + else -> granted() + } - val citizen = context.citizenOrNull - - if (subject == null) { - throw NoSubjectDefinedException(action) - } - - if (action == Action.CREATE) { - return when { - citizen == null -> denied("You must be connected to create user", "comment.create.notConnected") - subject !is CommentForUpdate<*, *> -> throw NoSubjectDefinedException(action) - subject.createdBy.id != citizen.id -> denied("You cannot create a comment with other user than yours", "comment.create.wrongUser") - subject.parent?.isDeleted() ?: false -> denied("You cannot create a comment on deleted parent", "comment.create.deletedParent") - subject.target.let { it is EntityDeletedAt && it.isDeleted() } -> denied("You cannot create a comment on deleted target", "comment.create.deletedTarget") - else -> granted() - } - } - - if (action == Action.VIEW) { - return when { - subject !is CommentForView<*, *> -> throw NoSubjectDefinedException(action) - subject.isDeleted() -> denied("Your cannot view a deleted comment", "comment.view.deleted") - else -> granted() - } - } - - if (action == Action.UPDATE) { - if (citizen == null) return denied("You must be connected to update comment", "comment.update.notConnected") - return when { - subject !is CommentForUpdate<*, *> -> throw NoSubjectDefinedException(action) - citizen.id == subject.createdBy.id -> granted() - else -> denied("You cannot update another user of yours", "comment.update.notYours") - } - } - - if (action == Action.DELETE) { - return denied("A comment can never be deleted", "comment.deleted.never") - } - - throw NoRuleDefinedException(action) + fun canUpdate(subject: S, citizen: CitizenI?): VoterResponse + where S : CommentI, + S : EntityCreatedBy = when { + citizen == null -> denied("You must be connected to update comment", "comment.update.notConnected") + citizen.id != subject.createdBy.id -> denied("You cannot update another user of yours", "comment.update.notYours") + else -> granted() } } diff --git a/src/main/kotlin/component/comment/generic/routes/CreateCommentChildrenRequest.kt b/src/main/kotlin/component/comment/generic/routes/CreateCommentChildrenRequest.kt index ca7101e..89a88eb 100644 --- a/src/main/kotlin/component/comment/generic/routes/CreateCommentChildrenRequest.kt +++ b/src/main/kotlin/component/comment/generic/routes/CreateCommentChildrenRequest.kt @@ -1,11 +1,12 @@ package fr.dcproject.component.comment.generic.routes import fr.dcproject.citizen +import fr.dcproject.citizenOrNull import fr.dcproject.component.comment.generic.CommentForUpdate import fr.dcproject.component.comment.generic.CommentRef import fr.dcproject.component.comment.generic.CommentRepository import fr.dcproject.component.comment.generic.CommentVoter -import fr.ktorVoter.assertCan +import fr.dcproject.voter.assert import io.ktor.application.* import io.ktor.features.* import io.ktor.http.* @@ -23,7 +24,7 @@ class CreateCommentChildrenRequest(val comment: CommentRef) { @KtorExperimentalAPI @KtorExperimentalLocationsAPI -fun Route.createCommentChildren(repo: CommentRepository) { +fun Route.createCommentChildren(repo: CommentRepository, voter: CommentVoter) { post { val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found") val newComment = CommentForUpdate( @@ -32,7 +33,7 @@ fun Route.createCommentChildren(repo: CommentRepository) { parent = parent ) - assertCan(CommentVoter.Action.CREATE, newComment) + voter.assert { canCreate(newComment, citizenOrNull) } repo.comment(newComment) call.respond(HttpStatusCode.Created, newComment) diff --git a/src/main/kotlin/component/comment/generic/routes/EditComment.kt b/src/main/kotlin/component/comment/generic/routes/EditComment.kt index 42d0150..d992732 100644 --- a/src/main/kotlin/component/comment/generic/routes/EditComment.kt +++ b/src/main/kotlin/component/comment/generic/routes/EditComment.kt @@ -1,10 +1,12 @@ package fr.dcproject.component.comment.generic.routes +import fr.dcproject.citizenOrNull import fr.dcproject.component.comment.generic.CommentRef import fr.dcproject.component.comment.generic.CommentRepository import fr.dcproject.component.comment.generic.CommentVoter -import fr.ktorVoter.assertCan +import fr.dcproject.voter.assert import io.ktor.application.* +import io.ktor.features.* import io.ktor.http.* import io.ktor.locations.* import io.ktor.request.* @@ -18,10 +20,10 @@ class EditCommentRequest(val comment: CommentRef) @KtorExperimentalAPI @KtorExperimentalLocationsAPI -fun Route.editComment(repo: CommentRepository) { +fun Route.editComment(repo: CommentRepository, voter: CommentVoter) { put { - val comment = repo.findById(it.comment.id)!! - assertCan(CommentVoter.Action.UPDATE, comment) + val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found") + voter.assert { canUpdate(comment, citizenOrNull) } comment.content = call.receiveText() repo.edit(comment) diff --git a/src/main/kotlin/component/comment/generic/routes/GetCommentChildrenRequest.kt b/src/main/kotlin/component/comment/generic/routes/GetCommentChildrenRequest.kt index 3e98e28..22d499b 100644 --- a/src/main/kotlin/component/comment/generic/routes/GetCommentChildrenRequest.kt +++ b/src/main/kotlin/component/comment/generic/routes/GetCommentChildrenRequest.kt @@ -1,8 +1,9 @@ package fr.dcproject.component.comment.generic.routes +import fr.dcproject.citizenOrNull import fr.dcproject.component.comment.generic.CommentRepository import fr.dcproject.component.comment.generic.CommentVoter -import fr.ktorVoter.assertCanAll +import fr.dcproject.voter.assert import io.ktor.application.* import io.ktor.http.* import io.ktor.locations.* @@ -25,7 +26,7 @@ class CommentChildrenRequest( @KtorExperimentalAPI @KtorExperimentalLocationsAPI -fun Route.getChildrenComments(repo: CommentRepository) { +fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) { get { val comments = repo.findByParent( @@ -34,7 +35,7 @@ fun Route.getChildrenComments(repo: CommentRepository) { it.limit ) - assertCanAll(CommentVoter.Action.VIEW, comments.result) + voter.assert { canView(comments.result, citizenOrNull) } call.respond(HttpStatusCode.OK, comments) } diff --git a/src/main/kotlin/component/comment/generic/routes/GetOneComment.kt b/src/main/kotlin/component/comment/generic/routes/GetOneComment.kt index 2c4c54d..37fc800 100644 --- a/src/main/kotlin/component/comment/generic/routes/GetOneComment.kt +++ b/src/main/kotlin/component/comment/generic/routes/GetOneComment.kt @@ -1,9 +1,10 @@ package fr.dcproject.component.comment.generic.routes +import fr.dcproject.citizenOrNull import fr.dcproject.component.comment.generic.CommentRef import fr.dcproject.component.comment.generic.CommentRepository import fr.dcproject.component.comment.generic.CommentVoter -import fr.ktorVoter.assertCan +import fr.dcproject.voter.assert import io.ktor.application.* import io.ktor.features.* import io.ktor.http.* @@ -18,10 +19,10 @@ class CommentRequest(val comment: CommentRef) @KtorExperimentalAPI @KtorExperimentalLocationsAPI -fun Route.getOneComment(repo: CommentRepository) { +fun Route.getOneComment(repo: CommentRepository, voter: CommentVoter) { get { - val comment = repo.findById(it.comment.id) ?: NotFoundException("Comment ${it.comment.id} not found") - assertCan(CommentVoter.Action.VIEW, comment) + val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment ${it.comment.id} not found") + voter.assert { canView(comment, citizenOrNull) } call.respond(HttpStatusCode.OK, comment) } diff --git a/src/main/kotlin/routes/CommentArticle.kt b/src/main/kotlin/routes/CommentArticle.kt index 3bb9d26..1bd6cb4 100644 --- a/src/main/kotlin/routes/CommentArticle.kt +++ b/src/main/kotlin/routes/CommentArticle.kt @@ -1,16 +1,15 @@ package fr.dcproject.routes import fr.dcproject.citizen +import fr.dcproject.citizenOrNull import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.citizen.Citizen import fr.dcproject.component.comment.article.CommentArticleRepository import fr.dcproject.component.comment.article.CommentArticleRepository.Sort import fr.dcproject.component.comment.generic.CommentForUpdate -import fr.dcproject.component.comment.generic.CommentVoter.Action.CREATE -import fr.dcproject.component.comment.generic.CommentVoter.Action.VIEW -import fr.ktorVoter.assertCan -import fr.ktorVoter.assertCanAll +import fr.dcproject.component.comment.generic.CommentVoter +import fr.dcproject.voter.assert import io.ktor.application.* import io.ktor.http.* import io.ktor.locations.* @@ -55,18 +54,18 @@ object CommentArticlePaths { } @KtorExperimentalLocationsAPI -fun Route.commentArticle(repo: CommentArticleRepository) { +fun Route.commentArticle(repo: CommentArticleRepository, voter: CommentVoter) { get { val comment = repo.findByTarget(it.article, it.page, it.limit, it.sort) if (comment.result.isNotEmpty()) { - assertCanAll(VIEW, comment.result) + voter.assert { canView(comment.result, citizenOrNull) } } call.respond(HttpStatusCode.OK, comment) } post { it.getComment(call).let { comment -> - assertCan(CREATE, comment) + voter.assert { canCreate(comment, citizenOrNull) } repo.comment(comment) call.respond(HttpStatusCode.Created, comment) } @@ -74,7 +73,7 @@ fun Route.commentArticle(repo: CommentArticleRepository) { get { repo.findByCitizen(it.citizen).let { comments -> - assertCanAll(VIEW, comments.result) + voter.assert { canView(comments.result, citizenOrNull) } call.respond(comments) } } diff --git a/src/main/kotlin/routes/CommentConstitution.kt b/src/main/kotlin/routes/CommentConstitution.kt index 03e1847..725a240 100644 --- a/src/main/kotlin/routes/CommentConstitution.kt +++ b/src/main/kotlin/routes/CommentConstitution.kt @@ -1,14 +1,13 @@ package fr.dcproject.routes import fr.dcproject.citizen +import fr.dcproject.citizenOrNull import fr.dcproject.component.citizen.Citizen import fr.dcproject.component.comment.generic.CommentForUpdate -import fr.dcproject.component.comment.generic.CommentVoter.Action.CREATE -import fr.dcproject.component.comment.generic.CommentVoter.Action.VIEW +import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.entity.ConstitutionRef import fr.dcproject.repository.CommentConstitutionRepository -import fr.ktorVoter.assertCan -import fr.ktorVoter.assertCanAll +import fr.dcproject.voter.assert import io.ktor.application.* import io.ktor.http.* import io.ktor.locations.* @@ -26,10 +25,10 @@ object CommentConstitutionPaths { } @KtorExperimentalLocationsAPI -fun Route.commentConstitution(repo: CommentConstitutionRepository) { +fun Route.commentConstitution(repo: CommentConstitutionRepository, voter: CommentVoter) { get { val comments = repo.findByTarget(it.constitution) - assertCanAll(VIEW, comments.result) + voter.assert { canView(comments.result, citizenOrNull) } call.respond(HttpStatusCode.OK, comments) } @@ -40,7 +39,7 @@ fun Route.commentConstitution(repo: CommentConstitutionRepository) { createdBy = citizen, content = content ) - assertCan(CREATE, comment) + voter.assert { canCreate(comment, citizenOrNull) } repo.comment(comment) call.respond(HttpStatusCode.Created, comment) @@ -48,7 +47,7 @@ fun Route.commentConstitution(repo: CommentConstitutionRepository) { get { val comments = repo.findByCitizen(it.citizen) - assertCanAll(VIEW, comments.result) + voter.assert { canView(comments.result, citizenOrNull) } call.respond(comments) } } \ No newline at end of file diff --git a/src/main/kotlin/voter/ConstitutionVoter.kt b/src/main/kotlin/voter/ConstitutionVoter.kt index 70d6c04..688eaec 100644 --- a/src/main/kotlin/voter/ConstitutionVoter.kt +++ b/src/main/kotlin/voter/ConstitutionVoter.kt @@ -1,7 +1,6 @@ package fr.dcproject.security.voter import fr.dcproject.component.comment.generic.CommentForView -import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.entity.UserI import fr.dcproject.user @@ -20,7 +19,7 @@ class ConstitutionVoter : Voter { } override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI { - if (!((action is Action || action is CommentVoter.Action || action is VoteVoter.Action) && + if (!((action is Action || action is VoteVoter.Action) && (subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is CommentForView<*, *>))) return abstain() val user = context.user @@ -44,7 +43,6 @@ class ConstitutionVoter : Voter { return granted() } - if (action is CommentVoter.Action) return voteForComment(action) if (action is VoteVoter.Action) return voteForVote(action, subject) if (action is Action) { @@ -66,16 +64,4 @@ class ConstitutionVoter : Voter { } return abstain() } - - private fun voteForComment(action: CommentVoter.Action): VoterResponseI { - if (action == CommentVoter.Action.CREATE) { - return granted() - } - - if (action == CommentVoter.Action.VIEW) { - return granted() - } - - return abstain() - } } diff --git a/src/main/kotlin/voter/VoterModule.kt b/src/main/kotlin/voter/VoterModule.kt index 27f94ef..b85d526 100644 --- a/src/main/kotlin/voter/VoterModule.kt +++ b/src/main/kotlin/voter/VoterModule.kt @@ -13,6 +13,9 @@ enum class Vote { } } + /** + * Convert vote to boolean + */ fun toBoolean(): Boolean = when (this) { GRANTED -> true DENIED -> false @@ -20,51 +23,103 @@ enum class Vote { } abstract class Voter { + /** + * A Shortcut for return a GrantedResponse + */ protected fun granted(message: String? = null, code: String? = null): GrantedResponse = GrantedResponse(this, message, code) + /** + * A Shortcut for return a DeniedResponse + */ protected fun denied(message: String, code: String): DeniedResponse = DeniedResponse(this, message, code) + /** + * Check all responses and return DENIED if one is DENIED + * + * If the list of responses is empty, return GRANTED + */ private fun VoterResponses.getOneResponse(): VoterResponse = this.firstOrNull { it.vote == Vote.DENIED } ?: granted() + /** + * An helper to convert a list of subject into one response + */ protected fun , T> canAll(items: S, action: (T) -> VoterResponse): VoterResponse = items .map { action(it) } .getOneResponse() } +/** + * Throw an Exception if voter return a DENIED response + */ fun T.assert(action: T.() -> VoterResponse) { action().assert() } +/** + * Check all responses and return DENIED if one is DENIED + * + * If the list of responses is empty, return GRANTED + */ fun VoterResponses.getOneResponse(): VoterResponse = this.firstOrNull { it.vote == Vote.DENIED } ?: GrantedResponse(first().voter) +/** + * Throw an Exception if one response is DENIED + */ fun VoterResponses.assert() = this.getOneResponse().assert() class VoterDeniedException(private val voterResponses: VoterResponses) : Throwable(voterResponses.first().message) { constructor(voterResponse: VoterResponse) : this(listOf(voterResponse)) + /** + * Get first response + */ fun first(): VoterResponse = voterResponses.first() + /** + * Check if the error code is present into the responses + */ fun hasErrorCode(code: String): Boolean = voterResponses .filter { it.vote == Vote.DENIED } .any { it.code == code } + /** + * Find and return the response than match with the error code + */ fun getErrorCode(code: String): VoterResponse? = voterResponses .firstOrNull { it.vote == Vote.DENIED && it.code == code } + /** + * Get a list of messages of all responses + */ fun getMessages(): List = voterResponses .mapNotNull { it.message } + /** + * Get the first message + */ fun getFirstMessage(): String? = voterResponses .first() .message } +/** + * The response that all Voter method return + * @see GrantedResponse + * @see DeniedResponse + */ sealed class VoterResponse( val vote: Vote, val voter: Voter, val message: String?, val code: String? ) { + /** + * Convert response as boolean + */ fun toBoolean(): Boolean = vote.toBoolean() + + /** + * Throw Exception if response if DENIED + */ fun assert() { if (this.vote == Vote.DENIED) { throw VoterDeniedException(this) diff --git a/src/test/kotlin/security/voter/CommentVoterTest.kt b/src/test/kotlin/security/voter/CommentVoterTest.kt index 17d53d0..d04da12 100644 --- a/src/test/kotlin/security/voter/CommentVoterTest.kt +++ b/src/test/kotlin/security/voter/CommentVoterTest.kt @@ -1,6 +1,5 @@ package fr.dcproject.security.voter -import fr.dcproject.citizenOrNull import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.citizen.Citizen @@ -11,10 +10,9 @@ import fr.dcproject.component.comment.generic.CommentForView import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.entity.User import fr.dcproject.entity.UserI -import fr.dcproject.voter.NoSubjectDefinedException -import fr.ktorVoter.* +import fr.dcproject.voter.Vote.DENIED +import fr.dcproject.voter.Vote.GRANTED import fr.postgresjson.connexion.Paginated -import io.ktor.application.* import io.ktor.locations.* import io.mockk.every import io.mockk.mockk @@ -24,7 +22,6 @@ import org.joda.time.DateTime import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.assertThrows import java.util.* import fr.dcproject.component.article.ArticleRepository as ArticleRepo @@ -112,109 +109,59 @@ internal class CommentVoterTest { mockkStatic("fr.dcproject.ApplicationContextKt") } - @Test - fun `support comment`(): Unit = CommentVoter().run { - val p = object : ActionI {} - mockk { - every { citizenOrNull } returns tesla - }.let { - this(CommentVoter.Action.VIEW, it, comment1).vote `should be` Vote.GRANTED - this(CommentVoter.Action.VIEW, it, article1).vote `should be` Vote.ABSTAIN - this(p, it, comment1).vote `should be` Vote.ABSTAIN - } - } - @Test fun `can be view the comment`() { - listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns tesla - }.let { - can(CommentVoter.Action.VIEW, it, comment1) `should be` true - } - } + CommentVoter() + .canView(comment1, tesla) + .vote `should be` GRANTED } @Test - fun `can be view the comment list`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns einstein - }.let { - canAll(CommentVoter.Action.VIEW, it, listOf(comment1)) `should be` true - } + fun `can be view the comment list`() { + CommentVoter() + .canView(listOf(comment1, comment2), einstein) + .vote `should be` GRANTED } @Test - fun `can be update your comment`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns tesla - }.let { - can(CommentVoter.Action.UPDATE, it, comment1) `should be` true - } + fun `can be update your comment`() { + CommentVoter() + .canUpdate(comment1, tesla) + .vote `should be` GRANTED } @Test - fun `can not be update other comment`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns einstein - }.let { - can(CommentVoter.Action.UPDATE, it, comment1) `should be` false - } + fun `can not be update other comment`() { + CommentVoter() + .canUpdate(comment1, einstein) + .vote `should be` DENIED } @Test - fun `can not be delete your comment`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns tesla - }.let { - can(CommentVoter.Action.DELETE, it, comment1) `should be` false - } + fun `can be create a comment`() { + CommentVoter() + .canCreate(comment1, tesla) + .vote `should be` GRANTED } @Test - fun `can be create a comment`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns tesla - }.let { - can(CommentVoter.Action.CREATE, it, comment1) `should be` true - } + fun `can not be create a comment if target is deleted`() { + CommentVoter() + .canCreate(commentTargetDeleted, tesla) + .vote `should be` DENIED } @Test - fun `can not be create a comment if target is deleted`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns tesla - }.let { - can(CommentVoter.Action.CREATE, it, commentTargetDeleted) `should be` false - } + fun `can not be create a comment with other creator`() { + CommentVoter() + .canCreate(comment1, einstein) + .vote `should be` DENIED } @Test - fun `can not be create a comment with other creator`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns einstein - }.let { - can(CommentVoter.Action.CREATE, it, comment1) `should be` false - } - } - - @Test - fun `can not be create a comment if is null`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns einstein - }.let { - assertThrows { - assertCan(CommentVoter.Action.CREATE, it, null) - } - } - } - - @Test - fun `can not be create a comment if not connected`(): Unit = listOf(CommentVoter()).run { - mockk { - every { citizenOrNull } returns null - }.let { - can(CommentVoter.Action.CREATE, it, comment1) `should be` false - } + fun `can not be create a comment if not connected`() { + CommentVoter() + .canCreate(comment1, null) + .vote `should be` DENIED } } \ No newline at end of file