https://kotlinlang.org logo
#ktor
Title
# ktor
s

S.

03/27/2022, 4:14 PM
I'm trying to test endpoints within a authenticate block which requires a UserSession but for some reason I can't get it to work with my /login-test route: Client request(GET http://localhost/login-test) invalid: 404 Not Found. Text: "" io.ktor.client.plugins.ClientRequestException: Client request(GET http://localhost/login-test) invalid: 404 Not Found. Text: ""
Copy code
testApplication {
            client.config {
                install(HttpCookies)
                install(ContentNegotiation) {
                    json()
                }
            }
            routing {
                get("/login-test") {
                    call.sessions.set(UserSession(id = 4))
                }
            }
            client.get("/login-test")
            val response = client.get("v1/words")
            response.status shouldBe HttpStatusCode.OK
        }
a

Aleksei Tirman [JB]

03/27/2022, 4:30 PM
You have to respond with something in
get("/login-test")
, e.g.
call.respond(HttpStatusCode.OK)
or disable default response validation.
s

S.

03/27/2022, 4:32 PM
ofc 🤦🏻 ty
though seems like it still doesn't create the session. the authentication still fails and I always get the challenge's response
a

Aleksei Tirman [JB]

03/27/2022, 7:14 PM
Could you please share the code for your server part?
s

S.

03/27/2022, 8:38 PM
Copy code
fun Application.configureSecurity() {

    install(Authentication) {
        basic("auth_basic") {
            skipWhen { call -> call.sessions.get<UserSession>() != null }
            realm = "Access to the '/v1' path"
            validate {
                UserService.authenticate(it)
            }
        }

        session<UserSession>("auth_session") {
            validate { session ->
                session
            }
            challenge {
                call.respondText("Failed", status = HttpStatusCode.Unauthorized)
            }
        }
    }

    install(Sessions) {
        cookie<UserSession>("user_session") {
            cookie.path = "/v1"
            cookie.maxAgeInSeconds = 21_600
        }
    }
}
Copy code
authenticate("auth_basic") {
                get("/login") {
                    val userId = call.principal<UserIdPrincipal>()
                    call.sessions.set(UserSession(id = userId?.name!!.toInt()))
                    call.respondText("Login successful", status = HttpStatusCode.Accepted)
                }
            }

            authenticate("auth_session") {
                get("/hello") {
                    val userSession = call.principal<UserSession>()
                    call.respondText("Hello ${userSession?.id}")
                }
                wordRouting()
            }
wordRouting() is what I'm trying to access. It works fine when testing it manually but this doesn't work either:
Copy code
testApplication {
            client.get("v1/login") {
                basicAuth("Kaley", "501")
            }
            val response = client.get("v1/words")
        }
(I'm aware of the NPE issue in /login, will address it later)
a

Aleksei Tirman [JB]

03/28/2022, 10:08 AM
The following test passes:
Copy code
import io.ktor.client.plugins.cookies.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.sessions.*
import io.ktor.server.testing.*
import org.junit.Test
import kotlin.test.assertEquals

class SomeTest {
    @Test
    fun some() {
        testApplication {
            application {
                configureSecurity()
            }
            val client = createClient {
                install(HttpCookies)
            }
            client.get("v1/login") {
                basicAuth("Kaley", "501")
            }
            val response = client.get("v1/hello")
            assertEquals("Hello 123", response.bodyAsText())
        }
    }
}

fun Application.configureSecurity() {
    install(Authentication) {
        basic("auth_basic") {
            skipWhen { call -> call.sessions.get<UserSession>() != null }
            realm = "Access to the '/v1' path"
            validate { creds ->
                if (creds.name == "Kaley" && creds.password == "501") UserIdPrincipal("123") else null
            }
        }

        session<UserSession>("auth_session") {
            validate { session ->
                session
            }
            challenge {
                call.respondText("Failed", status = HttpStatusCode.Unauthorized)
            }
        }
    }

    install(Sessions) {
        cookie<UserSession>("user_session") {
            cookie.path = "/v1"
            cookie.maxAgeInSeconds = 21_600
        }
    }

    routing {
        route("/v1") {
            authenticate("auth_basic") {
                get("/login") {
                    val userId = call.principal<UserIdPrincipal>()
                    call.sessions.set(UserSession(id = userId?.name!!.toInt()))
                    call.respondText("Login successful", status = HttpStatusCode.Accepted)
                }
            }

            authenticate("auth_session") {
                get("/hello") {
                    val userSession = call.principal<UserSession>()
                    call.respondText("Hello ${userSession?.id}")
                }
//            wordRouting()
            }
        }
    }
}

data class UserSession(val id: Int): Principal
Seems like the problem is that the
client.config
call doesn’t mutate configuration of an existing HttpClient but returns a new one which you don’t use. I would recommend creating a client in the test using the
createClient
method.
s

S.

03/28/2022, 4:12 PM
Ah I see 😅, honestly thought it's to configure the existing client
It works now, thank you so much for putting that much time into it~
2 Views