diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index d4e7b48..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index e0844bc..62269df 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,6 @@
+
diff --git a/build.gradle.kts b/build.gradle.kts
index c6bde77..1c78cad 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -38,6 +38,14 @@ dependencies {
implementation("net.pearx.kasechange:kasechange-jvm:1.1.0")
implementation("fr.postgresjson:postgresjson:$postgresjson_version")
testImplementation("io.ktor:ktor-server-tests:$ktor_version")
+ testImplementation("io.ktor:ktor-client-mock:$ktor_version")
+ testImplementation("io.ktor:ktor-client-mock-jvm:$ktor_version")
+ testImplementation("org.koin:koin-test:$koinVersion")
+ testImplementation("io.mockk:mockk:1.9")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.5.0")
+ testImplementation("org.amshove.kluent:kluent:1.4")
+ testImplementation("io.cucumber:cucumber-java8:4.3.1")
+ testImplementation("io.cucumber:cucumber-junit:4.3.1")
}
kotlin.sourceSets["main"].kotlin.srcDirs("src")
diff --git a/src/fr/dcproject/Module.kt b/src/fr/dcproject/Module.kt
index fd93cf4..31627f9 100644
--- a/src/fr/dcproject/Module.kt
+++ b/src/fr/dcproject/Module.kt
@@ -6,9 +6,10 @@ import org.koin.dsl.module
import java.io.File
import fr.dcproject.repository.Article as ArticleRepository
+val config = Config()
+
@KtorExperimentalAPI
val Module = module {
- val config = Config()
single { config }
diff --git a/src/fr/dcproject/repository/Article.kt b/src/fr/dcproject/repository/Article.kt
index aa2188c..c9b38e0 100644
--- a/src/fr/dcproject/repository/Article.kt
+++ b/src/fr/dcproject/repository/Article.kt
@@ -21,10 +21,17 @@ class Article(override var requester: Requester) : RepositoryI {
}
}
- fun find(page: Int = 1, limit: Int = 50, sort: String? = null, direction: Direction? = null, search: String? = null): Paginated {
+ fun find(
+ page: Int = 1,
+ limit: Int = 50,
+ sort: String? = null,
+ direction: Direction? = null,
+ search: String? = null
+ ): Paginated {
return requester
.getFunction("find_articles")
- .select(page, limit,
+ .select(
+ page, limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search
diff --git a/test/RunCucumberTest.kt b/test/RunCucumberTest.kt
new file mode 100644
index 0000000..de6c753
--- /dev/null
+++ b/test/RunCucumberTest.kt
@@ -0,0 +1,25 @@
+import cucumber.api.CucumberOptions
+import cucumber.api.Scenario
+import cucumber.api.java8.En
+import cucumber.api.junit.Cucumber
+import feature.Context
+import io.ktor.server.testing.TestApplicationEngine
+import io.ktor.server.testing.createTestEnvironment
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+import feature.Context.Companion.current as contextCurrent
+
+@RunWith(Cucumber::class)
+@CucumberOptions(plugin = ["pretty"])
+class RunCucumberTest: En {
+ init {
+ Before(-1) { scenario: Scenario ->
+// config.database = "dc-projectg-test"
+ contextCurrent = Context(TestApplicationEngine(createTestEnvironment()) {}, scenario)
+ }
+
+ After { scenario: Scenario ->
+ contextCurrent.engine.stop(0L, 0L, TimeUnit.MILLISECONDS)
+ }
+ }
+}
diff --git a/test/feature/Context.kt b/test/feature/Context.kt
new file mode 100644
index 0000000..ab4555e
--- /dev/null
+++ b/test/feature/Context.kt
@@ -0,0 +1,40 @@
+package feature
+
+import cucumber.api.Scenario
+import fr.dcproject.module
+import io.ktor.application.Application
+import io.ktor.server.testing.TestApplicationCall
+import io.ktor.server.testing.TestApplicationEngine
+import io.ktor.server.testing.TestApplicationRequest
+
+class Context(
+ val engine: TestApplicationEngine,
+ val scenario: Scenario
+) {
+ companion object {
+ lateinit var current: Context
+ }
+
+ init {
+ engine.start()
+ val moduleFunction: Application.() -> Unit = { module() }
+ val test: TestApplicationEngine.() -> Unit = {
+ moduleFunction(application)
+ }
+ engine.test()
+ }
+
+ var call: TestApplicationCall? = null
+
+ private val requestContextConfigurations: MutableList Unit> = mutableListOf()
+ fun setupRequest(testApplicationRequest: TestApplicationRequest) {
+ requestContextConfigurations.forEach {
+ it(testApplicationRequest)
+ }
+ }
+ fun setupNextRequests(requestContextConfiguration: TestApplicationRequest.() -> Unit) = requestContextConfigurations.add(requestContextConfiguration)
+}
+
+fun TestApplicationRequest.applyConfigurations() {
+ Context.current.setupRequest(this)
+}
\ No newline at end of file
diff --git a/test/feature/Request.kt b/test/feature/Request.kt
new file mode 100644
index 0000000..4c85e13
--- /dev/null
+++ b/test/feature/Request.kt
@@ -0,0 +1,92 @@
+package feature
+
+import com.google.gson.Gson
+import cucumber.api.Scenario
+import cucumber.api.java8.En
+import io.cucumber.datatable.DataTable
+import io.ktor.http.ContentType
+import io.ktor.http.HttpHeaders
+import io.ktor.http.HttpMethod
+import io.ktor.http.HttpStatusCode
+import io.ktor.server.testing.TestApplicationCall
+import io.ktor.server.testing.TestApplicationEngine
+import io.ktor.server.testing.setBody
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.koin.test.KoinTest
+import org.opentest4j.AssertionFailedError
+import kotlin.test.asserter
+import feature.Context.Companion.current as currentContext
+
+class Request: En, KoinTest {
+ init {
+ Before { scenario: Scenario ->
+ }
+
+ After { scenario: Scenario ->
+ }
+
+ When("I send a {string} request to {string} with body:") { method: String, uri: String, body: String ->
+ val test: TestApplicationEngine.() -> Unit = {
+ currentContext.call = handleRequest {
+ applyConfigurations()
+ addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
+ this.method = HttpMethod.parse(method)
+ this.uri = uri
+ setBody(body)
+ }
+ }
+
+ currentContext.engine.test()
+ }
+
+ When("I send a {string} request to {string}") { method: String, uri: String ->
+ val test: TestApplicationEngine.() -> Unit = {
+ currentContext.call = handleRequest {
+ applyConfigurations()
+ addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
+ this.method = HttpMethod.parse(method.toUpperCase())
+ this.uri = uri
+ }
+ }
+
+ currentContext.engine.test()
+ }
+
+ Then("the response status code should be {int}") { statusCode: Int ->
+ val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call", statusCode, null)
+ with(call) {
+ assertEquals(HttpStatusCode.fromValue(statusCode), response.status())
+ }
+ }
+
+ And("the response should contain:") { expected: DataTable ->
+ val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call")
+ val p = call.response
+ val response = Gson().fromJson>>(p.content, List::class.java)
+
+ expected.asMap(String::class.java, String::class.java).forEach { (key, value) ->
+ response.forEach {
+ if (it.containsKey(key)) {
+ assertEquals(it[key], value)
+ return@And
+ }
+ }
+ asserter.fail("The response not contain $key field")
+ }
+ }
+
+ And("the response should contain object:") { expected: DataTable ->
+ val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call")
+ val p = call.response
+ val response = Gson().fromJson