diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt index 03c30ec..0cb6a8d 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt @@ -1,5 +1,6 @@ package fr.dcproject.component.vote.routes +import fr.dcproject.application.http.badRequestIfNotValid import fr.dcproject.common.security.assert import fr.dcproject.common.utils.receiveOrBadRequest import fr.dcproject.component.auth.citizen @@ -11,6 +12,9 @@ import fr.dcproject.component.vote.VoteAccessControl import fr.dcproject.component.vote.database.VoteConstitutionRepository import fr.dcproject.component.vote.database.VoteForUpdate import fr.dcproject.component.vote.routes.PutVoteOnConstitution.ConstitutionVoteRequest.Input +import io.konform.validation.Validation +import io.konform.validation.jsonschema.maximum +import io.konform.validation.jsonschema.minimum import io.ktor.application.call import io.ktor.features.NotFoundException import io.ktor.http.HttpStatusCode @@ -26,17 +30,25 @@ object PutVoteOnConstitution { @Location("/constitutions/{constitution}/vote") class ConstitutionVoteRequest(constitution: UUID) { val constitution = ConstitutionRef(constitution) - data class Input(var note: Int) + data class Input(var note: Int) { + fun validate() = Validation { + Input::note { + minimum(-1) + maximum(1) + } + }.validate(this) + } } fun Route.voteConstitution(repo: VoteConstitutionRepository, ac: VoteAccessControl, constitutionRepo: ConstitutionRepository) { put { mustBeAuth() val constitution = constitutionRepo.findById(it.constitution.id) ?: throw NotFoundException("Unable to find constitution ${it.constitution.id}") - val content = call.receiveOrBadRequest() + val input = call.receiveOrBadRequest() + .apply { validate().badRequestIfNotValid() } val vote = VoteForUpdate( target = constitution, - note = content.note, + note = input.note, createdBy = this.citizen ) ac.assert { canCreate(vote, citizenOrNull) } diff --git a/src/main/resources/openapi.yaml b/src/main/resources/openapi.yaml index 76b8846..0abb426 100644 --- a/src/main/resources/openapi.yaml +++ b/src/main/resources/openapi.yaml @@ -1194,6 +1194,12 @@ paths: responses: 201: description: Return only http status 201 on success + 400: + description: BadReqest + content: + application/json: + schema: + $ref: '#/components/schemas/400' /citizens/{citizen}/votes: parameters: - $ref: '#/components/parameters/citizen' diff --git a/src/test/kotlin/integration/Vote routes.kt b/src/test/kotlin/integration/Vote routes.kt index efd54bc..a01aaf7 100644 --- a/src/test/kotlin/integration/Vote routes.kt +++ b/src/test/kotlin/integration/Vote routes.kt @@ -88,21 +88,60 @@ class `Vote routes` : BaseTest() { } } - @Test - fun `I can vote constitution`() { + @TestFactory + fun `I can vote constitution`(): List { withIntegrationApplication { `Given I have citizen`("Gregor", "Mendel") `Given I have constitution`(id = "76e79c89-efc1-492d-9e8f-dc9717363a11") - `When I send a PUT request`("/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote") { - `authenticated as`("Gregor", "Mendel") - `with body`( - """ - { - "note": 1 + } + return (-1..1).map { note -> + DynamicTest.dynamicTest("""I can vote constitution with note "$note"""") { + withIntegrationApplication { + `When I send a PUT request`("/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote") { + `authenticated as`("Gregor", "Mendel") + `with body`( + """ + { + "note": $note + }² + """ + ) + } `Then the response should be` Created + } + } + } + } + + @TestFactory + @Tag("BadRequest") + fun `I cannot vote constitution with wrong request`(): List { + withIntegrationApplication { + `Given I have citizen`("Gregor", "Mendel") + `Given I have constitution`(id = "76e79c89-efc1-492d-9e8f-dc9717363a11") + } + + return listOf(-10, -2, +2, +10).map { note -> + DynamicTest.dynamicTest("""I can vote constitution with note "$note"""") { + withIntegrationApplication { + `When I send a PUT request`( + "/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote", + ALL - REQUEST_BODY + ) { + `authenticated as`("Gregor", "Mendel") + `with body`( + """ + { + "note": $note + } + """ + ) + } `Then the response should be` BadRequest and { + `And the response should not be null`() + `And the response should contain`("$.invalidParams[0].name", ".note") + `And the response should contain`("$.invalidParams[0].reason", if (note > 0) "must be at most '1'" else "must be at least '-1'") } - """ - ) - } `Then the response should be` Created + } + } } }