Add Integration test for citizen

This commit is contained in:
2021-02-10 00:58:42 +01:00
parent 55bfbb619d
commit ec0115d613
8 changed files with 268 additions and 76 deletions

View File

@@ -7,22 +7,13 @@ import fr.dcproject.application.Env.TEST
import fr.dcproject.application.module
import fr.postgresjson.connexion.Connection
import fr.postgresjson.migration.Migrations
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.server.testing.TestApplicationCall
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.TestApplicationRequest
import io.ktor.server.testing.TestApplicationResponse
import io.ktor.server.testing.createTestEnvironment
import io.ktor.server.testing.setBody
import io.ktor.util.KtorExperimentalAPI
import io.lettuce.core.RedisClient
import io.lettuce.core.api.sync.RedisCommands
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.amshove.kluent.`should be`
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
@@ -50,36 +41,6 @@ abstract class BaseTest : KoinTest {
return engine.test()
}
public fun TestApplicationEngine.`I send a GET request`(uri: String? = null, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
val setupOveride: TestApplicationRequest.() -> Unit = {
method = HttpMethod.Get
if (uri != null) {
this.uri = uri
}
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setup?.let { it() }
}
return handleRequest(true, setupOveride)
}
public fun TestApplicationEngine.`I send a POST request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
val setupOveride: TestApplicationRequest.() -> Unit = {
method = HttpMethod.Post
if (uri != null) {
this.uri = uri
}
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setup?.let { it() }?.let {
setBody(it.trimIndent())
}
}
return handleRequest(true, setupOveride)
}
fun TestApplicationRequest.`with body`(body: String) {
setBody(body.trimIndent())
}
@BeforeAll
fun before() {
if (init == false) {
@@ -114,13 +75,3 @@ abstract class BaseTest : KoinTest {
.sendQuery("rollback to savepoint test_begin;", listOf())
}
}
fun TestApplicationCall.`Then the response should be`(status: HttpStatusCode? = null, block: TestApplicationResponse.() -> Unit): TestApplicationCall {
if (status != null) {
response.status().`should be`(status)
}
block(response)
return this
}

View File

@@ -0,0 +1,53 @@
package integration.asserts
import com.jayway.jsonpath.JsonPath
import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.TestApplicationCall
import io.ktor.server.testing.TestApplicationResponse
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should be`
import org.amshove.kluent.`should not be null`
import org.amshove.kluent.shouldContain
import kotlin.test.assertEquals
fun TestApplicationCall.`Then the response should be`(status: HttpStatusCode? = null, block: TestApplicationResponse.() -> Unit): TestApplicationCall = this.apply {
if (status != null) {
response.status().`should be`(status)
}
block(response)
}
infix fun TestApplicationCall.`Then the response should be`(status: HttpStatusCode): TestApplicationCall = this.apply {
response.status().`should be`(status)
}
infix fun TestApplicationCall.and(block: TestApplicationResponse.() -> Unit): TestApplicationCall = this.apply {
block(response)
}
infix fun TestApplicationCall.`has property`(path: String): Pair<JsonPath, Any> =
JsonPath.compile(path).let { jsonPath ->
jsonPath.read<Any>(response.content)?.let { result ->
Pair(jsonPath, result)
} ?: throw AssertionError("\"${path}\" element not found on json response")
}
infix fun TestApplicationResponse.`And have property`(path: String): Pair<JsonPath, Any> =
JsonPath.compile(path).let { jsonPath ->
jsonPath.read<Any>(content)?.let { result ->
Pair(jsonPath, result)
} ?: throw AssertionError("\"${path}\" element not found on json response")
}
infix fun Pair<JsonPath, Any>.`whish contains`(expected: Any): Pair<JsonPath, Any> = this.apply {
expected `should be equal to` second
}
fun TestApplicationResponse.`And the response should contain`(path: String, valueExpected: String) {
assertEquals(valueExpected, JsonPath.read<Any>(content, path)?.toString() ?: throw AssertionError("\"$path -> ${valueExpected}\" element not found on json response"))
}
val TestApplicationResponse.`And the response should not be null` get() = content.`should not be null`()
infix fun String.`and should contains`(expected: String) = this
.`should not be null`()
.shouldContain(expected)

View File

@@ -0,0 +1,26 @@
package integration.asserts.given
import com.auth0.jwt.JWT
import fr.dcproject.component.auth.jwt.JwtConfig
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenRepository
import io.ktor.http.HttpHeaders
import io.ktor.server.testing.TestApplicationRequest
import org.koin.core.context.GlobalContext
fun TestApplicationRequest.`authenticated as`(
firstName: String,
lastName: String,
): Citizen {
val username = "$firstName-$lastName".toLowerCase()
val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() }
val citizen = repo.findByUsername(username) ?: error("Cititzen not exist with username $username")
val jwtAsString: String = JWT.create()
.withIssuer("dc-project.fr")
.withClaim("id", citizen.user.id.toString())
.sign(JwtConfig.algorithm)
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
return citizen
}

View File

@@ -1,38 +1,31 @@
package integration.prerequisite
package integration.asserts.given
import com.auth0.jwt.JWT
import fr.dcproject.common.utils.toUUID
import fr.dcproject.component.auth.UserForCreate
import fr.dcproject.component.auth.jwt.JwtConfig
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenForCreate
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRepository
import io.ktor.http.HttpHeaders
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.TestApplicationRequest
import org.joda.time.DateTime
import org.koin.core.KoinComponent
import org.koin.core.context.GlobalContext
import org.koin.core.get
import org.koin.test.get
import steps.KtorServerContext
import java.util.UUID
fun TestApplicationEngine.`Given I have citizen`(
firstName: String,
lastName: String,
email: String = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr",
id: UUID = UUID.randomUUID()
id: String = UUID.randomUUID().toString()
): Citizen? {
val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() }
val user = UserForCreate(
id = id,
id = id.toUUID(),
username = "$firstName-$lastName".toLowerCase(),
password = "azerty",
)
val citizen = CitizenForCreate(
id = id,
id = id.toUUID(),
name = CitizenI.Name(firstName, lastName),
email = email,
birthday = DateTime.now(),

View File

@@ -0,0 +1,53 @@
package integration.asserts.`when`
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.server.testing.TestApplicationCall
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.TestApplicationRequest
import io.ktor.server.testing.setBody
public fun TestApplicationEngine.`When I send a GET request`(uri: String? = null, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
val setupOveride: TestApplicationRequest.() -> Unit = {
method = HttpMethod.Get
if (uri != null) {
this.uri = uri
}
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setup?.let { it() }
}
return handleRequest(true, setupOveride)
}
public fun TestApplicationEngine.`When I send a POST request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
val setupOveride: TestApplicationRequest.() -> Unit = {
method = HttpMethod.Post
if (uri != null) {
this.uri = uri
}
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setup?.let { it() }?.let {
setBody(it.trimIndent())
}
}
return handleRequest(true, setupOveride)
}
public fun TestApplicationEngine.`When I send a PUT request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
val setupOveride: TestApplicationRequest.() -> Unit = {
method = HttpMethod.Put
if (uri != null) {
this.uri = uri
}
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setup?.let { it() }?.let {
setBody(it.trimIndent())
}
}
return handleRequest(true, setupOveride)
}
fun TestApplicationRequest.`with body`(body: String) {
setBody(body.trimIndent())
}

View File

@@ -1,14 +1,17 @@
package integration.auth
import integration.BaseTest
import integration.`Then the response should be`
import integration.prerequisite.`Given I have citizen`
import integration.asserts.`And the response should not be null`
import integration.asserts.`Then the response should be`
import integration.asserts.`and should contains`
import integration.asserts.`when`.`When I send a POST request`
import integration.asserts.given.`Given I have citizen`
import integration.asserts.given.`authenticated as`
import io.ktor.http.HttpStatusCode
import io.ktor.http.HttpStatusCode.Companion.NoContent
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.amshove.kluent.`should contain`
import org.amshove.kluent.`should not be null`
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Tags
import org.junit.jupiter.api.Test
@@ -24,18 +27,32 @@ class LoginTest : BaseTest() {
fun `I can login with username and password`() {
withIntegrationApplication {
`Given I have citizen`("Niels", "Bohr")
`I send a POST request`("/login") {
`When I send a POST request`("/login") {
"""
{
"username": "niels-bohr",
"password": "azerty"
}
"""
}.`Then the response should be` (HttpStatusCode.OK) {
content
.`should not be null`()
.`should contain`("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.")
}.`Then the response should be`(HttpStatusCode.OK) {
`And the response should not be null` `and should contains` "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9."
}
}
}
@Test
fun `I can be connect with Passwordless auth`() {
withIntegrationApplication {
`Given I have citizen`("Leonhard", "Euler", "fabrice.lecomte.be@gmail.com", id = "c606110c-ff0e-4d09-a79e-74632d7bf7bd")
`When I send a POST request`("/auth/passwordless") {
`authenticated as`("Leonhard", "Euler")
"""
{
"url": "https://dc-project.fr/password/reset",
"email": "fabrice.lecomte.be@gmail.com"
}
"""
} `Then the response should be` NoContent
}
}
}

View File

@@ -1,10 +1,10 @@
package integration.auth
import integration.BaseTest
import integration.`Then the response should be`
import integration.asserts.`Then the response should be`
import integration.asserts.`when`.`When I send a POST request`
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.server.testing.setBody
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.amshove.kluent.`should be null`
@@ -26,7 +26,7 @@ class RegisterTest : BaseTest() {
@Category(RegisterTest::class)
fun `I can register`() {
withIntegrationApplication {
`I send a POST request`("/register") {
`When I send a POST request`("/register") {
"""
{
"name": {"first_name":"George", "last_name":"MICHEL"},
@@ -38,7 +38,7 @@ class RegisterTest : BaseTest() {
"email": "george-junior@gmail.com"
}
"""
}.`Then the response should be` (HttpStatusCode.OK) {
}.`Then the response should be`(HttpStatusCode.OK) {
content
.`should not be null`()
.`should contain`("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.")
@@ -49,7 +49,7 @@ class RegisterTest : BaseTest() {
@Test
fun `I cannot register if no username was sent`() {
withIntegrationApplication {
`I send a POST request`("/register") {
`When I send a POST request`("/register") {
"""
{
"name": {"first_name":"George2", "last_name":"MICHEL2"},
@@ -60,7 +60,7 @@ class RegisterTest : BaseTest() {
}
}
"""
}.`Then the response should be` (HttpStatusCode.BadRequest) {
}.`Then the response should be`(HttpStatusCode.BadRequest) {
content.`should be null`()
}
}

View File

@@ -0,0 +1,99 @@
package integration.citizen
import integration.BaseTest
import integration.asserts.`And have property`
import integration.asserts.`And the response should not be null`
import integration.asserts.`Then the response should be`
import integration.asserts.`when`.`When I send a GET request`
import integration.asserts.`when`.`When I send a PUT request`
import integration.asserts.`whish contains`
import integration.asserts.and
import integration.asserts.given.`Given I have citizen`
import integration.asserts.given.`authenticated as`
import io.ktor.http.HttpStatusCode.Companion.BadRequest
import io.ktor.http.HttpStatusCode.Companion.Created
import io.ktor.http.HttpStatusCode.Companion.OK
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Tags
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@ExperimentalCoroutinesApi
@KtorExperimentalLocationsAPI
@KtorExperimentalAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tags(Tag("integration"), Tag("citizen"))
class CitizenTest : BaseTest() {
@Test
fun `I can get Citizens informations`() {
withIntegrationApplication {
`Given I have citizen`("Jean", "Perrin", id = "5267a5c6-af42-4a02-aa2b-6b71d2e43973")
`When I send a GET request`("/citizens") {
`authenticated as`("Jean", "Perrin")
} `Then the response should be` OK and {
`And the response should not be null`
}
}
}
@Test
fun `I can get specific Citizen informations`() {
withIntegrationApplication {
`Given I have citizen`("Linus", "Pauling", id = "47a05c0f-7329-46c3-a7d0-325db37e9114")
`When I send a GET request`("/citizens/47a05c0f-7329-46c3-a7d0-325db37e9114") {
`authenticated as`("Linus", "Pauling")
} `Then the response should be` OK and {
`And the response should not be null`
`And have property`("$.id") `whish contains` "47a05c0f-7329-46c3-a7d0-325db37e9114"
}
}
}
@Test
fun `I can get my citizen informations when I was connected`() {
withIntegrationApplication {
`Given I have citizen`("Henri", "Becquerel", id = "47356809-c8ef-4649-8b99-1c5cb9886d38")
`When I send a GET request`("/citizens/current") {
`authenticated as`("Henri", "Becquerel")
} `Then the response should be` OK and {
`And the response should not be null`
`And have property`("$.id") `whish contains` "47356809-c8ef-4649-8b99-1c5cb9886d38"
}
}
}
@Test
fun `I can change my password`() {
withIntegrationApplication {
`Given I have citizen`("Georges", "Charpak", id = "0c966522-4071-43e5-a3ca-cfff2557f2cf")
`When I send a PUT request`("/citizens/0c966522-4071-43e5-a3ca-cfff2557f2cf/password/change") {
`authenticated as`("Georges", "Charpak")
"""
{
"old_password": "azerty",
"new_password": "qwerty"
}
"""
} `Then the response should be` Created
}
}
@Test
fun `I cannot change my password if request is bad formated`() {
withIntegrationApplication {
`Given I have citizen`("Louis", "Breguet", id = "6cf2a19d-d15d-4ee5-b2a9-907afd26b525")
`When I send a PUT request`("/citizens/6cf2a19d-d15d-4ee5-b2a9-907afd26b525/password/change") {
`authenticated as`("Louis", "Breguet")
"""
{
"plup": "azerty",
"gloup": "qwerty"
}
"""
} `Then the response should be` BadRequest
}
}
}