SimpleNotes/api/test/integration/routing/AuthControllerKtTest.kt

228 lines
7.1 KiB
Kotlin

package integration.routing
import be.vandewalleh.auth.SimpleJWT
import be.vandewalleh.entities.User
import be.vandewalleh.features.Config
import be.vandewalleh.mainModule
import be.vandewalleh.module
import be.vandewalleh.services.UserService
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import io.ktor.http.*
import io.ktor.server.testing.*
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.amshove.kluent.*
import org.json.JSONObject
import org.junit.jupiter.api.*
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.mindrot.jbcrypt.BCrypt
import utils.*
import java.util.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AuthControllerKtTest {
private val userService = mockk<UserService>()
init {
val user = User {
password = BCrypt.hashpw("password", BCrypt.gensalt())
username = "existing"
}
user["id"] = 1
every { userService.getFromUsername("existing") } returns user
every { userService.userExists(1) } returns true
every { userService.getUserInfo(1) } returns User {
username = "existing"
email = "existing@mail.com"
}
val user2 = User {
password = BCrypt.hashpw("right password", BCrypt.gensalt())
username = "wrong"
}
user["id"] = 2
every { userService.getFromUsername("wrong") } returns user2
every { userService.getFromUsername("notExisting") } returns null
every { userService.userExists(3) } returns false
every { userService.getUserInfo(3) } returns null
}
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 Login {
@Test
fun `login existing user with valid password`() {
val res = testEngine.post("/user/login") {
json {
it["username"] = "existing"
it["password"] = "password"
}
}
verify { userService.getFromUsername("existing") }
res.status() `should be equal to` HttpStatusCode.OK
val jsonObject = JSONObject(res.content)
val hasToken = jsonObject.has("token")
hasToken `should be equal to` true
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
val token = jsonObject.getString("token")
authJwt.verifier.verify(token)
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
val refreshToken = jsonObject.getString("refreshToken")
refreshJwt.verifier.verify(refreshToken)
}
@Test
fun `login existing user with invalid password`() {
val res = testEngine.post("/user/login") {
json {
it["username"] = "wrong"
it["password"] = "not this"
}
}
verify { userService.getFromUsername("wrong") }
res.status() `should be equal to` HttpStatusCode.Unauthorized
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
}
@Test
fun `login not existing user`() {
val res = testEngine.post("/user/login") {
json {
it["username"] = "notExisting"
it["password"] = "babababa"
}
}
verify { userService.getFromUsername("notExisting") }
res.status() `should be equal to` HttpStatusCode.Unauthorized
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
}
@Test
fun `login without body`() {
val res = testEngine.post("/user/login") {
addHeader(HttpHeaders.ContentType, "application/json")
}
res.status() `should be equal to` HttpStatusCode.BadRequest
}
}
@Nested
inner class Refresh {
@Test
fun `test valid refresh token`() {
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
val refreshToken = refreshJwt.sign(1)
val res = testEngine.post("/user/refresh_token") {
json {
it["refreshToken"] = refreshToken
}
}
val jsonObject = JSONObject(res.content)
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
verify { userService.userExists(1) }
res.status() `should be equal to` HttpStatusCode.OK
}
@Test
fun `test valid refresh token for deleted user`() {
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
val refreshToken = refreshJwt.sign(3)
val res = testEngine.post("/user/refresh_token") {
json {
it["refreshToken"] = refreshToken
}
}
verify { userService.userExists(3) }
res.status() `should be equal to` HttpStatusCode.Unauthorized
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
}
@Test
fun `test expired refresh token for existing user`() {
val config by kodein.instance<Config>()
val algorithm = Algorithm.HMAC256(config.jwt.refresh.secret.value)
val expiredToken = JWT.create()
.withClaim("id", 1)
.withExpiresAt(Date(0)) // January 1, 1970, 00:00:00 GMT
.sign(algorithm)
val res = testEngine.post("/user/refresh_token") {
json {
it["refreshToken"] = expiredToken
}
}
res.status() `should be equal to` HttpStatusCode.Unauthorized
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
}
}
@Nested
inner class UserInfo {
@Test
fun `test user info for existing user`() {
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
val token = authJwt.sign(1)
val res = testEngine.get("/user/me") {
setToken(token)
}
res.content `should strictly be equal to json` """{user:{username:"existing", email: "existing@mail.com"}}"""
res.status() `should be equal to` HttpStatusCode.OK
}
@Test
fun `test user info on deleted user`() {
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
val token = authJwt.sign(3)
val res = testEngine.get("/user/me") {
setToken(token)
}
res.status()!!.value `should not be in range` (200..299)
val jsonObject = JSONObject(res.content)
jsonObject.keyList() `should be equal to` listOf("msg")
}
}
}