Я изо всех сил пытаюсь написать тесты для конечной точки, которая находится под аутентификацией (токен). В частности, при написании теста я не могу связать запрос на вход со вторым запросом, несмотря на предоставление токена, полученного как часть запроса на вход.
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 - печенье но это не сработало. Любая помощь будет высоко ценится.