From 9c88adbabd44cfb4788a1a93237ca2fc718ae142 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Fri, 12 Mar 2021 23:32:32 +0100 Subject: [PATCH] Test openapi schema response of FindArticlesVersion,GetOneArticle,UpsertArticle change snack_case to camelCase --- .../fr/dcproject/application/Application.kt | 4 +- .../component/article/database/Article.kt | 11 +- .../article/database/ArticleRepository.kt | 4 +- .../article/routes/FindArticleVersions.kt | 31 +- .../component/article/routes/GetOneArticle.kt | 79 +++-- .../component/article/routes/UpsertArticle.kt | 14 +- src/main/resources/openapi2.yaml | 302 +++++++++++++++--- .../find_articles_versions_by_version_id.sql | 15 +- .../functional/NotificationConsumerTest.kt | 7 +- .../functional/NotificationsPushTest.kt | 10 +- src/test/kotlin/functional/ViewTest.kt | 11 +- src/test/kotlin/integration/Article routes.kt | 13 +- .../unit/security/Article Access Control.kt | 47 +-- .../unit/security/Comment Access Control.kt | 11 +- .../unit/security/Follow Access Control.kt | 9 +- .../unit/security/Opinion Access Control.kt | 11 +- .../unit/security/Vote Access Control.kt | 9 +- 17 files changed, 432 insertions(+), 156 deletions(-) diff --git a/src/main/kotlin/fr/dcproject/application/Application.kt b/src/main/kotlin/fr/dcproject/application/Application.kt index 6e4bf0e..eb55b1d 100644 --- a/src/main/kotlin/fr/dcproject/application/Application.kt +++ b/src/main/kotlin/fr/dcproject/application/Application.kt @@ -138,11 +138,11 @@ fun Application.module(env: Env = PROD) { install(ContentNegotiation) { jackson { - propertyNamingStrategy = PropertyNamingStrategies.SNAKE_CASE + propertyNamingStrategy = PropertyNamingStrategies.LOWER_CAMEL_CASE registerModule(JodaModule()) disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) configure(SerializationFeature.INDENT_OUTPUT, true) setDefaultPrettyPrinter( DefaultPrettyPrinter().apply { diff --git a/src/main/kotlin/fr/dcproject/component/article/database/Article.kt b/src/main/kotlin/fr/dcproject/component/article/database/Article.kt index 408f0c7..4fda505 100644 --- a/src/main/kotlin/fr/dcproject/component/article/database/Article.kt +++ b/src/main/kotlin/fr/dcproject/component/article/database/Article.kt @@ -29,7 +29,7 @@ data class ArticleForView( val content: String, val description: String, val tags: List = emptyList(), - override val createdBy: CitizenRef, + override val createdBy: CitizenCreator, override val versionNumber: Int = 0, override val versionId: UUID = UUID.randomUUID(), val workgroup: WorkgroupCart? = null, @@ -37,7 +37,7 @@ data class ArticleForView( override val draft: Boolean = false, override val deletedAt: DateTime? = null ) : ArticleRef(id), - ArticleAuthI, + ArticleAuthI, ArticleWithTitleI, Versionable, CreatedAt by CreatedAt.Imp(), @@ -79,9 +79,10 @@ class ArticleForListing( id: UUID? = null, override val title: String, override val createdBy: CitizenCreator, - override val workgroup: WorkgroupCart?, - override val deletedAt: DateTime?, - override val draft: Boolean + override val workgroup: WorkgroupCart? = null, + override val deletedAt: DateTime? = null, + override val draft: Boolean = false, + val lastVersion: Boolean = false ) : ArticleForListingI, ArticleRef(id), ArticleAuthI, diff --git a/src/main/kotlin/fr/dcproject/component/article/database/ArticleRepository.kt b/src/main/kotlin/fr/dcproject/component/article/database/ArticleRepository.kt index 316897c..37d54eb 100644 --- a/src/main/kotlin/fr/dcproject/component/article/database/ArticleRepository.kt +++ b/src/main/kotlin/fr/dcproject/component/article/database/ArticleRepository.kt @@ -13,13 +13,13 @@ class ArticleRepository(override var requester: Requester) : RepositoryI { return function.selectOne("id" to id) } - fun findVersionsById(page: Int = 1, limit: Int = 50, id: UUID): Paginated { + fun findVersionsById(page: Int = 1, limit: Int = 50, id: UUID): Paginated { return requester .getFunction("find_articles_versions_by_id") .select(page, limit, "id" to id) } - fun findVersionsByVersionId(page: Int = 1, limit: Int = 50, versionId: UUID): Paginated { + fun findVersionsByVersionId(page: Int = 1, limit: Int = 50, versionId: UUID): Paginated { return requester .getFunction("find_articles_versions_by_version_id") .select(page, limit, "version_id" to versionId) diff --git a/src/main/kotlin/fr/dcproject/component/article/routes/FindArticleVersions.kt b/src/main/kotlin/fr/dcproject/component/article/routes/FindArticleVersions.kt index 611f612..0667aba 100644 --- a/src/main/kotlin/fr/dcproject/component/article/routes/FindArticleVersions.kt +++ b/src/main/kotlin/fr/dcproject/component/article/routes/FindArticleVersions.kt @@ -1,7 +1,9 @@ package fr.dcproject.component.article.routes +import fr.dcproject.common.dto.toOutput import fr.dcproject.common.security.assert import fr.dcproject.component.article.ArticleAccessControl +import fr.dcproject.component.article.database.ArticleForListing import fr.dcproject.component.article.database.ArticleRef import fr.dcproject.component.article.database.ArticleRepository import fr.dcproject.component.auth.citizenOrNull @@ -37,7 +39,34 @@ object FindArticleVersions { get { repo.findVersions(it) .apply { ac.assert { canView(result, citizenOrNull) } } - .let { call.respond(it) } + .run { + call.respond( + toOutput { a: ArticleForListing -> + object { + val id = a.id + val title = a.title + val createdBy = object { + val id = a.createdBy.id + val name = a.createdBy.name.let { n -> + object { + val firstName = n.firstName + val lastName = n.lastName + } + } + val email = a.createdBy.email + } + val workgroup = a.workgroup?.let { w -> + object { + val id = w.id + val name = w.name + } + } + val draft = a.draft + val lastVersion = a.lastVersion + } + } + ) + } } } } diff --git a/src/main/kotlin/fr/dcproject/component/article/routes/GetOneArticle.kt b/src/main/kotlin/fr/dcproject/component/article/routes/GetOneArticle.kt index 78f8d3d..41b4e58 100644 --- a/src/main/kotlin/fr/dcproject/component/article/routes/GetOneArticle.kt +++ b/src/main/kotlin/fr/dcproject/component/article/routes/GetOneArticle.kt @@ -1,7 +1,5 @@ package fr.dcproject.component.article.routes -import fr.dcproject.common.dto.CreatedAt -import fr.dcproject.common.dto.Versionable import fr.dcproject.common.security.assert import fr.dcproject.component.article.ArticleAccessControl import fr.dcproject.component.article.ArticleViewManager @@ -9,10 +7,6 @@ import fr.dcproject.component.article.database.ArticleForView import fr.dcproject.component.article.database.ArticleRef import fr.dcproject.component.article.database.ArticleRepository import fr.dcproject.component.auth.citizenOrNull -import fr.dcproject.component.opinion.dto.Opinionable -import fr.dcproject.component.views.dto.Viewable -import fr.dcproject.component.views.entity.ViewAggregation -import fr.dcproject.component.vote.dto.Votable import io.ktor.application.call import io.ktor.features.NotFoundException import io.ktor.locations.KtorExperimentalLocationsAPI @@ -30,39 +24,56 @@ object GetOneArticle { val article = ArticleRef(article) } - class Output( - article: ArticleForView, - views: ViewAggregation = ViewAggregation() - ) : CreatedAt by CreatedAt.Imp(article), - Opinionable by Opinionable.Imp(article), - Votable by Votable.Imp(article), - Versionable by Versionable.Imp(article), - Viewable by Viewable.Imp(views) { - val id = article.id - val title = article.title - val anonymous = article.anonymous - val content = article.content - val description = article.description - val tags = article.tags - val draft = article.draft - val lastVersion = article.lastVersion - val createdBy = article.createdBy - val workgroup = article.workgroup?.let { Workgroup(article.workgroup.id, article.workgroup.name) } - - class Workgroup(val id: UUID, val name: String) - } - fun Route.getOneArticle(viewManager: ArticleViewManager, ac: ArticleAccessControl, repo: ArticleRepository) { get { val article: ArticleForView = repo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found") ac.assert { canView(article, citizenOrNull) } - Output( - article, - viewManager.getViewsCount(article) - ).also { out -> - call.respond(out) - } + call.respond( + article.let { a -> + object { + val id = a.id + val versionId = a.versionId + val versionNumber = a.versionNumber + val title = a.title + val anonymous = a.anonymous + val content = a.content + val description = a.description + val tags = a.tags + val draft = a.draft + val lastVersion = a.lastVersion + val createdAt = a.createdAt + val createdBy: Any = object { + val id: UUID = a.createdBy.id + val name: Any = object { + val firstName: String = a.createdBy.name.firstName + val lastName: String = a.createdBy.name.lastName + } + val email: String = a.createdBy.email + } + val workgroup: Any? = a.workgroup?.let { w -> + object { + val id: UUID = w.id + val name: String = w.name + } + } + val votes: Any = object { + val up: Int = a.votes.up + val neutral: Int = a.votes.neutral + val down: Int = a.votes.down + val total: Int = a.votes.total + val score: Int = a.votes.score + } + val views: Any = viewManager.getViewsCount(article).let { v -> + object { + val total = v.total + val unique = v.unique + } + } + val opinions: Map = a.opinions + } + } + ) launch { viewManager.addView(call.request.local.remoteHost, article, citizenOrNull) diff --git a/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt b/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt index c8afc74..4d408ad 100644 --- a/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt +++ b/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt @@ -4,7 +4,6 @@ import fr.dcproject.common.security.assert import fr.dcproject.common.utils.receiveOrBadRequest import fr.dcproject.component.article.ArticleAccessControl import fr.dcproject.component.article.database.ArticleForUpdate -import fr.dcproject.component.article.database.ArticleForView import fr.dcproject.component.article.database.ArticleRepository import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input import fr.dcproject.component.auth.citizen @@ -57,9 +56,16 @@ object UpsertArticle { post { val article = call.convertRequestToEntity() ac.assert { canUpsert(article, citizenOrNull) } - val newArticle: ArticleForView = repo.upsert(article) ?: error("Article not updated") - call.respond(newArticle) - publisher.publish(ArticleUpdateNotification(newArticle)) + repo.upsert(article)?.let { a -> + call.respond( + object { + val id: UUID = a.id + val versionId = a.versionId + val versionNumber = a.versionNumber + } + ) + publisher.publish(ArticleUpdateNotification(a)) + } ?: error("Article not updated") } } } diff --git a/src/main/resources/openapi2.yaml b/src/main/resources/openapi2.yaml index 2b206fc..855d010 100644 --- a/src/main/resources/openapi2.yaml +++ b/src/main/resources/openapi2.yaml @@ -1,6 +1,6 @@ openapi: 3.0.2 info: - version: '0.1' + version: '' title: 'DC Project' description: 'A free comunity program for create constitution' @@ -46,7 +46,7 @@ paths: format: uuid title: type: string - created_by: + createdBy: type: object additionalProperties: false properties: @@ -56,9 +56,9 @@ paths: name: type: object properties: - first_name: + firstName: type: string - last_name: + lastName: type: string email: type: string @@ -74,6 +74,71 @@ paths: type: string draft: type: boolean + post: + security: + - JWTAuth: [] + summary: Create new Article + tags: + - article + operationId: insertArticle + requestBody: + required: true + content: + application/json: + schema: + required: + - title + - content + - description + - tags + - anonymous + - draft + properties: + title: + type: string + example: + Limit power of press + content: + type: string + example: + Lorem upsum... + description: + type: string + example: + I think is the bether choice + tags: + type: array + items: + type: string + default: [ ] + example: [ power, press ] + anonymous: + type: boolean + default: true + draft: + type: boolean + default: false + workgroup: + allOf: + - $ref: '#/components/schemas/UuidEntity' + - default: null + responses: + 200: + description: Article created + content: + application/json: + schema: + properties: + id: + type: string + format: uuid + versionId: + type: string + format: uuid + versionNumber: + type: integer + 401: + $ref: '#/components/responses/401' /articles/{article}: parameters: - $ref: '#/components/parameters/article' @@ -90,53 +155,66 @@ paths: content: application/json: schema: - additionalProperties: false - properties: - id: - type: string - format: uuid - title: - type: string - anonymous: - type: boolean - content: - type: string - description: - type: string - tags: - type: array - items: - type: string - draft: - type: boolean - last_version: - type: boolean - created_by: - type: object - additionalProperties: false + $ref: '#/components/schemas/ArticleResponse' + + /articles/{article}/versions: + parameters: + - $ref: '#/components/parameters/article' + get: + summary: Get all versions of articles + tags: + - article + operationId: getArticleVersions + responses: + 200: + description: The versions of Article + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Paginated' + - type: object properties: - id: - type: string - format: uuid - name: - type: object - properties: - first_name: - type: string - last_name: - type: string - email: - type: string - workgroup: - type: object - nullable: true - additionalProperties: false - properties: - id: - type: string - format: uuid - name: - type: string + result: + type: array + items: + additionalProperties: false + properties: + id: + type: string + format: uuid + title: + type: string + draft: + type: boolean + lastVersion: + type: boolean + createdBy: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: object + properties: + firstName: + type: string + lastName: + type: string + email: + type: string + workgroup: + type: object + nullable: true + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string components: parameters: @@ -165,7 +243,7 @@ components: name: sort in: query description: The sort field name - example: first_name + example: firstName required: false schema: type: string @@ -220,6 +298,14 @@ components: type: string format: uuid + responses: + 401: + description: Unautorized + content: + application/json: + schema: + description: noting + schemas: UUID: type: string @@ -260,6 +346,120 @@ components: type: integer minimum: 0 example: 1 + ArticleResponse: + additionalProperties: false + required: + - id + - versionId + - versionNumber + - title + - anonymous + - content + - description + - tags + - draft + - lastVersion + - createdBy + - views + - opinions + - votes + properties: + id: + type: string + format: uuid + versionId: + type: string + format: uuid + versionNumber: + type: integer + minimum: 0 + title: + type: string + anonymous: + type: boolean + content: + type: string + description: + type: string + tags: + type: array + items: + type: string + draft: + type: boolean + lastVersion: + type: boolean + createdAt: + type: string + format: 'date-time' + createdBy: + type: object + additionalProperties: false + required: + - id + - name + - email + properties: + id: + type: string + format: uuid + name: + type: object + required: + - firstName + - lastName + properties: + firstName: + type: string + lastName: + type: string + email: + type: string + workgroup: + type: object + nullable: true + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + views: + additionalProperties: false + required: + - total + - unique + properties: + total: + type: integer + unique: + type: integer + opinions: + additionalProperties: + type: integer + example: + 'good writed': 5 + 'to short': 19 + votes: + additionalProperties: false + required: + - up + - neutral + - down + - total + - score + properties: + up: + type: integer + neutral: + type: integer + down: + type: integer + total: + type: integer + score: + type: integer securitySchemes: JWTAuth: diff --git a/src/main/resources/sql/functions/article/find_articles_versions_by_version_id.sql b/src/main/resources/sql/functions/article/find_articles_versions_by_version_id.sql index 30bd7c4..25cff2c 100644 --- a/src/main/resources/sql/functions/article/find_articles_versions_by_version_id.sql +++ b/src/main/resources/sql/functions/article/find_articles_versions_by_version_id.sql @@ -13,10 +13,17 @@ begin into resource, total from ( select - a.*, - find_citizen_by_id_with_user(a.created_by_id) as created_by, - find_workgroup_by_id(a.workgroup_id) as workgroup, - count_vote(a.id) as votes + a.id, + a.created_at, + find_citizen_by_id_with_user(a.created_by_id) as created_by, + find_workgroup_by_id(a.workgroup_id) as workgroup, + a.version_id, + a.version_number, + a.title, + a.deleted_at, + a.draft, + a.last_version, + count_vote(a.id) as votes from article as a where a.version_id = _version_id order by a.version_number desc diff --git a/src/test/kotlin/functional/NotificationConsumerTest.kt b/src/test/kotlin/functional/NotificationConsumerTest.kt index 823832b..85302ab 100644 --- a/src/test/kotlin/functional/NotificationConsumerTest.kt +++ b/src/test/kotlin/functional/NotificationConsumerTest.kt @@ -8,7 +8,6 @@ import fr.dcproject.component.article.database.ArticleRef import fr.dcproject.component.auth.database.UserCreator import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI -import fr.dcproject.component.citizen.database.CitizenRef import fr.dcproject.component.follow.database.FollowArticleRepository import fr.dcproject.component.follow.database.FollowForView import fr.dcproject.component.notification.ArticleUpdateNotification @@ -108,7 +107,11 @@ class NotificationConsumerTest { title = "MyTitle", content = "myContent", description = "myDescription", - createdBy = CitizenRef() + createdBy = CitizenCreator( + name = CitizenI.Name(firstName = "", lastName = ""), + email = "", + user = UserCreator(username = ""), + ) ) ) ).await() diff --git a/src/test/kotlin/functional/NotificationsPushTest.kt b/src/test/kotlin/functional/NotificationsPushTest.kt index 8eb61af..9c266d7 100644 --- a/src/test/kotlin/functional/NotificationsPushTest.kt +++ b/src/test/kotlin/functional/NotificationsPushTest.kt @@ -3,7 +3,9 @@ package functional import com.rabbitmq.client.ConnectionFactory import fr.dcproject.application.Configuration import fr.dcproject.component.article.database.ArticleForView -import fr.dcproject.component.citizen.database.CitizenRef +import fr.dcproject.component.auth.database.UserCreator +import fr.dcproject.component.citizen.database.CitizenCreator +import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.notification.ArticleUpdateNotification import fr.dcproject.component.notification.Notification import fr.dcproject.component.notification.NotificationsPush @@ -53,7 +55,11 @@ internal class NotificationsPushTest { every { redisClient.connect().async() } returns asyncCommand /* Citizen of notification */ - val citizen = CitizenRef() + val citizen = CitizenCreator( + name = CitizenI.Name(firstName = "", lastName = ""), + email = "", + user = UserCreator(username = ""), + ) /* Article is the target of the notification */ val article = ArticleForView( content = "content..", diff --git a/src/test/kotlin/functional/ViewTest.kt b/src/test/kotlin/functional/ViewTest.kt index 0d5018e..d7252db 100644 --- a/src/test/kotlin/functional/ViewTest.kt +++ b/src/test/kotlin/functional/ViewTest.kt @@ -4,6 +4,9 @@ import fr.dcproject.application.Env.TEST import fr.dcproject.application.module import fr.dcproject.component.article.ArticleViewManager import fr.dcproject.component.article.database.ArticleForView +import fr.dcproject.component.auth.database.UserCreator +import fr.dcproject.component.citizen.database.CitizenCreator +import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.citizen.database.CitizenRef import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.server.testing.withTestApplication @@ -27,9 +30,13 @@ class ViewTest { @Test fun `test View Article`() { val article = ArticleForView( - id = UUID.randomUUID(), versionId = UUID.randomUUID(), - createdBy = CitizenRef(), + createdBy = CitizenCreator( + id = UUID.randomUUID(), + name = CitizenI.Name(firstName = "", lastName = ""), + email = "", + user = UserCreator(username = ""), + ), content = "", description = "", title = "" diff --git a/src/test/kotlin/integration/Article routes.kt b/src/test/kotlin/integration/Article routes.kt index 6c3f3ff..d943f64 100644 --- a/src/test/kotlin/integration/Article routes.kt +++ b/src/test/kotlin/integration/Article routes.kt @@ -32,9 +32,9 @@ class `Article routes` : BaseTest() { `Given I have articles`(3) `When I send a GET request`("/articles") `Then the response should be` OK and { `And the response should not be null`() - `And the response should contain pattern`("$.result[0].created_by.name.first_name", "firstName.+") - `And the response should contain pattern`("$.result[1].created_by.name.first_name", "firstName.+") - `And the response should contain pattern`("$.result[2].created_by.name.first_name", "firstName.+") + `And the response should contain pattern`("$.result[0].createdBy.name.firstName", "firstName.+") + `And the response should contain pattern`("$.result[1].createdBy.name.firstName", "firstName.+") + `And the response should contain pattern`("$.result[2].createdBy.name.firstName", "firstName.+") `And the response should not contain`("$.result[3]") `And the response should contain list`("$.result", 3, 3) `And schema must be valid`() @@ -77,6 +77,7 @@ class `Article routes` : BaseTest() { `And the response should not be null`() `And have property`("$.total") `whish contains` 1 `And have property`("$.result[0].id") `whish contains` "13e6091c-8fed-4600-b079-a97a6b7a9800" + `And schema must be valid`() } } } @@ -89,7 +90,7 @@ class `Article routes` : BaseTest() { `authenticated as`("John", "Doe") """ { - "version_id": "09c418b6-63ba-448b-b38b-502b41cd500e", + "versionId": "09c418b6-63ba-448b-b38b-502b41cd500e", "title": "title2", "anonymous": false, "content": "content2", @@ -101,8 +102,8 @@ class `Article routes` : BaseTest() { """ } `Then the response should be` OK and { `And the response should not be null`() - `And have property`("$.version_id") `whish contains` "09c418b6-63ba-448b-b38b-502b41cd500e" - `And have property`("$.title") `whish contains` "title2" + `And have property`("$.versionId") `whish contains` "09c418b6-63ba-448b-b38b-502b41cd500e" + `And schema must be valid`() } } } diff --git a/src/test/kotlin/unit/security/Article Access Control.kt b/src/test/kotlin/unit/security/Article Access Control.kt index 799422f..9dd5895 100644 --- a/src/test/kotlin/unit/security/Article Access Control.kt +++ b/src/test/kotlin/unit/security/Article Access Control.kt @@ -3,10 +3,13 @@ package unit.security import fr.dcproject.common.security.AccessDecision.DENIED import fr.dcproject.common.security.AccessDecision.GRANTED import fr.dcproject.component.article.ArticleAccessControl +import fr.dcproject.component.article.database.ArticleForListing import fr.dcproject.component.article.database.ArticleForView import fr.dcproject.component.auth.database.User +import fr.dcproject.component.auth.database.UserCreator import fr.dcproject.component.auth.database.UserI import fr.dcproject.component.citizen.database.CitizenCart +import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI import fr.postgresjson.connexion.Paginated import io.mockk.every @@ -26,13 +29,13 @@ import fr.dcproject.component.article.database.ArticleRepository as ArticleRepo @Execution(CONCURRENT) @Tags(Tag("security"), Tag("unit")) internal class `Article Access Control` { - private val tesla = CitizenCart( + private val tesla = CitizenCreator( id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"), - user = User( + user = UserCreator( username = "nicolas-tesla", - roles = listOf(UserI.Roles.ROLE_USER) ), - name = CitizenI.Name("Nicolas", "Tesla") + name = CitizenI.Name("Nicolas", "Tesla"), + email = "nikola-tesla@volt.com" ) private val einstein = CitizenCart( user = User( @@ -42,16 +45,16 @@ internal class `Article Access Control` { name = CitizenI.Name("Albert", "Einstein") ) - private fun getRepo(article: ArticleForView): ArticleRepo { + private fun getRepo(article: ArticleForListing): ArticleRepo { return mockk { - every { findVersionsByVersionId(1, 1, any()) } returns Paginated(listOf(article), 0, 1, 1) + every { find(1, 1, any()) } returns Paginated(listOf(article), 0, 1, 1) } } @Test fun `creator can be view the article`() { val article = getArticle(tesla).copy(draft = true) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(article, tesla) .decision `should be` GRANTED } @@ -59,7 +62,7 @@ internal class `Article Access Control` { @Test fun `other user can be view the article`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(article, einstein) .decision `should be` GRANTED } @@ -69,7 +72,7 @@ internal class `Article Access Control` { val article = getArticle(tesla) val article2 = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(listOf(article, article2), einstein) .decision `should be` GRANTED } @@ -77,7 +80,7 @@ internal class `Article Access Control` { @Test fun `the no creator can not be view the article on draft`() { val article = getArticle(tesla).copy(draft = true) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(article, einstein) .decision `should be` DENIED } @@ -87,7 +90,7 @@ internal class `Article Access Control` { val article = getArticle(tesla) val article2 = getArticle(tesla).copy(draft = true) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(listOf(article, article2), einstein) .decision `should be` DENIED } @@ -95,7 +98,7 @@ internal class `Article Access Control` { @Test fun `can not view deleted article`() { val article = getArticle(tesla).copy(deletedAt = DateTime.now()) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canView(article, tesla) .decision `should be` DENIED } @@ -103,7 +106,7 @@ internal class `Article Access Control` { @Test fun `can delete article if owner`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canDelete(article, tesla) .decision `should be` GRANTED } @@ -111,7 +114,7 @@ internal class `Article Access Control` { @Test fun `can not delete article if not owner`() { val article = getArticle(tesla).copy(deletedAt = DateTime.now()) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canDelete(article, einstein) .code `should be` "article.delete.notYours" } @@ -119,7 +122,7 @@ internal class `Article Access Control` { @Test fun `can create article if logged`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canUpsert(article, tesla) .decision `should be` GRANTED } @@ -127,7 +130,7 @@ internal class `Article Access Control` { @Test fun `can not create article if not logged`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canUpsert(article, null) .code `should be` "article.create.notConnected" } @@ -135,7 +138,7 @@ internal class `Article Access Control` { @Test fun `can update article if yours`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canUpsert(article, tesla) .decision `should be` GRANTED } @@ -143,12 +146,12 @@ internal class `Article Access Control` { @Test fun `can not update article if not yours`() { val article = getArticle(tesla) - ArticleAccessControl(getRepo(article)) + ArticleAccessControl(getRepo(getArticleForListing(tesla))) .canUpsert(article, einstein) .code `should be` "article.update.notYours" } - private fun getArticle(createdBy: CitizenCart = tesla) = ArticleForView( + private fun getArticle(createdBy: CitizenCreator = tesla) = ArticleForView( id = UUID.randomUUID(), title = "Hello world", content = "Super", @@ -158,4 +161,10 @@ internal class `Article Access Control` { versionId = UUID.randomUUID(), versionNumber = 1 ) + + private fun getArticleForListing(createdBy: CitizenCreator = tesla) = ArticleForListing( + id = UUID.randomUUID(), + title = "Hello world", + createdBy = createdBy, + ) } diff --git a/src/test/kotlin/unit/security/Comment Access Control.kt b/src/test/kotlin/unit/security/Comment Access Control.kt index 6df3760..89c2034 100644 --- a/src/test/kotlin/unit/security/Comment Access Control.kt +++ b/src/test/kotlin/unit/security/Comment Access Control.kt @@ -5,9 +5,10 @@ import fr.dcproject.common.security.AccessDecision.GRANTED import fr.dcproject.component.article.database.ArticleForView import fr.dcproject.component.article.database.ArticleRef import fr.dcproject.component.auth.database.User +import fr.dcproject.component.auth.database.UserCreator import fr.dcproject.component.auth.database.UserI import fr.dcproject.component.citizen.database.Citizen -import fr.dcproject.component.citizen.database.CitizenCart +import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.comment.generic.CommentAccessControl import fr.dcproject.component.comment.generic.database.CommentForUpdate @@ -46,13 +47,13 @@ internal class `Comment Access Control` { name = CitizenI.Name("Albert", "Einstein") ) - private val einstein2 = CitizenCart( + private val einstein2 = CitizenCreator( id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), - user = User( + user = UserCreator( username = "albert-einstein", - roles = listOf(UserI.Roles.ROLE_USER) ), - name = CitizenI.Name("Albert", "Einstein") + name = CitizenI.Name("Albert", "Einstein"), + email = "albert-einstein@email.com" ) private val article1 = ArticleForView( diff --git a/src/test/kotlin/unit/security/Follow Access Control.kt b/src/test/kotlin/unit/security/Follow Access Control.kt index d6f5dfb..d57152a 100644 --- a/src/test/kotlin/unit/security/Follow Access Control.kt +++ b/src/test/kotlin/unit/security/Follow Access Control.kt @@ -7,7 +7,6 @@ import fr.dcproject.component.auth.database.User import fr.dcproject.component.auth.database.UserCreator import fr.dcproject.component.auth.database.UserI import fr.dcproject.component.citizen.database.Citizen -import fr.dcproject.component.citizen.database.CitizenCart import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.follow.FollowAccessControl @@ -55,13 +54,13 @@ internal class `Follow Access Control` { followAnonymous = true ) - private val einstein2 = CitizenCart( + private val einstein2 = CitizenCreator( id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), - user = User( + user = UserCreator( username = "albert-einstein", - roles = listOf(UserI.Roles.ROLE_USER) ), - name = CitizenI.Name("Albert", "Einstein") + name = CitizenI.Name("Albert", "Einstein"), + email = "albert-einstein@email.com" ) private val einstein3 = Citizen( diff --git a/src/test/kotlin/unit/security/Opinion Access Control.kt b/src/test/kotlin/unit/security/Opinion Access Control.kt index bee6fde..8bdd07a 100644 --- a/src/test/kotlin/unit/security/Opinion Access Control.kt +++ b/src/test/kotlin/unit/security/Opinion Access Control.kt @@ -3,10 +3,7 @@ package unit.security import fr.dcproject.common.security.AccessDecision.DENIED import fr.dcproject.common.security.AccessDecision.GRANTED import fr.dcproject.component.article.database.ArticleForView -import fr.dcproject.component.auth.database.User import fr.dcproject.component.auth.database.UserCreator -import fr.dcproject.component.auth.database.UserI -import fr.dcproject.component.citizen.database.CitizenCart import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.citizen.database.CitizenRef @@ -39,13 +36,13 @@ internal class `Opinion Access Control` { id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), ) - private val einstein2 = CitizenCart( + private val einstein2 = CitizenCreator( id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), - user = User( + user = UserCreator( username = "albert-einstein", - roles = listOf(UserI.Roles.ROLE_USER) ), - name = CitizenI.Name("Albert", "Einstein") + name = CitizenI.Name("Albert", "Einstein"), + email = "albert-einstein@email.com" ) private val article1 = ArticleForView( diff --git a/src/test/kotlin/unit/security/Vote Access Control.kt b/src/test/kotlin/unit/security/Vote Access Control.kt index d451e9c..d04dbb4 100644 --- a/src/test/kotlin/unit/security/Vote Access Control.kt +++ b/src/test/kotlin/unit/security/Vote Access Control.kt @@ -7,7 +7,6 @@ import fr.dcproject.component.auth.database.User import fr.dcproject.component.auth.database.UserCreator import fr.dcproject.component.auth.database.UserI import fr.dcproject.component.citizen.database.Citizen -import fr.dcproject.component.citizen.database.CitizenCart import fr.dcproject.component.citizen.database.CitizenCreator import fr.dcproject.component.citizen.database.CitizenI import fr.dcproject.component.vote.VoteAccessControl @@ -60,13 +59,13 @@ internal class `Vote Access Control` { followAnonymous = true ) - private val einstein2 = CitizenCart( + private val einstein2 = CitizenCreator( id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), - user = User( + user = UserCreator( username = "albert-einstein", - roles = listOf(UserI.Roles.ROLE_USER) ), - name = CitizenI.Name("Albert", "Einstein") + name = CitizenI.Name("Albert", "Einstein"), + email = "albert-einstein@email.com" ) private val article1 = ArticleForView(