Merge branch 'refactoring-test'

This commit is contained in:
2020-03-17 15:52:53 +01:00
64 changed files with 1921 additions and 594 deletions

View File

@@ -6,6 +6,7 @@
<option name="MAIN_CLASS_NAME" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" /> <option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea -Djdk.attach.allowAttachSelf=true" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" /> <value defaultName="wholeProject" />

View File

@@ -6,7 +6,7 @@
<option name="MAIN_CLASS_NAME" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" /> <option name="TEST_OBJECT" value="tags" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.options=&quot;--tags ~@online&quot;" /> <option name="VM_PARAMETERS" value="-ea -Dcucumber.options=&quot;--tags ~@online&quot; -Djdk.attach.allowAttachSelf=true" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" /> <value defaultName="wholeProject" />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Article Tests" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Article Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />

24
.idea/runConfigurations/Auth_Tests.xml generated Normal file
View File

@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Auth Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@auth&quot;" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="!online" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Citizen Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@citizen&quot;" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="!online" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Comment Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@comment&quot;" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="!online" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Constitution Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@constitution&quot;" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="!online" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -2,10 +2,12 @@
<configuration default="false" name="Cucumber Tests" type="JUnit" factoryName="JUnit"> <configuration default="false" name="Cucumber Tests" type="JUnit" factoryName="JUnit">
<output_file path="$PROJECT_DIR$/var/log/test/cucumber.out.log" /> <output_file path="$PROJECT_DIR$/var/log/test/cucumber.out.log" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" /> <option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" /> <option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" /> <option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Djdk.attach.allowAttachSelf=true" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<envs> <envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" /> <env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Tests (offline)" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Cucumber Tests (offline)" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />
@@ -13,7 +13,7 @@
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" /> <option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" /> <option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot; not @online&quot; -Dstrict" /> <option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot; not @online&quot; -Djdk.attach.allowAttachSelf=true" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" /> <value defaultName="wholeProject" />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Follow Tests" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Follow Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />
@@ -7,7 +7,7 @@
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" /> <option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" /> <option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@follow&quot; -Dstrict" /> <option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@follow&quot;" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" /> <value defaultName="wholeProject" />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Mark as @error" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Mark as @error" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Opinion Tests" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Opinion Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />
@@ -7,7 +7,7 @@
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" /> <option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" /> <option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@opinion&quot; -Dstrict" /> <option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@opinion&quot;" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" /> <value defaultName="wholeProject" />

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test All SQL" type="DatabaseScript" folderName="SQL TEST" editBeforeRun="true"> <configuration default="false" name="Test All SQL" type="DatabaseScript" folderName="SQL TEST">
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_article_by_id.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_article_by_id.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_articles.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_articles.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_articles_versions_by_id.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/article/find_articles_versions_by_id.sql" />
@@ -53,6 +53,7 @@
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion_choice.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/find_workgroup_by_id.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/find_workgroup_by_id.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/find_workgroups.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/find_workgroups.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/upsert_workgroup.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/workgroup/upsert_workgroup.sql" />

View File

@@ -9,6 +9,7 @@
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql" /> <script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql" />
<script-file value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion_choice.sql" />
<script-file value="$PROJECT_DIR$/src/test/sql/opinion.sql" /> <script-file value="$PROJECT_DIR$/src/test/sql/opinion.sql" />
<script-mode>FILE</script-mode> <script-mode>FILE</script-mode>
<data-source id="a9a6d0e9-327d-4e7d-9b93-3cb6f7948866" namespace="database/&quot;test&quot;/schema/&quot;public&quot;" /> <data-source id="a9a6d0e9-327d-4e7d-9b93-3cb6f7948866" namespace="database/&quot;test&quot;/schema/&quot;public&quot;" />

24
.idea/runConfigurations/Vote_Tests.xml generated Normal file
View File

@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Vote Tests" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" />
<useClassPathOnly />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea -Dcucumber.filter.tags=&quot;@vote&quot;" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="!online" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

29
.idea/runConfigurations/Voter_Tests.xml generated Normal file
View File

@@ -0,0 +1,29 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Voter Tests" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<useClassPathOnly />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="fr.dcproject.security.voter.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="fr.dcproject" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" />
<option name="VM_PARAMETERS" value="-ea -Djdk.attach.allowAttachSelf=true" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<envs>
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
</envs>
<dir value="$PROJECT_DIR$" />
<tag value="voter" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Workgroup test" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="Workgroup test" type="JUnit" factoryName="JUnit" folderName="Cucumber" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<useClassPathOnly /> <useClassPathOnly />
@@ -25,7 +25,6 @@
<tag value="!online" /> <tag value="!online" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Test Workgroup" run_configuration_type="DatabaseScript" />
</method> </method>
</configuration> </configuration>
</component> </component>

View File

@@ -8,7 +8,7 @@ import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import java.util.* import java.util.*
class OpinionChoice( class OpinionChoice(
id: UUID, id: UUID? = null,
val name: String, val name: String,
val target: List<String>? val target: List<String>?
) : OpinionChoiceRef(id), ) : OpinionChoiceRef(id),
@@ -16,5 +16,5 @@ class OpinionChoice(
EntityDeletedAt by EntityDeletedAtImp() EntityDeletedAt by EntityDeletedAtImp()
open class OpinionChoiceRef( open class OpinionChoiceRef(
id: UUID id: UUID?
) : UuidEntity(id) ) : UuidEntity(id ?: UUID.randomUUID())

View File

@@ -6,7 +6,7 @@ import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import java.util.* import java.util.*
class Workgroup( class Workgroup(
id: UUID?, id: UUID? = null,
name: String, name: String,
description: String, description: String,
logo: String? = null, logo: String? = null,
@@ -28,7 +28,7 @@ class Workgroup(
EntityUpdatedAt by EntityUpdatedAtImp() EntityUpdatedAt by EntityUpdatedAtImp()
open class WorkgroupSimple<Z : CitizenRef>( open class WorkgroupSimple<Z : CitizenRef>(
id: UUID?, id: UUID? = null,
var name: String, var name: String,
var description: String, var description: String,
var logo: String? = null, var logo: String? = null,
@@ -40,7 +40,7 @@ open class WorkgroupSimple<Z : CitizenRef>(
EntityDeletedAt by EntityDeletedAtImp() EntityDeletedAt by EntityDeletedAtImp()
open class WorkgroupRef( open class WorkgroupRef(
id: UUID? id: UUID? = null
) : UuidEntity(id ?: UUID.randomUUID()), WorkgroupI ) : UuidEntity(id ?: UUID.randomUUID()), WorkgroupI
interface WorkgroupWithAuthI<Z : CitizenWithUserI> : WorkgroupWithMembersI<Z>, EntityCreatedBy<Z>, EntityDeletedAt { interface WorkgroupWithAuthI<Z : CitizenWithUserI> : WorkgroupWithMembersI<Z>, EntityCreatedBy<Z>, EntityDeletedAt {

View File

@@ -43,6 +43,12 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
.selectOne( .selectOne(
"id" to id "id" to id
) )
fun upsertOpinionChoice(opinionChoice: OpinionChoiceEntity): OpinionChoiceEntity = requester
.getFunction("upsert_opinion_choice")
.selectOne(
"resource" to opinionChoice
)!!
} }
open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requester) { open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requester) {

View File

@@ -34,9 +34,6 @@ object ConstitutionPaths {
@Location("/constitutions/{constitution}") @Location("/constitutions/{constitution}")
class ConstitutionRequest(val constitution: ConstitutionEntity) class ConstitutionRequest(val constitution: ConstitutionEntity)
@Location("/constitutions/{constitution}/follow")
class ConstitutionFollowRequest(val constitution: ConstitutionEntity)
@Location("/constitutions") @Location("/constitutions")
class PostConstitutionRequest class PostConstitutionRequest
} }

View File

@@ -15,7 +15,7 @@ import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepositor
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
object FollowConstitutionPaths { object FollowConstitutionPaths {
@Location("/constitutions/{constitution}/follow") @Location("/constitutions/{constitution}/follows")
class ConstitutionFollowRequest(val constitution: ConstitutionRef) class ConstitutionFollowRequest(val constitution: ConstitutionRef)
@Location("/citizens/{citizen}/follows/constitutions") @Location("/citizens/{citizen}/follows/constitutions")
@@ -38,6 +38,13 @@ fun Route.followConstitution(repo: FollowConstitutionRepository) {
call.respond(HttpStatusCode.NoContent) call.respond(HttpStatusCode.NoContent)
} }
get<FollowConstitutionPaths.ConstitutionFollowRequest> {
repo.findFollow(citizen, it.constitution)?.let { follow ->
assertCan(VIEW, follow)
call.respond(follow)
} ?: call.respond(HttpStatusCode.NotFound)
}
get<FollowConstitutionPaths.CitizenFollowConstitutionRequest> { get<FollowConstitutionPaths.CitizenFollowConstitutionRequest> {
val follows = repo.findByCitizen(it.citizen) val follows = repo.findByCitizen(it.citizen)
assertCan(VIEW, follows.result) assertCan(VIEW, follows.result)

View File

@@ -2,7 +2,6 @@ package fr.dcproject.security.voter
import fr.dcproject.entity.ArticleAuthI import fr.dcproject.entity.ArticleAuthI
import fr.dcproject.entity.ArticleI import fr.dcproject.entity.ArticleI
import fr.dcproject.entity.ArticleSimpleI
import fr.dcproject.entity.UserI import fr.dcproject.entity.UserI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
@@ -27,7 +26,7 @@ class ArticleVoter : Voter {
if (action == Action.VIEW) return view(subject, user) if (action == Action.VIEW) return view(subject, user)
if (action == Action.DELETE) return delete(subject, user) if (action == Action.DELETE) return delete(subject, user)
if (action == Action.UPDATE) return update(subject, user) if (action == Action.UPDATE) return update(subject, user)
if (action is CommentVoter.Action) return voteForComment(action) if (action is CommentVoter.Action) return voteForComment(action, subject)
if (action is VoteVoter.Action) return voteForVote(action, subject) if (action is VoteVoter.Action) return voteForVote(action, subject)
if (action is Action) return Vote.DENIED if (action is Action) return Vote.DENIED
@@ -67,23 +66,36 @@ class ArticleVoter : Voter {
private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote { private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote {
if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) { if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) {
val target = subject.target val target = subject.target
if (target !is ArticleSimpleI) { if (target is ArticleAuthI<*>) {
return Vote.ABSTAIN if (target.isDeleted()) {
} return Vote.DENIED
if (target.isDeleted()) { }
} else if (target is ArticleI) {
return Vote.DENIED return Vote.DENIED
} }
} }
return Vote.ABSTAIN return Vote.ABSTAIN
} }
private fun voteForComment(action: CommentVoter.Action): Vote { private fun voteForComment(action: CommentVoter.Action, subject: Any?): Vote {
if (action == CommentVoter.Action.CREATE) { if (subject is CommentEntity<*>) {
return Vote.GRANTED val target = subject.target
} if (target is ArticleAuthI<*>) {
if (target.isDeleted()) {
return Vote.DENIED
}
} else if (target is ArticleI) {
return Vote.DENIED
}
if (action == CommentVoter.Action.CREATE) {
return Vote.GRANTED
}
if (action == CommentVoter.Action.VIEW) { if (action == CommentVoter.Action.VIEW) {
return Vote.GRANTED return Vote.GRANTED
}
} else {
return Vote.DENIED
} }
return Vote.ABSTAIN return Vote.ABSTAIN

View File

@@ -17,7 +17,7 @@ class CitizenVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is List<*> || subject is CitizenBasicI?) .and(subject is CitizenBasicI?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -32,14 +32,6 @@ class CitizenVoter : Voter {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is CitizenBasicI || it.isDeleted()) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -13,32 +13,32 @@ class CommentVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is Comment<*>? || subject is List<*>) .and(subject is Comment<*>?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
val user = call.user val user = call.user
if (action == Action.CREATE && user != null) {
if (subject !is Comment<*>) {
return Vote.DENIED
}
if (action == Action.CREATE) {
if (user == null) {
return Vote.DENIED
}
if (subject.createdBy.user.id != user.id) {
return Vote.DENIED
}
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.VIEW) { if (action == Action.VIEW) {
if (subject is Comment<*>) { return if (subject.isDeleted()) Vote.DENIED
return if (subject.isDeleted()) Vote.DENIED else Vote.GRANTED
else Vote.GRANTED
}
if (subject is List<*>) {
subject.forEach {
if (it !is Comment<*> || it.isDeleted()) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED
} }
if (action == Action.UPDATE && user != null && subject is Comment<*> && user.id == subject.createdBy.user.id) { if (action == Action.UPDATE && user != null && user.id == subject.createdBy.user.id) {
return Vote.GRANTED return Vote.GRANTED
} }

View File

@@ -16,7 +16,7 @@ class ConstitutionVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action) return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action)
.and(subject is List<*> || subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is Comment<*>) .and(subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is Comment<*>)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -30,14 +30,6 @@ class ConstitutionVoter : Voter {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is ConstitutionSimple<*, *> || it.isDeleted()) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -13,7 +13,7 @@ class FollowVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is List<*> || subject is FollowEntity<*>?) .and(subject is FollowEntity<*>?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -32,14 +32,6 @@ class FollowVoter : Voter {
if (subject is FollowEntity<*>) { if (subject is FollowEntity<*>) {
return voteView(user, subject) return voteView(user, subject)
} }
if (subject is List<*>) {
subject.forEach {
if (it !is FollowEntity<*> || voteView(user, it) == Vote.DENIED) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -10,7 +10,7 @@ class OpinionChoiceVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is OpinionChoice? || subject is List<*>) .and(subject is OpinionChoice?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -18,14 +18,6 @@ class OpinionChoiceVoter : Voter {
if (subject is OpinionChoice) { if (subject is OpinionChoice) {
return Vote.GRANTED return Vote.GRANTED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is OpinionChoice) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -12,7 +12,7 @@ class OpinionVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is Opinion<*>? || subject is List<*>) .and(subject is Opinion<*>?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -25,14 +25,6 @@ class OpinionVoter : Voter {
if (subject is Opinion<*>) { if (subject is Opinion<*>) {
return Vote.GRANTED return Vote.GRANTED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is Opinion<*>) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }
@@ -45,10 +37,6 @@ class OpinionVoter : Voter {
else Vote.DENIED else Vote.DENIED
} }
if (action is Action) {
return Vote.DENIED
}
return Vote.ABSTAIN return Vote.ABSTAIN
} }
} }

View File

@@ -10,10 +10,7 @@ class VoteVoter : Voter {
} }
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return action is Action && ( return action is Action && subject is VoteEntity<*>?
subject is VoteEntity<*>? ||
subject is List<*>
)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -30,16 +27,6 @@ class VoteVoter : Voter {
Vote.GRANTED Vote.GRANTED
} }
} }
if (subject is List<*>) {
subject.forEach {
if (it !is VoteEntity<*> || it.createdBy.user.id != user.id) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -15,7 +15,7 @@ class WorkgroupVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is List<*> || subject is WorkgroupI?) .and(subject is WorkgroupI?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -31,14 +31,6 @@ class WorkgroupVoter : Voter {
else if (subject.anonymous && user != null && subject.isMember(user)) Vote.GRANTED else if (subject.anonymous && user != null && subject.isMember(user)) Vote.GRANTED
else Vote.DENIED else Vote.DENIED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is WorkgroupWithAuthI<*> || it.isDeleted()) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }
@@ -52,10 +44,9 @@ class WorkgroupVoter : Voter {
} }
return Vote.DENIED return Vote.DENIED
} } else if (subject !is WorkgroupWithAuthI<*> && (action == Action.DELETE || action == Action.UPDATE)) {
throw object :
if (action is Action) { VoterException("Unable to define if your are granted, the subject must implement 'WorkgroupWithAuthI'") {}
return Vote.DENIED
} }
return Vote.ABSTAIN return Vote.ABSTAIN

View File

@@ -0,0 +1,20 @@
create or replace function upsert_opinion_choice(inout resource json)
language plpgsql as
$$
declare
_id uuid = coalesce((resource->>'id')::uuid, uuid_generate_v4());
begin
insert into opinion_choice (id, name, target)
select
_id,
name,
target
from json_populate_record(null::opinion_choice, resource)
on conflict (name) do update set
target = excluded.target;
select find_opinion_choice_by_id(_id) into resource;
end;
$$;
-- drop function if exists upsert_opinion_choice(json);

View File

@@ -35,10 +35,10 @@ class FollowTest {
"username":"jaque", "username":"jaque",
"blocked_at":null, "blocked_at":null,
"plain_password":"azerty", "plain_password":"azerty",
"created_at":null, "created_at":"2019-08-09T11:42:47.168Z",
"updated_at":null "updated_at":"2019-08-09T11:42:47.168Z"
}, },
"created_at":null "created_at":"2019-08-09T11:42:47.168Z"
}, },
"target":{ "target":{
"id":"34588ea7-c180-4694-801b-1b5c5a6ed73f", "id":"34588ea7-c180-4694-801b-1b5c5a6ed73f",
@@ -66,15 +66,15 @@ class FollowTest {
"username":"jaque", "username":"jaque",
"blocked_at":null, "blocked_at":null,
"plain_password":"azerty", "plain_password":"azerty",
"created_at":null, "created_at":"2019-08-09T11:42:47.168Z",
"updated_at":null "updated_at":"2019-08-09T11:42:47.168Z"
} }
}, },
"version_id":"a4aa7dd4-d174-42d2-9ba5-ae6f1129ffce", "version_id":"a4aa7dd4-d174-42d2-9ba5-ae6f1129ffce",
"version_number":null, "version_number":null,
"created_at":null "created_at":null
}, },
"created_at":null "created_at":"2019-08-09T11:42:47.168Z"
}""".trimIndent() }""".trimIndent()
@Test @Test

View File

@@ -42,7 +42,6 @@ class RunCucumberTest : En, KoinTest {
withTestApplication({ module(CUCUMBER) }) { withTestApplication({ module(CUCUMBER) }) {
migrations() migrations()
fixtures()
} }
unitialized = true unitialized = true
} }

View File

@@ -72,12 +72,12 @@ class VoteTest {
"blocked_at": null, "blocked_at": null,
"plain_password": "azerty", "plain_password": "azerty",
"roles": [], "roles": [],
"created_at": null, "created_at": "2019-08-09T11:42:47.168Z",
"updated_at": null "updated_at": "2019-08-09T11:42:47.168Z"
}, },
"deleted": false, "deleted": false,
"created_at": null, "created_at": "2019-08-09T11:42:47.168Z",
"deleted_at": null "deleted_at": "2019-08-09T11:42:47.168Z"
}, },
"votes": { "votes": {
"up": 0, "up": 0,

View File

@@ -9,7 +9,6 @@ import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import java.util.concurrent.CompletionException
import fr.dcproject.entity.Article as ArticleEntity import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
@@ -18,93 +17,77 @@ import fr.dcproject.repository.Citizen as CitizenRepository
class ArticleSteps : En, KoinTest { class ArticleSteps : En, KoinTest {
init { init {
/** Given("I have {int} article") { nb: Int ->
* @deprecated repeat(nb) {
*/ createArticle()
Given("I have article with id {string}") { id: String ->
var citizen = Citizen(
name = CitizenI.Name("John", "Doe"),
email = "john.doe@gmail.com",
birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty")
)
try {
get<CitizenRepository>().insertWithUser(citizen)
} catch (e: CompletionException) {
citizen = get<CitizenRepository>().findByUsername("john-doe")!!
}
val article = ArticleEntity(
id = UUID.fromString(id),
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = citizen
)
get<ArticleRepository>().upsert(article)
}
Given("I have article") { extraData: DataTable ->
extraData.asMap<String, String>(String::class.java, String::class.java).let { params ->
val username = params["createdBy"]?.toLowerCase()?.replace(' ', '-')
?: error("You must provide the 'createdBy' parameter")
val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
val id = params["id"]?.toUUID() ?: UUID.randomUUID()
val article = ArticleEntity(
id = id,
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = citizen
)
get<ArticleRepository>().upsert(article)
} }
} }
Given("I have article with id {string} created by {string}") { id: String, username: String -> Given("I have article") { extraData: DataTable? ->
val citizen = get<CitizenRepository>().findByUsername(username)!! createArticle(extraData)
val article = ArticleEntity(
id = UUID.fromString(id),
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = citizen
)
get<ArticleRepository>().upsert(article)
} }
Given("I have comment {string} on article {string}") { commentId: String, articleId: String -> Given("I have article with ID {string}") { id: String ->
var citizen = Citizen( createArticle(id = UUID.fromString(id))
name = CitizenI.Name("John", "Doe"), }
email = "john.doe@gmail.com",
birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty")
)
try { Given("I have comment created by {word} {word} on article {string}:") { firstName: String, lastName: String, articleId: String, extraData: DataTable? ->
get<CitizenRepository>().insertWithUser(citizen) commentArticle(articleId, firstName, lastName, extraData)
} catch (e: CompletionException) { }
citizen = get<CitizenRepository>().findByUsername("john-doe")!! Given("I have comment created by {word} {word} on article {string}") { firstName: String, lastName: String, articleId: String ->
} commentArticle(articleId, firstName, lastName)
val article = ArticleEntity(
id = UUID.fromString(articleId),
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = citizen
)
get<ArticleRepository>().upsert(article)
val comment: CommentEntity<ArticleRef> = CommentEntity(
id = UUID.fromString(commentId),
createdBy = citizen,
target = article,
content = "hello"
)
get<CommentArticle>().comment(comment)
} }
} }
private fun createArticle(extraData: DataTable? = null, id: UUID? = null) {
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
val createdByUsername = params?.get("createdBy")
val username = (createdByUsername ?: "username" + UUID.randomUUID().toString())
.toLowerCase().replace(' ', '-')
val createdBy = if (createdByUsername != null) {
get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
} else {
val first = "firstName" + UUID.randomUUID().toString()
val last = "lastName" + UUID.randomUUID().toString()
Citizen(
birthday = DateTime.now(),
name = CitizenI.Name(
first,
last
),
email = "$first@fakeemail.com",
user = UserEntity(username = username, plainPassword = "azerty")
).also {
get<CitizenRepository>().insertWithUser(it)
}
}
val article = ArticleEntity(
id = id ?: params?.get("id")?.toUUID() ?: UUID.randomUUID(),
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = createdBy
)
get<ArticleRepository>().upsert(article)
}
private fun commentArticle(articleId: String, firstName: String, lastName: String, extraData: DataTable? = null, id: UUID? = null) {
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
val article = get<ArticleRepository>().findById(UUID.fromString(articleId)) ?: error("Article not exist")
val citizen = get<CitizenRepository>().findByUsername(
("$firstName-$lastName".toLowerCase()).toLowerCase().replace(' ', '-')
) ?: error("Citizen not exist")
val comment: CommentEntity<ArticleRef> = CommentEntity(
id = id ?: params?.get("id")?.let { UUID.fromString(it) } ?: UUID.randomUUID(),
createdBy = citizen,
target = article,
content = params?.get("content") ?: "hello"
)
get<CommentArticle>().comment(comment)
}
} }

View File

@@ -0,0 +1,55 @@
package feature
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.User
import io.cucumber.datatable.DataTable
import io.cucumber.java8.En
import org.joda.time.DateTime
import org.koin.test.KoinTest
import org.koin.test.get
import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
class CitizenSteps : En, KoinTest {
init {
Given("I have citizen") { extraData: DataTable? ->
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
createCitizen(
params?.get("firstName") ?: "firstName" + UUID.randomUUID(),
params?.get("lastName") ?: "lastName" + UUID.randomUUID(),
extraData
)
}
Given("I have citizen {word} {word}") { firstName: String, lastName: String ->
createCitizen(firstName, lastName)
}
Given("I have citizen {word} {word} with ID {string}") { firstName: String, lastName: String, id: String ->
createCitizen(firstName, lastName, id = UUID.fromString(id))
}
}
private fun createCitizen(firstName: String, lastName: String, extraData: DataTable? = null, id: UUID? = null) {
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
val id: UUID = id ?: params?.get("id")?.let { UUID.fromString(it) } ?: UUID.randomUUID()
val email = params?.get("email") ?: ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr"
val user = User(
id = id,
username = "$firstName-$lastName".toLowerCase(),
plainPassword = "azerty"
)
val citizen = Citizen(
id = id,
name = CitizenI.Name(firstName, lastName),
email = email,
birthday = DateTime.now(),
user = user
)
get<CitizenRepository>().insertWithUser(citizen)
}
}

View File

@@ -1,62 +1,102 @@
package feature package feature
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.ConstitutionSimple.TitleSimple import fr.dcproject.repository.CommentConstitution
import fr.dcproject.entity.request.Constitution import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
import org.joda.time.DateTime import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import java.util.concurrent.CompletionException import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository import fr.dcproject.repository.Constitution as ConstitutionRepository
class ConstitutionSteps : En, KoinTest { class ConstitutionSteps : En, KoinTest {
init { init {
Given("I have constitution with id {string}") { id: String -> Given("I have {int} constitution") { nb: Int ->
var citizen = Citizen( repeat(nb) {
id = UUID.fromString(id), createConstitution()
name = CitizenI.Name("John", "Doe"),
email = "john.doe@gmail.com",
birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty")
)
try {
get<CitizenRepository>().insertWithUser(citizen)
} catch (e: CompletionException) {
citizen = get<CitizenRepository>().findByUsername("john-doe")!!
} }
val title1 = Constitution.Title(
name = "My Title"
)
val constitution = Constitution(
title = "hello",
titles = mutableListOf(title1),
anonymous = false
)
get<ConstitutionRepository>().upsert(constitution.create(citizen))
} }
Given("I have constitution with id {string} created by {string}") { id: String, username: String -> Given("I have constitution") { extraData: DataTable? ->
val citizen = get<CitizenRepository>().findByUsername(username)!! createConstitution(extraData)
}
val title1 = TitleSimple<ArticleRef>( Given("I have constitution with ID {string}") { id: String? ->
name = "My Title" createConstitution(id = UUID.fromString(id))
) }
val constitution = ConstitutionSimple<CitizenSimple, TitleSimple<ArticleRef>>( Given("I have comment created by {word} {word} on constitution {string}:") { firstName: String, lastName: String, constitutionId: String, extraData: DataTable? ->
id = UUID.fromString(id), commentConstitution(constitutionId, firstName, lastName, extraData)
title = "hello", }
titles = mutableListOf(title1), Given("I have comment created by {word} {word} on constitution {string}") { firstName: String, lastName: String, constitutionId: String ->
anonymous = false, commentConstitution(constitutionId, firstName, lastName)
createdBy = citizen
)
get<ConstitutionRepository>().upsert(constitution)
} }
} }
private fun createConstitution(extraData: DataTable? = null, id: UUID? = null) {
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
val createdByUsername = params?.get("createdBy")
val username = (createdByUsername ?: "username" + UUID.randomUUID().toString())
.toLowerCase().replace(' ', '-')
val createdBy = if (createdByUsername != null) {
get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
} else {
val first = "firstName" + UUID.randomUUID().toString()
val last = "lastName" + UUID.randomUUID().toString()
Citizen(
birthday = DateTime.now(),
name = CitizenI.Name(
first,
last
),
email = "$first@fakeemail.com",
user = UserEntity(username = username, plainPassword = "azerty")
).also {
get<CitizenRepository>().insertWithUser(it)
}
}
val title1 = ConstitutionSimple.TitleSimple<ArticleRef>(
name = "My Title"
)
val constitution = ConstitutionSimple<CitizenSimple, ConstitutionSimple.TitleSimple<ArticleRef>>(
id = id ?: params?.get("id")?.toUUID() ?: UUID.randomUUID(),
title = "hello",
titles = mutableListOf(title1),
anonymous = false,
createdBy = createdBy
)
get<ConstitutionRepository>().upsert(constitution)
}
private fun commentConstitution(
constitutionId: String,
firstName: String,
lastName: String,
extraData: DataTable? = null
) {
val params = extraData?.asMap<String, String>(String::class.java, String::class.java)
val constitution =
get<ConstitutionRepository>().findById(UUID.fromString(constitutionId)) ?: error("Constitution not exist")
val citizen = get<CitizenRepository>().findByUsername(
("$firstName-$lastName".toLowerCase()).toLowerCase().replace(' ', '-')
) ?: error("Citizen not exist")
val comment: CommentEntity<ConstitutionRef> = CommentEntity(
id = params?.get("id")?.let { UUID.fromString(it) } ?: UUID.randomUUID(),
createdBy = citizen,
target = constitution,
content = params?.get("content") ?: "hello"
)
get<CommentConstitution>().comment(comment)
}
} }

View File

@@ -1,6 +1,7 @@
package feature package feature
import fr.dcproject.entity.ArticleRef import fr.dcproject.entity.ArticleRef
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.Follow import fr.dcproject.entity.Follow
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.java8.En import io.cucumber.java8.En
@@ -8,14 +9,21 @@ import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.FollowArticle as FollowArticleRepository import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository
class FollowSteps : En, KoinTest { class FollowSteps : En, KoinTest {
init { init {
Given("The citizen {word} {word} follow article {string}") { firstName: String, lastName: String, articleId: String -> Given("I have follow of {word} {word} on article {string}") { firstName: String, lastName: String, articleId: String ->
val username = "$firstName-$lastName".toLowerCase() val username = "$firstName-$lastName".toLowerCase()
val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist") val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
val follow = Follow(createdBy = citizen, target = ArticleRef(articleId.toUUID())) val follow = Follow(createdBy = citizen, target = ArticleRef(articleId.toUUID()))
get<FollowArticleRepository>().follow(follow) get<FollowArticleRepository>().follow(follow)
} }
Given("I have follow of {word} {word} on constitution {string}") { firstName: String, lastName: String, constitutionId: String ->
val username = "$firstName-$lastName".toLowerCase()
val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
val follow = Follow(createdBy = citizen, target = ConstitutionRef(constitutionId.toUUID()))
get<FollowConstitutionRepository>().follow(follow)
}
} }
} }

View File

@@ -2,91 +2,14 @@ package feature
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.User
import fr.postgresjson.connexion.Requester
import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
import io.ktor.http.HttpHeaders import io.ktor.http.HttpHeaders
import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import org.koin.test.inject
import java.util.*
import java.util.concurrent.CompletionException
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
class KtorServerAuthSteps : En, KoinTest { class KtorServerAuthSteps : En, KoinTest {
private val requester: Requester by inject()
init { init {
When("I have citizen:") { body: DataTable ->
val data = body.asMap<String, String>(String::class.java, String::class.java)
val username = (data["firstName"] + "-" + data["lastName"]).toLowerCase()
val user = User(
username = username,
plainPassword = "azerty"
)
val citizen = Citizen(
id = UUID.fromString(data["id"]),
name = CitizenI.Name(data["firstName"]!!, data["lastName"]!!),
email = data["email"] ?: "$username@dc-project.com",
birthday = DateTime.now(),
user = user
)
get<CitizenRepository>().insertWithUser(citizen)
}
Given("I am authenticated as {word} {word} with id {string}") { firstName: String, lastName: String, id: String ->
val jwtAsString: String = JWT.create()
.withIssuer("dc-project.fr")
.withClaim("id", id)
.sign(JwtConfig.algorithm)
val user = User(
id = UUID.fromString(id),
username = "$firstName-$lastName".toLowerCase(),
plainPassword = "azerty"
)
val citizen = Citizen(
id = UUID.fromString(id),
name = CitizenI.Name(firstName, lastName),
email = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr",
birthday = DateTime.now(),
user = user
)
try {
get<CitizenRepository>().insertWithUser(citizen)
} catch (e: CompletionException) {
// Nothing
}
KtorServerContext.defaultServer.addPreRequestSetup {
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
}
}
Given("I have citizen {word} {word}") { firstName: String, lastName: String ->
val id: UUID = UUID.randomUUID()
val user = User(
id = id,
username = "$firstName-$lastName".toLowerCase(),
plainPassword = "azerty"
)
val citizen = Citizen(
id = id,
name = CitizenI.Name(firstName, lastName),
email = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr",
birthday = DateTime.now(),
user = user
)
get<CitizenRepository>().insertWithUser(citizen)
}
Given("I am authenticated as {word} {word}") { firstName: String, lastName: String -> Given("I am authenticated as {word} {word}") { firstName: String, lastName: String ->
val username = "$firstName-$lastName".toLowerCase() val username = "$firstName-$lastName".toLowerCase()
val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Cititzen not exist with username $username") val citizen = get<CitizenRepository>().findByUsername(username) ?: error("Cititzen not exist with username $username")
@@ -99,28 +22,5 @@ class KtorServerAuthSteps : En, KoinTest {
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString") addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
} }
} }
Given("I have citizen {word} {word} with id {string}") { firstName: String, lastName: String, id: String ->
val user = User(
id = UUID.randomUUID(),
username = "$firstName-$lastName".toLowerCase(),
plainPassword = "azerty"
)
val citizen = Citizen(
id = UUID.fromString(id),
name = CitizenI.Name(firstName, lastName),
email = "$firstName-$lastName".toLowerCase() + "@gmail.com",
birthday = DateTime.now(),
user = user,
followAnonymous = false,
voteAnonymous = false
)
try {
get<CitizenRepository>().insertWithUser(citizen)
} catch (e: CompletionException) {
// Nothing
}
}
} }
} }

View File

@@ -1,11 +1,13 @@
package feature package feature
import fr.dcproject.entity.OpinionArticle import fr.dcproject.entity.OpinionArticle
import fr.dcproject.entity.OpinionChoice
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.*
import fr.dcproject.repository.Article as ArticleRepository import fr.dcproject.repository.Article as ArticleRepository
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.OpinionArticle as OpinionRepository import fr.dcproject.repository.OpinionArticle as OpinionRepository
@@ -13,33 +15,60 @@ import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
class OpinionSteps : En, KoinTest { class OpinionSteps : En, KoinTest {
init { init {
Given("I have the opinion {string} on article {string} created by {string}:") { opinionChoice: String, article: String, citizen: String, extraInfo: DataTable -> Given("I have an opinion choice {string}") { name: String ->
extraInfo.asMap<String, String>(String::class.java, String::class.java).let { val opinionChoice = OpinionChoice(
val opinion = OpinionArticle( name = name,
choice = get<OpinionChoiceRepository>().findOpinionsChoiceByName(opinionChoice) target = listOf()
?: error("Opinion Choice not exist"), )
target = get<ArticleRepository>().findById(article.toUUID()) ?: error("Article not exist"), get<OpinionRepository>().upsertOpinionChoice(opinionChoice)
createdBy = get<CitizenRepository>().findById(citizen.toUUID()) ?: error("Citizen not exist") }
)
get<OpinionRepository>().opinion(opinion) Given("I have an opinion choice {string} with ID {string}") { name: String, id: String ->
} val opinionChoice = OpinionChoice(
id = id.toUUID(),
name = name,
target = listOf()
)
get<OpinionRepository>().upsertOpinionChoice(opinionChoice)
}
Given("I have an opinion {string} on article {string} created by {word} {word}") { opinionChoiceName: String, articleId: String, firstName: String, lastName: String ->
createOpinion(opinionChoiceName, articleId, firstName, lastName)
}
Given("I have an opinion {string} on article {string} created by {word} {word} with ID {string}") { opinionChoiceName: String, articleId: String, firstName: String, lastName: String, id: String ->
createOpinion(opinionChoiceName, articleId, firstName, lastName, id)
} }
Given("I have an opinion") { extraInfo: DataTable -> Given("I have an opinion") { extraInfo: DataTable ->
extraInfo.asMap<String, String>(String::class.java, String::class.java)?.let { params -> createOpinionOnArticle(extraInfo)
val username = params["createdBy"]?.toLowerCase()?.replace(' ', '-')
?: error("You must provide the 'createdBy' parameter")
val opinion = OpinionArticle(
choice = params["opinion"]?.let {
get<OpinionChoiceRepository>().findOpinionsChoiceByName(it) ?: error("Opinion Choice not exist")
} ?: error("You must provide the 'opinion' parameter"),
target = params["article"]?.let {
get<ArticleRepository>().findById(it.toUUID()) ?: error("Article not exist")
} ?: error("You must provide the 'article' parameter"),
createdBy = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
)
get<OpinionRepository>().opinion(opinion)
}
} }
} }
private fun createOpinion(opinionChoiceName: String, articleId: String, firstName: String, lastName: String, id: String? = null) {
val opinion = OpinionArticle(
id = id?.toUUID() ?: UUID.randomUUID(),
choice = get<OpinionChoiceRepository>().findOpinionsChoiceByName(opinionChoiceName)
?: error("Opinion Choice not exist"),
target = get<ArticleRepository>().findById(articleId.toUUID()) ?: error("Article not exist"),
createdBy = get<CitizenRepository>().findByUsername("$firstName-$lastName".toLowerCase().replace(' ', '-')) ?: error("Citizen not exist")
)
get<OpinionRepository>().opinion(opinion)
}
private fun createOpinionOnArticle(extraInfo: DataTable? = null) {
val params = extraInfo?.asMap<String, String>(String::class.java, String::class.java)
val username = params?.get("createdBy")?.toLowerCase()?.replace(' ', '-')
?: error("You must provide the 'createdBy' parameter")
val opinion = OpinionArticle(
choice = params["opinion"]?.let {
get<OpinionChoiceRepository>().findOpinionsChoiceByName(it) ?: error("Opinion Choice not exist")
} ?: error("You must provide the 'opinion' parameter"),
target = params["article"]?.let {
get<ArticleRepository>().findById(it.toUUID()) ?: error("Article not exist")
} ?: error("You must provide the 'article' parameter"),
createdBy = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
)
get<OpinionRepository>().opinion(opinion)
}
} }

View File

@@ -0,0 +1,33 @@
package feature
import fr.dcproject.entity.Vote
import fr.dcproject.utils.toUUID
import io.cucumber.java8.En
import org.koin.test.KoinTest
import org.koin.test.get
import java.util.*
import fr.dcproject.repository.Article as ArticleRepository
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.VoteArticle as VoteRepository
class VoteSteps : En, KoinTest {
init {
Given("I have an vote {int} on article {string} created by {word} {word}") { note: Int, articleId: String, firstName: String, lastName: String ->
createVote(note, articleId, firstName, lastName)
}
Given("I have an vote {int} on article {string} created by {word} {word} with ID {string}") { note: Int, articleId: String, firstName: String, lastName: String, id: String ->
createVote(note, articleId, firstName, lastName, id)
}
}
private fun createVote(note: Int, articleId: String, firstName: String, lastName: String, id: String? = null) {
val vote = Vote(
id = id?.toUUID() ?: UUID.randomUUID(),
note = note,
target = get<ArticleRepository>().findById(articleId.toUUID()) ?: error("Article not exist"),
createdBy = get<CitizenRepository>().findByUsername("$firstName-$lastName".toLowerCase().replace(' ', '-')) ?: error("Citizen not exist")
)
get<VoteRepository>().vote(vote)
}
}

View File

@@ -7,10 +7,12 @@ import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.joda.time.DateTime import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class ArticleVoterTest { internal class ArticleVoterTest {
val tesla = CitizenBasic( val tesla = CitizenBasic(
user = User( user = User(
@@ -48,15 +50,13 @@ internal class ArticleVoterTest {
} }
@Test @Test
fun `other user can be view the article`() = ArticleVoter().run { fun `other user can be view the article`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla) val article = getArticle(tesla)
val article2 = getArticle(tesla)
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns einstein.user every { user } returns einstein.user
}.let { }.let {
supports(ArticleVoter.Action.VIEW, it, article) `should be` true can(ArticleVoter.Action.VIEW, it, article) `should be` true
vote(ArticleVoter.Action.VIEW, it, article) `should be` Vote.GRANTED
} }
} }
@@ -73,14 +73,13 @@ internal class ArticleVoterTest {
} }
@Test @Test
fun `the no creator can not be view the article on draft`() = ArticleVoter().run { fun `the no creator can not be view the article on draft`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla).apply { draft = true } val article = getArticle(tesla).apply { draft = true }
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns einstein.user every { user } returns einstein.user
}.let { }.let {
supports(ArticleVoter.Action.VIEW, it, article) `should be` true can(ArticleVoter.Action.VIEW, it, article) `should be` false
vote(ArticleVoter.Action.VIEW, it, article) `should be` Vote.DENIED
} }
} }
@@ -97,62 +96,79 @@ internal class ArticleVoterTest {
} }
@Test @Test
fun `can not view deleted article`() = ArticleVoter().run { fun `can not view deleted article`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla).apply { deletedAt = DateTime.now() } val article = getArticle(tesla).apply { deletedAt = DateTime.now() }
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
supports(ArticleVoter.Action.VIEW, it, article) `should be` true can(ArticleVoter.Action.VIEW, it, article) `should be` false
vote(ArticleVoter.Action.VIEW, it, article) `should be` Vote.DENIED
} }
} }
@Test @Test
fun `can delete article if owner`() = ArticleVoter().run { fun `can delete article if owner`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla) val article = getArticle(tesla)
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
supports(ArticleVoter.Action.DELETE, it, article) `should be` true can(ArticleVoter.Action.DELETE, it, article) `should be` true
vote(ArticleVoter.Action.DELETE, it, article) `should be` Vote.GRANTED
} }
} }
@Test @Test
fun `can not delete article if not owner`() = ArticleVoter().run { fun `can not delete article if not owner`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla).apply { deletedAt = DateTime.now() } val article = getArticle(tesla).apply { deletedAt = DateTime.now() }
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns einstein.user every { user } returns einstein.user
}.let { }.let {
supports(ArticleVoter.Action.DELETE, it, article) `should be` true can(ArticleVoter.Action.DELETE, it, article) `should be` false
vote(ArticleVoter.Action.DELETE, it, article) `should be` Vote.DENIED
} }
} }
@Test @Test
fun `can create article if logged`() = ArticleVoter().run { fun `can create article if logged`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla) val article = getArticle(tesla)
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
supports(ArticleVoter.Action.CREATE, it, article) `should be` true can(ArticleVoter.Action.CREATE, it, article) `should be` true
vote(ArticleVoter.Action.CREATE, it, article) `should be` Vote.GRANTED
} }
} }
@Test @Test
fun `can not create article if not logged`() = ArticleVoter().run { fun `can not create article if not logged`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla) val article = getArticle(tesla)
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns null every { user } returns null
}.let { }.let {
supports(ArticleVoter.Action.CREATE, it, article) `should be` true can(ArticleVoter.Action.CREATE, it, article) `should be` false
vote(ArticleVoter.Action.CREATE, it, article) `should be` Vote.DENIED }
}
@Test
fun `can update article if yours`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla)
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(ArticleVoter.Action.UPDATE, it, article) `should be` true
}
}
@Test
fun `can not update article if not yours`() = listOf(ArticleVoter()).run {
val article = getArticle(tesla)
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(ArticleVoter.Action.UPDATE, it, article) `should be` false
} }
} }

View File

@@ -0,0 +1,125 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class CitizenVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla")
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein")
)
private val curie = CitizenBasic(
user = User(
username = "marie-curie",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "curie@best.com",
name = CitizenI.Name("Marie", "Curie")
).apply { deletedAt = DateTime.now() }
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support citizen`() = CitizenVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(CitizenVoter.Action.VIEW, it, einstein) `should be` true
supports(p, it, einstein) `should be` false
}
}
@Test
fun `can be view the citizen`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CitizenVoter.Action.VIEW, it, einstein) `should be` true
}
}
@Test
fun `can be view the citizen list`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.VIEW, it, listOf(einstein, tesla)) `should be` true
}
}
@Test
fun `can not view deleted citizen`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CitizenVoter.Action.VIEW, it, curie) `should be` false
}
}
@Test
fun `can be update itself`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.UPDATE, it, einstein) `should be` true
}
}
@Test
fun `can not be update other citizen`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.UPDATE, it, tesla) `should be` false
}
}
@Test
fun `can be change password of itself`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.CHANGE_PASSWORD, it, einstein) `should be` true
}
}
@Test
fun `can not be change password of other citizen`() = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.CHANGE_PASSWORD, it, tesla) `should be` false
}
}
}

View File

@@ -0,0 +1,182 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class CommentVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla")
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein")
)
private val article1 = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
)
private val comment1 = Comment(
content = "Hello",
createdBy = tesla,
target = article1
)
private val commentTargetDeleted = Comment(
content = "Hello",
createdBy = tesla,
target = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
).apply { deletedAt = DateTime.now() }
)
private val commentTargetNoUser = Comment(
content = "Hello",
createdBy = tesla,
target = ArticleRef()
)
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support comment`() = CommentVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(CommentVoter.Action.VIEW, it, comment1) `should be` true
supports(CommentVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, comment1) `should be` false
}
}
@Test
fun `can be view the comment`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.VIEW, it, comment1) `should be` true
}
}
@Test
fun `can be view the comment list`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CommentVoter.Action.VIEW, it, listOf(comment1)) `should be` true
}
}
@Test
fun `can be update your comment`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.UPDATE, it, comment1) `should be` true
}
}
@Test
fun `can not be update other comment`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CommentVoter.Action.UPDATE, it, comment1) `should be` false
}
}
@Test
fun `can not be delete your comment`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.DELETE, it, comment1) `should be` false
}
}
@Test
fun `can be create a comment`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, comment1) `should be` true
}
}
@Test
fun `can not be create a comment if target is deleted`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, commentTargetDeleted) `should be` false
}
}
@Test
fun `can not be create a comment if target has no user`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, commentTargetNoUser) `should be` false
}
}
@Test
fun `can not be create a comment with other creator`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CommentVoter.Action.CREATE, it, comment1) `should be` false
}
}
@Test
fun `can not be create a comment if is null`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CommentVoter.Action.CREATE, it, null) `should be` false
}
}
@Test
fun `can not be create a comment if not connected`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(CommentVoter.Action.CREATE, it, comment1) `should be` false
}
}
}

View File

@@ -0,0 +1,145 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class FollowVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein"),
followAnonymous = true
)
private val article1 = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
)
private val follow1 = Follow(
createdBy = tesla,
target = article1
)
private val followAnon = Follow(
createdBy = einstein,
target = article1
)
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support follow`() = FollowVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(FollowVoter.Action.VIEW, it, follow1) `should be` true
supports(FollowVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, follow1) `should be` false
}
}
@Test
fun `can be view the follow`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(FollowVoter.Action.VIEW, it, follow1) `should be` true
}
}
@Test
fun `can be view the follow list`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(FollowVoter.Action.VIEW, it, listOf(follow1)) `should be` true
}
}
@Test
fun `can be view your anonymous follow`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(FollowVoter.Action.VIEW, it, followAnon) `should be` true
}
}
@Test
fun `can not be view the anonymous follow of other`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(FollowVoter.Action.VIEW, it, followAnon) `should be` false
}
}
@Test
fun `can be follow article`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(FollowVoter.Action.CREATE, it, follow1) `should be` true
}
}
@Test
fun `can not be follow article if not connected`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(FollowVoter.Action.CREATE, it, follow1) `should be` false
}
}
@Test
fun `can be unfollow article`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(FollowVoter.Action.DELETE, it, follow1) `should be` true
}
}
@Test
fun `can not be unfollow article if not connected`() = listOf(FollowVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(FollowVoter.Action.DELETE, it, follow1) `should be` false
}
}
}

View File

@@ -0,0 +1,75 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class OpinionChoiceVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false
)
private val article1 = Article(
content = "Hi",
createdBy = tesla,
description = "blablabla",
title = "Super article"
)
private val choice1 = OpinionChoice(
name = "Opinion1",
target = listOf()
)
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support opinion choice`() = OpinionChoiceVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(OpinionChoiceVoter.Action.VIEW, it, choice1) `should be` true
supports(OpinionChoiceVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, choice1) `should be` false
}
}
@Test
fun `can be view the opinion choice`() = listOf(OpinionChoiceVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionChoiceVoter.Action.VIEW, it, choice1) `should be` true
}
}
@Test
fun `can be view the opinion choice list`() = listOf(OpinionChoiceVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionChoiceVoter.Action.VIEW, it, listOf(choice1)) `should be` true
}
}
}

View File

@@ -0,0 +1,144 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class OpinionVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein"),
followAnonymous = true
)
private val article1 = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
)
private val opinion1 = Opinion(
createdBy = tesla,
target = article1,
choice = OpinionChoice(
name = "Opinion1",
target = listOf("article")
)
)
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support opinion`() = OpinionVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(OpinionVoter.Action.VIEW, it, opinion1) `should be` true
supports(OpinionVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, opinion1) `should be` false
}
}
@Test
fun `can be view the opinion`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionVoter.Action.VIEW, it, opinion1) `should be` true
}
}
@Test
fun `can be not view the opinion if is null`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionVoter.Action.VIEW, it, null) `should be` false
}
}
@Test
fun `can be view the opinion list`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionVoter.Action.VIEW, it, listOf(opinion1)) `should be` true
}
}
@Test
fun `can be opinion an article`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionVoter.Action.CREATE, it, opinion1) `should be` true
}
}
@Test
fun `can not be opinion if not connected`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(OpinionVoter.Action.CREATE, it, opinion1) `should be` false
}
}
@Test
fun `can be remove opinion`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(OpinionVoter.Action.DELETE, it, opinion1) `should be` true
}
}
@Test
fun `can not be remove opinion if not connected`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(OpinionVoter.Action.DELETE, it, opinion1) `should be` false
}
}
@Test
fun `can not be remove opinion of other user`() = listOf(OpinionVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(OpinionVoter.Action.DELETE, it, opinion1) `should be` false
}
}
}

View File

@@ -0,0 +1,168 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import fr.dcproject.entity.Vote as VoteEntity
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class VoteVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein"),
followAnonymous = true
)
private val article1 = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
)
private val vote1 = VoteEntity(
createdBy = tesla,
target = article1,
note = 1
)
private val voteOnDeleted = VoteEntity(
createdBy = tesla,
target = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
).apply { deletedAt = DateTime.now() },
note = 1
)
private val voteWithoutUser = VoteEntity(
createdBy = tesla,
target = ArticleRef(),
note = 1
)
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support vote`() = VoteVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(VoteVoter.Action.VIEW, it, vote1) `should be` true
supports(VoteVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, vote1) `should be` false
}
}
@Test
fun `can be view your the vote`() = listOf(VoteVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.VIEW, it, vote1) `should be` true
}
}
@Test
fun `can not be view vote of other`() = listOf(VoteVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(VoteVoter.Action.VIEW, it, vote1) `should be` false
}
}
@Test
fun `can be not view the vote if is null`() = listOf(VoteVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.VIEW, it, null) `should be` false
}
}
@Test
fun `can be view your votes list`() = listOf(VoteVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.VIEW, it, listOf(vote1)) `should be` true
}
}
@Test
fun `can be vote an article`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.CREATE, it, vote1) `should be` true
}
}
@Test
fun `can not be vote if not connected`() = listOf(VoteVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(VoteVoter.Action.CREATE, it, vote1) `should be` false
}
}
@Test
fun `can not be vote an article if article is deleted`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.CREATE, it, voteOnDeleted) `should be` false
}
}
@Test
fun `can not be vote an article if article have no user`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.CREATE, it, voteWithoutUser) `should be` false
}
}
@Test
fun `can not be comment an article if article is deleted`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, voteOnDeleted) `should be` false
}
}
}

View File

@@ -0,0 +1,204 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.*
import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.amshove.kluent.`should be`
import org.joda.time.DateTime
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import fr.dcproject.entity.Workgroup as WorkgroupEntity
@KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tag("voter")
internal class WorkgroupVoterTest {
private val tesla = CitizenBasic(
user = User(
username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false
)
private val einstein = CitizenBasic(
user = User(
username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
),
birthday = DateTime.now(),
email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein"),
followAnonymous = true
)
private val article1 = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
)
private val workgroupPublic = WorkgroupEntity(
createdBy = tesla,
description = "Super desc",
name = "super name",
owner = tesla,
anonymous = false
)
private val workgroupAnon = WorkgroupEntity(
createdBy = tesla,
description = "Super desc",
name = "super name",
owner = tesla,
anonymous = true
)
private val workgroupref = WorkgroupRef()
init {
mockkStatic("fr.dcproject.security.voter.VoterKt")
}
@Test
fun `support workgroup`() = WorkgroupVoter().run {
val p = object : ActionI {}
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
supports(WorkgroupVoter.Action.VIEW, it, workgroupPublic) `should be` true
supports(WorkgroupVoter.Action.VIEW, it, article1) `should be` false
supports(p, it, workgroupPublic) `should be` false
}
}
@Test
fun `can be view your workgroup`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, workgroupPublic) `should be` true
}
}
@Test
fun `can be view your workgroup if is not public`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, workgroupAnon) `should be` true
}
}
@Test
fun `can be view workgroup of other if is public`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be view workgroup of other if is not public`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, workgroupAnon) `should be` false
}
}
@Test
fun `can be not view the workgroup if is null`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, null) `should be` false
}
}
@Test
fun `can be view your workgroup list`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.VIEW, it, listOf(workgroupPublic)) `should be` true
}
}
@Test
fun `can be create workgroup`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.CREATE, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be create workgroup if not connected`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns null
}.let {
can(WorkgroupVoter.Action.CREATE, it, workgroupPublic) `should be` false
}
}
@Test
fun `can be delete workgroup if owner`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.DELETE, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be delete workgroup if not owner`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.DELETE, it, workgroupPublic) `should be` false
}
}
@Test
fun `can be update workgroup if owner`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be update workgroup if not owner`() = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupPublic) `should be` false
}
}
@Test
fun `can not be update workgroup if workgroup has no user`() {
Assertions.assertThrows(VoterException::class.java) {
listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupref)
}
}
}
}
}

View File

@@ -2,21 +2,27 @@
Feature: articles routes Feature: articles routes
Scenario: The route for get articles must response a 200 Scenario: The route for get articles must response a 200
Given I have 3 article
When I send a GET request to "/articles" When I send a GET request to "/articles"
Then the response status code should be 200 Then the response status code should be 200
Scenario: Can get versions of article by the id Scenario: Can get versions of article by the id
When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/versions" Given I have article
| id | 13e6091c-8fed-4600-b079-a97a6b7a9800 |
When I send a GET request to "/articles/13e6091c-8fed-4600-b079-a97a6b7a9800/versions"
Then the response status code should be 200 Then the response status code should be 200
Scenario: The route for get one article must response a 200 and return article Scenario: The route for get one article must response a 200 and return article
When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" Given I have article
| id | 65cda9f3-8991-4420-8d41-1da9da72c9bb |
When I send a GET request to "/articles/65cda9f3-8991-4420-8d41-1da9da72c9bb"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b | | id | 65cda9f3-8991-4420-8d41-1da9da72c9bb |
Scenario: The route for create article must response a 200 and return object Scenario: The route for create article must response a 200 and return object
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen John Doe
And I am authenticated as John Doe
When I send a POST request to "/articles" with body: When I send a POST request to "/articles" with body:
""" """
{ {
@@ -27,10 +33,7 @@ Feature: articles routes
"description": "description2", "description": "description2",
"tags": [ "tags": [
"green" "green"
], ]
"created_by": {
"id": "64b7b379-2298-43ec-b428-ba134930cabd"
}
} }
""" """
Then the response status code should be 200 Then the response status code should be 200

View File

@@ -1,3 +1,4 @@
@auth
Feature: Auth routes Feature: Auth routes
Scenario: The route for create citizen must response a 200 and return object Scenario: The route for create citizen must response a 200 and return object
@@ -34,10 +35,11 @@ Feature: Auth routes
Then the response status code should be 400 Then the response status code should be 400
Scenario: The route for create citizen must response a 200 and return object Scenario: The route for create citizen must response a 200 and return object
Given I have citizen Niels Bohr
When I send a POST request to "/login" with body: When I send a POST request to "/login" with body:
""" """
{ {
"name": "username-1", "name": "niels-bohr",
"password": "azerty" "password": "azerty"
} }
""" """

View File

@@ -1,31 +1,35 @@
@citizen
Feature: citizens routes Feature: citizens routes
Scenario: The route for get citizens must response a 200 Scenario: The route for get citizens must response a 200
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Jean Perrin with ID "5267a5c6-af42-4a02-aa2b-6b71d2e43973"
And I am authenticated as Jean Perrin
When I send a GET request to "/citizens" When I send a GET request to "/citizens"
Then the response status code should be 200 Then the response status code should be 200
Scenario: The route for get one citizen must response a 200 and return citizen Scenario: The route for get one citizen must response a 200 and return citizen
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Linus Pauling with ID "47a05c0f-7329-46c3-a7d0-325db37e9114"
When I send a GET request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff" Given I am authenticated as Linus Pauling
When I send a GET request to "/citizens/47a05c0f-7329-46c3-a7d0-325db37e9114"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| id | 6434f4f9-f570-f22a-c134-8668350651ff | | id | 47a05c0f-7329-46c3-a7d0-325db37e9114 |
Scenario: Can get connected citizen Scenario: Can get connected citizen
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Henri Becquerel with ID "47356809-c8ef-4649-8b99-1c5cb9886d38"
Given I am authenticated as Henri Becquerel
When I send a GET request to "/citizens/current" When I send a GET request to "/citizens/current"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| id | 64b7b379-2298-43ec-b428-ba134930cabd | | id | 47356809-c8ef-4649-8b99-1c5cb9886d38 |
@online @online
Scenario: Can be connect with SSO Scenario: Can be connect with SSO
Given I have citizen: Given I have citizen
| id | c606110c-ff0e-4d09-a79e-74632d7bf7bd | | id | c606110c-ff0e-4d09-a79e-74632d7bf7bd |
| firstName | John |
| lastName | Doe |
| email | fabrice.lecomte.be@gmail.com | | email | fabrice.lecomte.be@gmail.com |
| firstName | Leonhard |
| lastName | Euler |
When I send a POST request to "/sso" with body: When I send a POST request to "/sso" with body:
""" """
{ {
@@ -36,8 +40,9 @@ Feature: citizens routes
Then the response status code should be 204 Then the response status code should be 204
Scenario: Can be change my password Scenario: Can be change my password
Given I am authenticated as Joe Patate with id "c211dca6-aa21-45c2-95ba-c7f2179ee37e" Given I have citizen Georges Charpak with ID "0c966522-4071-43e5-a3ca-cfff2557f2cf"
When I send a PUT request to "/citizens/c211dca6-aa21-45c2-95ba-c7f2179ee37e/password/change" with body: And I am authenticated as Georges Charpak
When I send a PUT request to "/citizens/0c966522-4071-43e5-a3ca-cfff2557f2cf/password/change" with body:
""" """
{ {
"old_password": "azerty", "old_password": "azerty",
@@ -47,8 +52,10 @@ Feature: citizens routes
Then the response status code should be 201 Then the response status code should be 201
Scenario: If a send bad request when a change password, that return a 400 Bad request Scenario: If a send bad request when a change password, that return a 400 Bad request
Given I am authenticated as Joe Carotte with id "19110bb5-58a2-4ef1-9497-0207d4b4f48f"
When I send a PUT request to "/citizens/19110bb5-58a2-4ef1-9497-0207d4b4f48f/password/change" with body: Given I have citizen Louis Breguet with ID "6cf2a19d-d15d-4ee5-b2a9-907afd26b525"
And I am authenticated as Louis Breguet
When I send a PUT request to "/citizens/6cf2a19d-d15d-4ee5-b2a9-907afd26b525/password/change" with body:
""" """
{ {
"plup": "azerty", "plup": "azerty",

View File

@@ -1,7 +1,11 @@
@comment
Feature: comment Feature: comment
Scenario: Can comment childrens Scenario: Can comment childrens
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen John Dalton
And I have article with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I am authenticated as John Dalton
When I send a GET request to "/comments/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/children" And I have article
| id | 4c948e8f-eada-4e10-8d7d-7192affe1313 |
And I have comment created by John Dalton on article "4c948e8f-eada-4e10-8d7d-7192affe1313"
When I send a GET request to "/comments/4c948e8f-eada-4e10-8d7d-7192affe1313/children"
Then the response status code should be 200 Then the response status code should be 200

View File

@@ -1,9 +1,12 @@
@comment
Feature: comment Article Feature: comment Article
Scenario: Can comment an article Scenario: Can comment an article
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Michael Faraday
And I have article with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I am authenticated as Michael Faraday
When I send a POST request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/comments" with body: And I have article
| id | aa16c635-28da-46f0-9a89-934eef88c7ca |
When I send a POST request to "/articles/aa16c635-28da-46f0-9a89-934eef88c7ca/comments" with body:
""" """
{ {
"content": "Hello mister" "content": "Hello mister"
@@ -11,32 +14,52 @@ Feature: comment Article
""" """
Then the response status code should be 201 Then the response status code should be 201
# TODO add more comment on article
Scenario: Can get all comment on article Scenario: Can get all comment on article
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Enrico Fermi
And I have article with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I am authenticated as Enrico Fermi
When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/comments" And I have article
| id | 6166c078-ca97-4366-b0aa-2a5cd558c78a |
And I have comment created by Enrico Fermi on article "6166c078-ca97-4366-b0aa-2a5cd558c78a"
When I send a GET request to "/articles/6166c078-ca97-4366-b0aa-2a5cd558c78a/comments"
Then the response status code should be 200 Then the response status code should be 200
# TODO add votes
Scenario: Can get all comment on article sorted by votes Scenario: Can get all comment on article sorted by votes
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Pierre Curie
And I have article with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I am authenticated as Pierre Curie
When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/comments?sort=votes" And I have article
| id | 5e209f63-57ce-43ca-922a-273b0d62f567 |
And I have comment created by Enrico Fermi on article "5e209f63-57ce-43ca-922a-273b0d62f567"
When I send a GET request to "/articles/5e209f63-57ce-43ca-922a-273b0d62f567/comments?sort=votes"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| result[1].votes.up | 1 | | $.result[0].votes.up | 0 |
Scenario: Can get comments on articles of the current citizen Scenario: Can get comments on articles of the current citizen
Given I have citizen John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Erwin Schrodinger with ID "292a20cc-4a60-489e-9866-a95d38ffaf47"
And I have article with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I have article
When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/comments/articles" | id | 17df7fb9-b388-4e20-ab19-29c29972da01 |
| createdBy | Erwin Schrodinger |
And I have comment created by Erwin Schrodinger on article "17df7fb9-b388-4e20-ab19-29c29972da01"
When I send a GET request to "/citizens/292a20cc-4a60-489e-9866-a95d38ffaf47/comments/articles"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| current_page | 1 | | current_page | 1 |
| limit | 50 | | limit | 50 |
And the Response should contain:
"""
292a20cc-4a60-489e-9866-a95d38ffaf47
"""
Scenario: Can edit a comment Scenario: Can edit a comment
Given I am authenticated as username 3 with id "92877af7-0a45-fd6a-2ed7-fe81e1236b78" Given I have citizen Hubert Reeves
When I send a PUT request to "/comments/2f01c257-cf20-3466-fb10-a3b8eff12a97" with body: And I have article
| id | bb05e4a3-55a1-4088-85e7-8d8c23be29b1 |
And I am authenticated as Hubert Reeves
And I have comment created by Hubert Reeves on article "bb05e4a3-55a1-4088-85e7-8d8c23be29b1":
| id | fd30d20f-656c-42c6-8955-f61c04537464 |
When I send a PUT request to "/comments/fd30d20f-656c-42c6-8955-f61c04537464" with body:
""" """
Hello boy Hello boy
""" """
@@ -45,7 +68,13 @@ Feature: comment Article
| content | Hello boy | | content | Hello boy |
Scenario: Can get comment by its ID Scenario: Can get comment by its ID
When I send a GET request to "/comments/2f01c257-cf20-3466-fb10-a3b8eff12a97" Given I have citizen Alfred Kastler
And I have article
| id | 3897465b-19d2-43a0-86ea-1e29dbb11ec9 |
And I have comment created by Alfred Kastler on article "3897465b-19d2-43a0-86ea-1e29dbb11ec9":
| id | edd296a8-fc7a-4717-a2bb-9f035ceca3c2 |
| content | Hello boy |
When I send a GET request to "/comments/edd296a8-fc7a-4717-a2bb-9f035ceca3c2"
Then the response status code should be 200 Then the response status code should be 200
And the JSON should contain: And the JSON should contain:
| content | Hello boy | | content | Hello boy |

View File

@@ -1,35 +1,31 @@
@comment
Feature: comment Constitution Feature: comment Constitution
Scenario: Can comment an constitution Scenario: Can comment an constitution
Given I am authenticated as John Toe with id "a6eb1f5a-8c02-42f4-8e8e-a722f26841ef" Given I have citizen Nicolas Copernic
And I have constitution with id "d7e20f0b-3fdd-4638-817a-bbd87054eb82" created by "john-toe" And I am authenticated as Nicolas Copernic
When I send a POST request to "/constitutions/d7e20f0b-3fdd-4638-817a-bbd87054eb82/comments" with body: And I have constitution
| id | 1707c287-a472-4a62-89f2-9e85030e915c |
When I send a POST request to "/constitutions/1707c287-a472-4a62-89f2-9e85030e915c/comments" with body:
""" """
Hello mister {
"content": "Hello mister"
}
""" """
Then the response status code should be 201 Then the response status code should be 201
Scenario: Can get comments on constitution of the current citizen Scenario: Can get comments on constitutions of the current citizen
Given I have citizen John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Charles Darwin with ID "46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5"
And I have constitution with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I have constitution
When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/comments/constitutions" | id | 34ddd50a-da00-4a90-a869-08baa2a121be |
| createdBy | Charles Darwin |
And I have comment created by Charles Darwin on constitution "34ddd50a-da00-4a90-a869-08baa2a121be"
When I send a GET request to "/citizens/46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5/comments/constitutions"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| current_page | 1 | | current_page | 1 |
| limit | 50 | | limit | 50 |
And the Response should contain:
Scenario: Can edit a comment
Given I am authenticated as username 3 with id "92877af7-0a45-fd6a-2ed7-fe81e1236b78"
When I send a PUT request to "/comments/b0422e48-687f-bea7-b45f-b6b301246e97" with body:
""" """
Hello boy 34ddd50a-da00-4a90-a869-08baa2a121be
""" """
Then the response status code should be 200
And the JSON should contain:
| content | Hello boy |
Scenario: Can get comment by its ID
When I send a GET request to "/comments/b0422e48-687f-bea7-b45f-b6b301246e97"
Then the response status code should be 200
And the JSON should contain:
| content | Hello boy |

View File

@@ -1,17 +1,21 @@
Feature: constitution routes @constitution
Feature: constitution
Scenario: The route for get constitutions must response a 200 Scenario: The route for get constitutions must response a 200
Given I have 3 constitution
When I send a GET request to "/constitutions" When I send a GET request to "/constitutions"
Then the response status code should be 200 Then the response status code should be 200
Scenario: The route for get one constitution must response a 200 and return constitution Scenario: The route for get one constitution must response a 200 and return constitution
When I send a GET request to "/constitutions/0ca489a6-ef68-8bd5-2355-5793d4b3d66c" Given I have constitution with ID "0321c8d1-4ce3-4763-b5f4-a92611d280b4"
When I send a GET request to "/constitutions/0321c8d1-4ce3-4763-b5f4-a92611d280b4"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| id | 0ca489a6-ef68-8bd5-2355-5793d4b3d66c | | id | 0321c8d1-4ce3-4763-b5f4-a92611d280b4 |
Scenario: The route for create constitution must response a 200 and return object Scenario: The route for create constitution must response a 200 and return object
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Henri Poincaré
And I am authenticated as Henri Poincaré
When I send a POST request to "/constitutions" with body: When I send a POST request to "/constitutions" with body:
""" """
{ {
@@ -20,19 +24,10 @@ Feature: constitution routes
"annonymous":true, "annonymous":true,
"titles":[ "titles":[
{ {
"id":"8156b66f-a9c8-4fd9-8375-a8a1f42ccfd2",
"name":"plop", "name":"plop",
"rank":0, "rank":0
"created_by":{
"id":"64b7b379-2298-43ec-b428-ba134930cabd"
}
} }
], ]
"created_by":{
"id":"64b7b379-2298-43ec-b428-ba134930cabd"
},
"created_at":null,
"version_number":null
} }
""" """
Then the response status code should be 200 Then the response status code should be 200

View File

@@ -1,50 +0,0 @@
@follow
Feature: follow Article and Constitution
# Article
Scenario: The route for follow article must response a 201 and return object
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
When I send a POST request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follows"
Then the response status code should be 201
Scenario: The route for get follows of articles must response a 200 and return objects
Given I have citizen John Smith with id "e3c0b08c-11be-418e-95e0-8596b4402feb"
When I send a GET request to "/citizens/e3c0b08c-11be-418e-95e0-8596b4402feb/follows/articles"
Then the response status code should be 200
And the response should contain object:
| current_page | 1 |
| limit | 50 |
Scenario: The route for unfollow article must response a 204
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
When I send a DELETE request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follows"
Then the response status code should be 204
# Constitution
Scenario: The route for follow constitution must response a 201 and return object
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
When I send a POST request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow"
Then the response status code should be 201
Scenario: The route for get follows of constitutions must response a 200 and return objects
Given I have citizen John Smith with id "e3c0b08c-11be-418e-95e0-8596b4402feb"
When I send a GET request to "/citizens/e3c0b08c-11be-418e-95e0-8596b4402feb/follows/constitutions"
Then the response status code should be 200
And the response should contain object:
| current_page | 1 |
| limit | 50 |
Scenario: The route for unfollow constitution must response a 204
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
When I send a DELETE request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow"
Then the response status code should be 204
Scenario: I can know if I follow an article
Given I have article with id "3ee4e6d0-f312-4940-872d-1f578c8d824c"
And I have citizen Marie Curie
And I am authenticated as Marie Curie
And The citizen Marie Curie follow article "3ee4e6d0-f312-4940-872d-1f578c8d824c"
When I send a GET request to "/articles/3ee4e6d0-f312-4940-872d-1f578c8d824c/follows"
Then the response status code should be 200
And the JSON should contain:
| target.id | 3ee4e6d0-f312-4940-872d-1f578c8d824c |

View File

@@ -0,0 +1,42 @@
@follow
Feature: follow Article
# Article
Scenario: The route for follow article must response a 201 and return
Given I have citizen Louis Pasteur
And I am authenticated as Louis Pasteur
And I have article
| id | 04754b7b-edef-4adc-af81-75e3aadeebea |
When I send a POST request to "/articles/04754b7b-edef-4adc-af81-75e3aadeebea/follows"
Then the response status code should be 201
Scenario: The route for get follows of articles must response a 200 and return objects
Given I have citizen Johannes Kepler with ID "e4592d53-3660-4264-8353-ebdbf5d9c41c"
And I am authenticated as Johannes Kepler
And I have article
| id | d743619a-1f6b-4d20-a2d6-8e81f0e6a4c8 |
And I have follow of Johannes Kepler on article "d743619a-1f6b-4d20-a2d6-8e81f0e6a4c8"
When I send a GET request to "/citizens/e4592d53-3660-4264-8353-ebdbf5d9c41c/follows/articles"
Then the response status code should be 200
And the response should contain object:
| current_page | 1 |
| limit | 50 |
Scenario: The route for unfollow article must response a 204
Given I have citizen Thomas Edison
And I am authenticated as Thomas Edison
And I have article
| id | aad3aa9d-95fd-4919-9e84-46255f620e31 |
And I have follow of Thomas Edison on article "aad3aa9d-95fd-4919-9e84-46255f620e31"
When I send a DELETE request to "/articles/aad3aa9d-95fd-4919-9e84-46255f620e31/follows"
Then the response status code should be 204
Scenario: I can know if I follow an article
Given I have article with ID "3ee4e6d0-f312-4940-872d-1f578c8d824c"
And I have citizen Marie Curie
And I am authenticated as Marie Curie
And I have follow of Marie Curie on article "3ee4e6d0-f312-4940-872d-1f578c8d824c"
When I send a GET request to "/articles/3ee4e6d0-f312-4940-872d-1f578c8d824c/follows"
Then the response status code should be 200
And the JSON should contain:
| target.id | 3ee4e6d0-f312-4940-872d-1f578c8d824c |

View File

@@ -0,0 +1,42 @@
@follow
Feature: follow Constitution
# Constitution
Scenario: The route for follow constitution must response a 201 and return
Given I have citizen Dmitri Mendeleïev
And I am authenticated as Dmitri Mendeleïev
And I have constitution
| id | f6553f5e-0cf5-476a-b84e-15e52ec9d8f9 |
When I send a POST request to "/constitutions/f6553f5e-0cf5-476a-b84e-15e52ec9d8f9/follows"
Then the response status code should be 201
Scenario: The route for get follows of constitutions must response a 200 and return objects
Given I have citizen André-Marie Ampère with ID "877b45b0-302d-487d-8944-6d03ccdbd0f8"
And I am authenticated as André-Marie Ampère
And I have constitution
| id | 7f642078-8e74-47fc-9712-0c37531674a0 |
And I have follow of André-Marie Ampère on constitution "7f642078-8e74-47fc-9712-0c37531674a0"
When I send a GET request to "/citizens/877b45b0-302d-487d-8944-6d03ccdbd0f8/follows/constitutions"
Then the response status code should be 200
And the response should contain object:
| current_page | 1 |
| limit | 50 |
Scenario: The route for unfollow constitution must response a 204
Given I have citizen Claude Ptolémée
And I am authenticated as Claude Ptolémée
And I have constitution
| id | 4ea03776-a28c-4202-9b90-db1b942946c1 |
And I have follow of Claude Ptolémée on constitution "4ea03776-a28c-4202-9b90-db1b942946c1"
When I send a DELETE request to "/constitutions/4ea03776-a28c-4202-9b90-db1b942946c1/follows"
Then the response status code should be 204
Scenario: I can know if I follow an constitution
Given I have constitution with ID "5141e781-ebcd-4de0-8c2b-7d23d4cd58b5"
And I have citizen Denis Papin
And I am authenticated as Denis Papin
And I have follow of Denis Papin on constitution "5141e781-ebcd-4de0-8c2b-7d23d4cd58b5"
When I send a GET request to "/constitutions/5141e781-ebcd-4de0-8c2b-7d23d4cd58b5/follows"
Then the response status code should be 200
And the JSON should contain:
| target.id | 5141e781-ebcd-4de0-8c2b-7d23d4cd58b5 |

View File

@@ -1,63 +1,73 @@
@opinion @opinion
Feature: Opinion Feature: Opinion
Scenario: Can get one opinion Choices
When I send a GET request to "/opinions/6e978eb5-3c48-0def-b093-e01f43983adb"
Then the response status code should be 200
And the JSON should contain:
| name | Opinion1 |
Scenario: Can get all opinion choices Scenario: Can get all opinion choices
Given I have an opinion choice "Opinion1"
And I have an opinion choice "Opinion2"
When I send a GET request to "/opinions" When I send a GET request to "/opinions"
Then the response status code should be 200 Then the response status code should be 200
And the JSON should contain: And the JSON should contain:
| [0]name | Opinion1 | | [0]name | Opinion1 |
| [1]name | Opinion2 |
Scenario: Can get one opinion Choices
Given I have an opinion choice "Opinion3" with ID "347ec243-0e76-4ab5-9884-7bd503cf5ab5"
When I send a GET request to "/opinions/347ec243-0e76-4ab5-9884-7bd503cf5ab5"
Then the response status code should be 200
And the JSON should contain:
| name | Opinion3 |
Scenario: Can create opinion on article Scenario: Can create opinion on article
Given I have citizen: Given I have citizen Isaac Newton with ID "2f414045-95d9-42ca-a3a9-8cdde52ad253"
| id | 2f414045-95d9-42ca-a3a9-8cdde52ad253 |
| firstName | Isaac |
| lastName | Newton |
And I am authenticated as Isaac Newton And I am authenticated as Isaac Newton
And I have article And I have article
| id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7 | | id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b |
| createdBy | Isaac Newton |
And I have an opinion
| opinion | Opinion1 |
| article | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b |
| createdBy | Isaac Newton | | createdBy | Isaac Newton |
And I have an opinion choice "Opinion4" with ID "0f4f1721-3136-44f1-9f31-1459f3317b15"
And I have an opinion "Opinion4" on article "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" created by Isaac Newton with ID "74e93e12-556b-4399-95a6-04f93a4dd66c"
When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions" with body: When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions" with body:
""" """
{ {
"opinion_choice": "6e978eb5-3c48-0def-b093-e01f43983adb" "opinion_choice": "0f4f1721-3136-44f1-9f31-1459f3317b15"
} }
""" """
Then the response status code should be 201 Then the response status code should be 201
Scenario: Can I get all opinions of citizen filtered by target ids Scenario: Can I get all opinions of citizen filtered by target ids
When I send a GET request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff/opinions?id=9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" Given I have article with ID "4eb8a2c6-ba63-4c1e-919f-72017132a54e"
And I have citizen Albert Jacquard with ID "c6392fc4-56f5-461b-8015-953a4da3029f"
And I am authenticated as Albert Jacquard
And I have an opinion choice "Opinion5" with ID "74d6e105-9aa6-4589-8776-82ce260bb6f4"
And I have an opinion "Opinion5" on article "4eb8a2c6-ba63-4c1e-919f-72017132a54e" created by Albert Jacquard with ID "994660e7-e9f4-4ae9-9290-b34d78663c7a"
When I send a GET request to "/citizens/c6392fc4-56f5-461b-8015-953a4da3029f/opinions?id=4eb8a2c6-ba63-4c1e-919f-72017132a54e"
Then the response status code should be 200 Then the response status code should be 200
And the JSON should contain: And the JSON should contain:
| [0].name | Opinion2 | | [0].name | Opinion5 |
Scenario: Can recieve opinion aggregation with article Scenario: Can recieve opinion aggregation with article
When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" Given I have an opinion choice "Opinion6"
And I have an opinion choice "Opinion7"
And I have an opinion choice "Opinion8"
And I have citizen James Watt
And I have citizen Paul Langevin
And I have article with ID "bda8940a-6792-4f2b-936a-ba5c805c8487"
And I have an opinion "Opinion6" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by James Watt
And I have an opinion "Opinion7" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by James Watt
And I have an opinion "Opinion7" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by Paul Langevin
When I send a GET request to "/articles/bda8940a-6792-4f2b-936a-ba5c805c8487"
Then the response status code should be 200 Then the response status code should be 200
And the JSON should contain: And the JSON should contain:
| opinions.Opinion2 | 1 | | opinions.Opinion6 | 1 |
| opinions.Opinion7 | 2 |
Scenario: Can get all opinion of one citizen Scenario: Can get all opinion of one citizen
Given I have citizen: Given I have citizen Albert Einstein with ID "c1542096-3431-432d-8e35-9dc071d4c818"
| id | c1542096-3431-432d-8e35-9dc071d4c818 |
| firstName | Albert |
| lastName | Einstein |
And I am authenticated as Albert Einstein And I am authenticated as Albert Einstein
And I have an opinion And I have an opinion choice "Opinion9"
| opinion | Opinion1 | And I have article with ID "8651b530-ac1b-4214-a784-706781371074"
| article | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b | And I have an opinion "Opinion9" on article "8651b530-ac1b-4214-a784-706781371074" created by Albert Einstein
| createdBy | Albert Einstein |
When I send a GET request to "/citizens/c1542096-3431-432d-8e35-9dc071d4c818/opinions/articles" When I send a GET request to "/citizens/c1542096-3431-432d-8e35-9dc071d4c818/opinions/articles"
Then the response status code should be 200 Then the response status code should be 200
And the JSON element result should have 1 items And the JSON element result should have 1 items
And the JSON should contain: And the JSON should contain:
| result[0].name | Opinion1 | | result[0].name | Opinion9 |

View File

@@ -1,8 +1,11 @@
@vote
Feature: vote Article Feature: vote Article
Scenario: Can Vote article Scenario: Can Vote article
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Thalès Milet
When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/vote" with body: And I am authenticated as Thalès Milet
And I have article with ID "835c5101-ca39-4038-a4e6-da6ee62ca6d5"
When I send a PUT request to "/articles/835c5101-ca39-4038-a4e6-da6ee62ca6d5/vote" with body:
""" """
{ {
"note": 1 "note": 1
@@ -11,8 +14,10 @@ Feature: vote Article
Then the response status code should be 201 Then the response status code should be 201
Scenario: Can Vote constitution Scenario: Can Vote constitution
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Gregor Mendel
When I send a PUT request to "/constitutions/64b1f265-bfb3-332b-eef9-d00f63a3beaa/vote" with body: And I am authenticated as Gregor Mendel
And I have constitution with ID "76e79c89-efc1-492d-9e8f-dc9717363a11"
When I send a PUT request to "/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote" with body:
""" """
{ {
"note": -1 "note": -1
@@ -21,8 +26,11 @@ Feature: vote Article
Then the response status code should be 201 Then the response status code should be 201
Scenario: Can get votes of current citizen Scenario: Can get votes of current citizen
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Carl Gauss with ID "c044823d-e778-4256-9016-b1334bf933d3"
When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/votes/articles" And I am authenticated as Carl Gauss
And I have article with ID "7c9286db-470d-448c-aab1-3f0b072213b1"
And I have an vote 1 on article "7c9286db-470d-448c-aab1-3f0b072213b1" created by Carl Gauss
When I send a GET request to "/citizens/c044823d-e778-4256-9016-b1334bf933d3/votes/articles"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| current_page | 1 | | current_page | 1 |
@@ -31,16 +39,25 @@ Feature: vote Article
| result[0].note | 1 | | result[0].note | 1 |
Scenario: Can get votes of current citizen by target ids Scenario: Can get votes of current citizen by target ids
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Rosalind Franklin with ID "ab3269f0-877b-46b2-ae1a-e7e7d1c12132"
When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/votes?id=9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b&id=64b1f265-bfb3-332b-eef9-d00f63a3beaa" And I am authenticated as Rosalind Franklin
And I have article with ID "4d457f53-b937-4622-9542-d5f689d3716b"
And I have an vote 1 on article "4d457f53-b937-4622-9542-d5f689d3716b" created by Rosalind Franklin
And I have article with ID "117ef3e6-a740-4d04-9a4a-a800a5f274b4"
And I have an vote -1 on article "117ef3e6-a740-4d04-9a4a-a800a5f274b4" created by Rosalind Franklin
When I send a GET request to "/citizens/ab3269f0-877b-46b2-ae1a-e7e7d1c12132/votes?id=4d457f53-b937-4622-9542-d5f689d3716b&id=117ef3e6-a740-4d04-9a4a-a800a5f274b4"
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| [0].note | 1 | | [0].note | -1 |
| [1].note | 1 |
Scenario: Can vote a comment Scenario: Can vote a comment
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I have citizen Antoine Lavoisier
And I have comment "ea5c9e87-c99e-4646-a381-2910219e077f" on article "cc9c624e-a27e-42de-af78-ae821c657a68" And I am authenticated as Antoine Lavoisier
When I send a PUT request to "/comments/ea5c9e87-c99e-4646-a381-2910219e077f/vote" with body: And I have article with ID "54428366-e71e-4961-876c-8a13df5e4b41"
And I have comment created by Antoine Lavoisier on article "54428366-e71e-4961-876c-8a13df5e4b41":
| id | e793eccc-456b-4450-a292-46d592229b74 |
When I send a PUT request to "/comments/e793eccc-456b-4450-a292-46d592229b74/vote" with body:
""" """
{ {
"note": -1 "note": -1

View File

@@ -45,8 +45,8 @@ Feature: Workgroup
Scenario: Can add member to workgroup Scenario: Can add member to workgroup
Given I have citizen Blaise Pascal Given I have citizen Blaise Pascal
And I have citizen Roger Penrose with id "6d883fe7-5fc0-4a50-8858-72230673eba4" And I have citizen Roger Penrose with ID "6d883fe7-5fc0-4a50-8858-72230673eba4"
And I have citizen Alessandro Volta with id "b5bac515-45d4-4aeb-9b6d-2627a0bbc419" And I have citizen Alessandro Volta with ID "b5bac515-45d4-4aeb-9b6d-2627a0bbc419"
And I am authenticated as Blaise Pascal And I am authenticated as Blaise Pascal
And I have workgroup: And I have workgroup:
| id | b0ea1922-3bc6-44e2-aa7c-40158998cfbb | | id | b0ea1922-3bc6-44e2-aa7c-40158998cfbb |
@@ -63,8 +63,8 @@ Feature: Workgroup
Scenario: Can remove member to workgroup Scenario: Can remove member to workgroup
Given I have citizen Heinrich Hertz Given I have citizen Heinrich Hertz
And I have citizen William Thomson with id "87909ba3-2069-431c-9924-219fd8411cf2" And I have citizen William Thomson with ID "87909ba3-2069-431c-9924-219fd8411cf2"
And I have citizen Paul Dirac with id "1baf48bb-02bc-4d8f-ac86-33335354f5e7" And I have citizen Paul Dirac with ID "1baf48bb-02bc-4d8f-ac86-33335354f5e7"
And I am authenticated as Heinrich Hertz And I am authenticated as Heinrich Hertz
And I have workgroup: And I have workgroup:
| id | b6c975df-dd44-4e99-adc1-f605746b0e11 | | id | b6c975df-dd44-4e99-adc1-f605746b0e11 |
@@ -85,15 +85,15 @@ Feature: Workgroup
And the JSON should have 1 items And the JSON should have 1 items
Scenario: Can update members on workgroup Scenario: Can update members on workgroup
Given I have citizen John Dalton Given I have citizen Leon Foucault
And I have citizen Sadi Carnot with id "be3b0926-8628-4426-804a-75188a6eb315" And I have citizen Sadi Carnot with ID "be3b0926-8628-4426-804a-75188a6eb315"
And I have citizen Joseph Fourier with id "d9671eca-abaf-4b67-9230-3ece700c1ddb" And I have citizen Joseph Fourier with ID "d9671eca-abaf-4b67-9230-3ece700c1ddb"
And I have citizen Georg Ohm with id "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1" And I have citizen Georg Ohm with ID "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"
And I am authenticated as John Dalton And I am authenticated as Leon Foucault
And I have workgroup: And I have workgroup:
| id | 784fe6bc-7635-4ae2-b080-3a4743b998bf | | id | 784fe6bc-7635-4ae2-b080-3a4743b998bf |
| name | Les Tacos | | name | Les Tacos |
| owner | John Dalton | | owner | Leon Foucault |
And I have members in workgroup "784fe6bc-7635-4ae2-b080-3a4743b998bf": And I have members in workgroup "784fe6bc-7635-4ae2-b080-3a4743b998bf":
| be3b0926-8628-4426-804a-75188a6eb315 | | be3b0926-8628-4426-804a-75188a6eb315 |
| d9671eca-abaf-4b67-9230-3ece700c1ddb | | d9671eca-abaf-4b67-9230-3ece700c1ddb |

View File

@@ -58,15 +58,10 @@ begin
-- upsert article -- upsert article
select upsert_article(created_article) into created_article; select upsert_article(created_article) into created_article;
select (h->>'id')::uuid into opinion_choice1_id from upsert_opinion_choice('{"name": "Opinion1", "target":["article"]}') h;
insert into opinion_choice(id, name, target) assert opinion_choice1_id is not null, 'Opinion choice must be return json with id';
values (opinion_choice1_id, 'Opinion1', '{article}'); select (h->>'id')::uuid into opinion_choice2_id from upsert_opinion_choice('{"name": "Opinion2"}') h;
perform upsert_opinion_choice('{"name": "Opinion3", "target":["article"]}') h;
insert into opinion_choice(id, name)
values (opinion_choice2_id, 'Opinion2');
insert into opinion_choice(name, target)
values ('Opinion3', '{article}');
perform upsert_opinion( perform upsert_opinion(
resource => json_build_object( resource => json_build_object(