More tests !
This commit is contained in:
parent
913e3dfc93
commit
07ec732c55
17
api/pom.xml
17
api/pom.xml
@ -129,6 +129,11 @@
|
||||
<artifactId>ktorm-support-mysql</artifactId>
|
||||
<version>${ktorm_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.liuwj.ktorm</groupId>
|
||||
<artifactId>ktorm-jackson</artifactId>
|
||||
<version>${ktorm_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.hekeki</groupId>
|
||||
<artifactId>huckleberry</artifactId>
|
||||
@ -179,6 +184,18 @@
|
||||
<version>1.61</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.mockk</groupId>
|
||||
<artifactId>mockk</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src</sourceDirectory>
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
package be.vandewalleh.extensions
|
||||
|
||||
import be.vandewalleh.auth.UserDbIdPrincipal
|
||||
import be.vandewalleh.kodein
|
||||
import be.vandewalleh.services.FullNoteCreateDTO
|
||||
import be.vandewalleh.services.FullNotePatchDTO
|
||||
import be.vandewalleh.services.UserService
|
||||
import io.ktor.application.*
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.request.*
|
||||
import io.ktor.response.*
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
|
||||
respond(status, status.description)
|
||||
respond(status, """{"msg": "${status.description}"}""")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,9 +3,12 @@ package be.vandewalleh.features
|
||||
import io.ktor.application.*
|
||||
import io.ktor.features.*
|
||||
import io.ktor.jackson.*
|
||||
import me.liuwj.ktorm.jackson.*
|
||||
|
||||
fun Application.contentNegotiationFeature() {
|
||||
install(ContentNegotiation) {
|
||||
jackson {}
|
||||
jackson {
|
||||
registerModule(KtormModule())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package be.vandewalleh.routing
|
||||
|
||||
import be.vandewalleh.entities.User
|
||||
import be.vandewalleh.extensions.respondStatus
|
||||
import be.vandewalleh.extensions.userId
|
||||
import be.vandewalleh.services.UserService
|
||||
@ -12,16 +13,22 @@ import io.ktor.routing.*
|
||||
import org.kodein.di.Kodein
|
||||
import org.kodein.di.generic.instance
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
import java.time.LocalDateTime
|
||||
|
||||
fun Routing.user(kodein: Kodein) {
|
||||
val userService by kodein.instance<UserService>()
|
||||
|
||||
post("/user/test") {
|
||||
val user = call.receive<User>()
|
||||
call.respond(user)
|
||||
}
|
||||
|
||||
route("/user") {
|
||||
post {
|
||||
val user = call.receive<UserDto>()
|
||||
val user = call.receive<User>()
|
||||
|
||||
if (userService.userExists(user.username, user.email))
|
||||
return@post call.respond(HttpStatusCode.Conflict)
|
||||
return@post call.respondStatus(HttpStatusCode.Conflict)
|
||||
|
||||
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
|
||||
|
||||
@ -32,7 +39,7 @@ fun Routing.user(kodein: Kodein) {
|
||||
|
||||
authenticate {
|
||||
put {
|
||||
val user = call.receive<UserDto>()
|
||||
val user = call.receive<User>()
|
||||
|
||||
if (userService.userExists(user.username, user.email))
|
||||
return@put call.respond(HttpStatusCode.Conflict)
|
||||
@ -45,12 +52,13 @@ fun Routing.user(kodein: Kodein) {
|
||||
}
|
||||
|
||||
delete {
|
||||
userService.deleteUser(call.userId())
|
||||
call.respondStatus(HttpStatusCode.OK)
|
||||
val status = if (userService.deleteUser(call.userId()))
|
||||
HttpStatusCode.OK
|
||||
else
|
||||
HttpStatusCode.NotFound
|
||||
call.respondStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private data class UserDto(val username: String, val email: String, val password: String)
|
||||
|
||||
@ -98,9 +98,13 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteUser(userId: Int) {
|
||||
fun deleteUser(userId: Int): Boolean {
|
||||
db.useTransaction {
|
||||
db.delete(Users) { it.id eq userId }
|
||||
return when (db.delete(Users) { it.id eq userId }) {
|
||||
1 -> true
|
||||
0 -> false
|
||||
else -> error("??")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
142
api/test/routing/UserControllerKtTest.kt
Normal file
142
api/test/routing/UserControllerKtTest.kt
Normal file
@ -0,0 +1,142 @@
|
||||
package routing
|
||||
|
||||
import be.vandewalleh.auth.SimpleJWT
|
||||
import be.vandewalleh.entities.User
|
||||
import be.vandewalleh.mainModule
|
||||
import be.vandewalleh.module
|
||||
import be.vandewalleh.services.UserService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.testing.*
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.amshove.kluent.*
|
||||
import org.junit.jupiter.api.*
|
||||
import org.kodein.di.Kodein
|
||||
import org.kodein.di.generic.bind
|
||||
import org.kodein.di.generic.instance
|
||||
import utils.*
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class UserControllerKtTest {
|
||||
|
||||
private val userService = mockk<UserService>()
|
||||
|
||||
init {
|
||||
// new user
|
||||
every { userService.userExists("new", "new@test.com") } returns false
|
||||
every { userService.createUser("new", "new@test.com", any()) } returns User {
|
||||
this.createdAt = LocalDateTime.now()
|
||||
this.username = "new"
|
||||
this.email = "new@test.com"
|
||||
}
|
||||
|
||||
// existing user
|
||||
every { userService.userExists("existing", "existing@test.com") } returns true
|
||||
every { userService.createUser("existing", "existing@test.com", any()) } returns null
|
||||
every { userService.getUserId("existing@test.com") } returns 1
|
||||
every { userService.deleteUser(1) } returns true andThen false
|
||||
|
||||
// modified user
|
||||
every { userService.userExists("modified", "modified@test.com") } returns true
|
||||
every {
|
||||
userService.userExists(
|
||||
and(not("modified"), not("existing")),
|
||||
and(not("modified@test.com"), not("existing@test.com"))
|
||||
)
|
||||
} returns false
|
||||
every { userService.userExists(1) } returns true
|
||||
every { userService.createUser("modified", "modified@test.com", any()) } returns null
|
||||
every { userService.getUserId("modified@test.com") } returns 1
|
||||
every { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit
|
||||
|
||||
}
|
||||
|
||||
|
||||
private val kodein = Kodein {
|
||||
import(mainModule, allowOverride = true)
|
||||
bind<UserService>(overrides = true) with instance(userService)
|
||||
}
|
||||
|
||||
private val testEngine = TestApplicationEngine().apply {
|
||||
start()
|
||||
application.module(kodein)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class CreateUser {
|
||||
@Test
|
||||
fun `create a new user`() {
|
||||
val res = testEngine.post("/user") {
|
||||
json {
|
||||
it["username"] = "new"
|
||||
it["password"] = "test"
|
||||
it["email"] = "new@test.com"
|
||||
}
|
||||
}
|
||||
res.status() `should be equal to` HttpStatusCode.Created
|
||||
res.content `should be equal to json` """{msg:"Created"}"""
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create an existing user`() {
|
||||
val res = testEngine.post("/user") {
|
||||
json {
|
||||
it["username"] = "existing"
|
||||
it["email"] = "existing@test.com"
|
||||
it["password"] = "test"
|
||||
}
|
||||
}
|
||||
res.status() `should be equal to` HttpStatusCode.Conflict
|
||||
res.content `should be equal to json` """{msg:"Conflict"}"""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
inner class DeleteUser {
|
||||
|
||||
@Test
|
||||
fun `delete an existing user`() {
|
||||
val authJwt by kodein.instance<SimpleJWT>("auth")
|
||||
val token = authJwt.sign(1)
|
||||
|
||||
val res = testEngine.delete("/user") {
|
||||
addHeader(HttpHeaders.Authorization, "Bearer $token")
|
||||
}
|
||||
res.status() `should be equal to` HttpStatusCode.OK
|
||||
res.content `should be equal to json` """{msg:"OK"}"""
|
||||
|
||||
// try again
|
||||
val res2 = testEngine.delete("/user") {
|
||||
setToken(token)
|
||||
}
|
||||
res2.status() `should be equal to` HttpStatusCode.NotFound
|
||||
res2.content `should be equal to json` """{msg:"Not Found"}"""
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class ModifyUser {
|
||||
|
||||
@Test
|
||||
fun `modify a user`() {
|
||||
val authJwt by kodein.instance<SimpleJWT>("auth")
|
||||
val token = authJwt.sign(1)
|
||||
|
||||
val res = testEngine.put("/user") {
|
||||
setToken(token)
|
||||
json {
|
||||
it["username"] = "ThisIsMyNewName"
|
||||
it["email"] = "ThisIsMyNewName@mail.com"
|
||||
}
|
||||
}
|
||||
|
||||
res.status() `should be equal to` HttpStatusCode.OK
|
||||
res.content `should be equal to json` """{msg:"OK"}"""
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
19
api/test/utils/Assertions.kt
Normal file
19
api/test/utils/Assertions.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package utils
|
||||
|
||||
import org.skyscreamer.jsonassert.JSONAssert
|
||||
|
||||
infix fun String?.shouldBeEqualToJson(expected: String?) = JSONAssert.assertEquals(expected, this, false)
|
||||
|
||||
infix fun String?.`should be equal to json`(expected: String?) = shouldBeEqualToJson(expected)
|
||||
|
||||
infix fun String?.shouldStrictlyBeEqualToJson(expected: String?) = JSONAssert.assertEquals(expected, this, true)
|
||||
|
||||
infix fun String?.`should strictly be equal to json`(expected: String?) = shouldStrictlyBeEqualToJson(expected)
|
||||
|
||||
infix fun String?.shouldNotStrictlyBeEqualToJson(expected: String?) = JSONAssert.assertNotEquals(expected, this, true)
|
||||
|
||||
infix fun String?.`should not strictly be equal to json`(expected: String?) = shouldNotStrictlyBeEqualToJson(expected)
|
||||
|
||||
infix fun String?.shouldNotBeEqualToJson(expected: String?) = JSONAssert.assertNotEquals(expected, this, false)
|
||||
|
||||
infix fun String?.`should not be equal to json`(expected: String?) = shouldNotBeEqualToJson(expected)
|
||||
23
api/test/utils/JsonAssertExtensions.kt
Normal file
23
api/test/utils/JsonAssertExtensions.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package utils
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
operator fun JSONObject.set(name: String, value: String) {
|
||||
this.put(name, value)
|
||||
}
|
||||
|
||||
operator fun JSONObject.set(name: String, value: Double) {
|
||||
this.put(name, value)
|
||||
}
|
||||
|
||||
operator fun JSONObject.set(name: String, value: Long) {
|
||||
this.put(name, value)
|
||||
}
|
||||
|
||||
operator fun JSONObject.set(name: String, value: Int) {
|
||||
this.put(name, value)
|
||||
}
|
||||
|
||||
operator fun JSONObject.set(name: String, value: Boolean) {
|
||||
this.put(name, value)
|
||||
}
|
||||
52
api/test/utils/KtorTestingExtensions.kt
Normal file
52
api/test/utils/KtorTestingExtensions.kt
Normal file
@ -0,0 +1,52 @@
|
||||
package utils
|
||||
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.server.testing.*
|
||||
import org.json.JSONObject
|
||||
|
||||
|
||||
fun TestApplicationRequest.json(block: (JSONObject) -> Unit) {
|
||||
addHeader(HttpHeaders.ContentType, "application/json")
|
||||
setBody(JSONObject().apply(block).toString())
|
||||
}
|
||||
|
||||
fun TestApplicationRequest.setToken(token: String) {
|
||||
addHeader(HttpHeaders.Authorization, "Bearer $token")
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.post(
|
||||
uri: String,
|
||||
setup: TestApplicationRequest.() -> Unit = {}
|
||||
): TestApplicationResponse = handleRequest {
|
||||
this.uri = uri
|
||||
this.method = HttpMethod.Post
|
||||
setup()
|
||||
}.response
|
||||
|
||||
fun TestApplicationEngine.get(
|
||||
uri: String,
|
||||
setup: TestApplicationRequest.() -> Unit = {}
|
||||
): TestApplicationResponse = handleRequest {
|
||||
this.uri = uri
|
||||
this.method = HttpMethod.Get
|
||||
setup()
|
||||
}.response
|
||||
|
||||
fun TestApplicationEngine.delete(
|
||||
uri: String,
|
||||
setup: TestApplicationRequest.() -> Unit = {}
|
||||
): TestApplicationResponse = handleRequest {
|
||||
this.uri = uri
|
||||
this.method = HttpMethod.Delete
|
||||
setup()
|
||||
}.response
|
||||
|
||||
fun TestApplicationEngine.put(
|
||||
uri: String,
|
||||
setup: TestApplicationRequest.() -> Unit = {}
|
||||
): TestApplicationResponse = handleRequest {
|
||||
this.uri = uri
|
||||
this.method = HttpMethod.Put
|
||||
setup()
|
||||
}.response
|
||||
Loading…
x
Reference in New Issue
Block a user