Тестирование конечной точки при авторизации с Ktor - PullRequest
1 голос
/ 02 февраля 2020

Я изо всех сил пытаюсь написать тесты для конечной точки, которая находится под аутентификацией (токен). В частности, при написании теста я не могу связать запрос на вход со вторым запросом, несмотря на предоставление токена, полученного как часть запроса на вход.

LoginEndpoint.kt

const val LOGIN_ENDPOINT = "$API_VERSION/login"

@Location(LOGIN_ENDPOINT)
class Login

fun Route.loginEndpoint(patientsRepository: Repository<Patient>, authProvider: AuthProvider) {
    post<Login> {
        val params = call.receive<Parameters>()
        val userId = params["id"] ?: return@post call.respond(status = HttpStatusCode.BadRequest, message = "Missing user id")

        val user = patientsRepository.get(userId)
        if (user != null) {
            val token = authProvider.generateToken(user.id!!)
            call.respond(TextContent("{ \"token\": \"$token\" }", ContentType.Application.Json))
        } else {
            call.respond(status = HttpStatusCode.NotFound, message = "User with id $userId does not exist")
        }
    }
}

LoginEndpointTest.kt (прохождение, все хорошо)

    @Test
    fun `given user exists then returns 200 and token`() {
        val userId = "paco123"
        val token = "magic_token_123"
        withTestApplication({
            givenTestModule()
        }) {
            every { authProvider.generateToken(userId) } answers { token }
            givenPatientExists(userId)
            with(handleRequest(HttpMethod.Post, "/$API_VERSION/login") {
                addHeader("content-type", "application/x-www-form-urlencoded")
                setBody("id=$userId")
            }) {
                assertEquals(HttpStatusCode.OK, response.status())
                assertEquals("{ \"token\": \"magic_token_123\" }", response.content)
            }
        }
    }

    private fun Application.givenTestModule() {
        module(
            testing = true,
            repositoryModule = TestingRepositoryModule,
            authProvider = authProvider
        )
    }

Теперь проблемная c конечная точка.

ProfileEndpoint.kt

const val PATIENTS_API_ENDPOINT = "$API_VERSION/profile"

@Location(PATIENTS_API_ENDPOINT)
class ProfileEndpoint

fun Route.profileEndpoint(patientsRepository: Repository<Patient>) {
    authenticate("jwt") {
        get<ProfileEndpoint> {
            val apiUser: Patient = call.apiUser!!
            val id = apiUser.id!!
            val patient = patientsRepository.get(id)
            when (patient != null) {
                false -> call.respond(status = HttpStatusCode.NotFound, message = "Patient with id $id does not exist")
                true -> call.respond(status = HttpStatusCode.OK, message = patient.map())
            }
        }
    }
}

Наконец, мой провальный тест

ProfileEndpointTest.kt

    @Test
    fun `given user is logged in then returns 200 and profile`() {
        val userId = "paco123"
        val token = "magic_token_123"
        withTestApplication({
            givenTestModule()
        }) {
            every { authProvider.generateToken(userId) } answers { token }
            givenPatientExists(userId)

            handleRequest(HttpMethod.Post, "/$API_VERSION/login") {
                addHeader("content-type", "application/x-www-form-urlencoded")
                setBody("id=$userId")
            }

            handleRequest(HttpMethod.Get, "/$API_VERSION/profile") {
                addHeader("Authorization", "Bearer $token")
            }.apply {
                assertEquals(HttpStatusCode.OK, response.status())
            }
        }
    }

Ошибка:

expected:<200 OK> but was:<401 Unauthorized>
Expected :200 OK
Actual   :401 Unauthorized

AuthProvider.kt

open class AuthProvider(secret: String = System.getenv(Environment.JWT_SECRET)) {
    private val algorithm = Algorithm.HMAC512(secret)

    fun getVerifier(): JWTVerifier = JWT
        .require(algorithm)
        .withIssuer(APP_NAME)
        .build()

    fun generateToken(userId: String): String = JWT.create()
        .withSubject(SUBJECT)
        .withIssuer(APP_NAME)
        .withClaim(CLAIM, userId)
        .withExpiresAt(expiresAt())
        .sign(algorithm)

    private fun expiresAt() = Date(System.currentTimeMillis() + MILLIES_PER_DAY * TOKEN_DAYS_LENGTH)
}

val ApplicationCall.apiUser get() = authentication.principal<Patient>()

Я пытался использовать cookiesSession, как в примере этой документации https://ktor.io/servers/testing.html#preserving - печенье но это не сработало. Любая помощь будет высоко ценится.

...