Ktor测试路由与 ‘authenticate(){}’

huangapple go评论113阅读模式
英文:

Ktor testing routes with 'authenticate(){}'

问题

以下是代码部分的翻译:

fun Application.install() {
    routing {
        route("point") {
            authenticate(AUTH_SESSION) {
                post("/test") { testController.test(context) }
            }
        }
    }
}
@Test
fun `test auth route`() = testApplication {
    val client = client()
    client.post("/point/test")
}

请注意,我已经删除了HTML编码字符(")并将其替换为普通引号。如果您需要其他内容的翻译,请提供相关文本。

英文:

What is the appropriate way of unit testing such routes?

fun Application.install() {
    routing {
        route("point") {
            authenticate(AUTH_SESSION) {
                post("/test") { testController.test(context) }
            }
        }
    }
}

The problem is such routes require auth info. This code results in 401 error:

@Test
fun `test auth route`() = testApplication {
    val client = client()
    client.post("/point/test")
}

I use GoogleOneTap auth for a mobile application. From GoogleOneTap jwt token I generate User session with a Bearer. That session expires after some time.
So, such a test user requires manual auth each time session expires? If so, it's not appropriate for a CI.

Should I implement a simple auth with user-password and call auth before each call under authenticate(AUTH_SESSION)?

答案1

得分: 0

这是你要翻译的内容的翻译部分:

Finally, I got it:
1) Read through ktor docs https://ktor.io/docs/testing.html
1) With any auth you have auth route: `post("/auth/myprovider")` which gives you bearer cookie

Your routing:
```kotlin
routing {
    // auth route to get a bearer cookie
    post("/auth/google") { googleAuthController.signIn(context) }
    authenticate(AUTH_SESSION) {
        // these routes need auth bearer cookie
        post("/my/route/requiring/auth")
    }
}

post("/my/route/requiring/auth") uses authentication, hence all testing requests have to have auth bearer cookie. To get it, create a separate test user with test credentials:

class GoogleAuthController(
    private val userDao: UserDao
) {

    suspend fun signIn(call: ApplicationCall) {
        val token = call.parameters["token"]!!
        val userInfo: UserInfo? = verifyTestUsers(token) ?: GoogleAuthVerifier.verify(token)
        if (userInfo != null) {
            // create user
            val userSession = UserSession(
                id = userInfo.id,
                email = userInfo.email,
                name = userInfo.name,
                pictureUrl = userInfo.pictureUrl,
                locale = userInfo.locale
            )
            // save user to db
            userDao.saveUserInfoGoogleOneTap(userSession)
            // set user cookie
            call.sessions.set(userSession)
            call.respond(HttpStatusCode.OK)
        } else {
            throw IllegalArgumentException("Invalid token")
        }
    }

    // testUsers: Map<String, UserInfo>;
    private fun verifyTestUsers(token: String): UserInfo? = testUsers[token]
}

Basically, this is how you can get a cookie for any of your test users.

Next, to make calls, you need a client:

suspend fun ApplicationTestBuilder.myAuthClient(token: String = testUserToken) = createClient {
    install(HttpCookies)
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
            encodeDefaults = true
        })
    }
}.also {
    it.post("/auth/google") {
        parameter("token", token)
    }
}

Here on each client creation you call your auth method and get credentials for a test user.

Now you are ready to make authenticated calls:

internal class TestAuth {

    @Test
    fun `test auth`() = testApplication {
        val client = myAuthClient() 
        // here `client` already has a Bearer cookie
        
        val response = client.post("/my/route/requiring/auth")
        assert(response.body<String>().isNotEmpty())
    }
}

希望这些翻译对你有所帮助。

英文:

Finally, I got it:

  1. Read through ktor docs https://ktor.io/docs/testing.html
  2. With any auth you have auth route: post(&quot;/auth/myprovider&quot;) which gives you bearer cookie

Your routing:

routing {
    // auth route to get a bearer cookie
    post(&quot;/auth/google&quot;) { googleAuthController.signIn(context) }
    authenticate(AUTH_SESSION) {
        // these routes need auth bearer cookie
        post(&quot;/my/route/requiring/auth&quot;)
    }
}

post(&quot;/my/route/requiring/auth&quot;) uses authentication, hence all testing requests have to have auth bearer cookie. To get it, create a separate test user with test credentials:

class GoogleAuthController(
    private val userDao: UserDao
) {

    suspend fun signIn(call: ApplicationCall) {
        val token = call.parameters[&quot;token&quot;]!!
        val userInfo: UserInfo? = verifyTestUsers(token) ?: GoogleAuthVerifier.verify(token)
        if (userInfo != null) {
            // create user
            val userSession = UserSession(
                id = userInfo.id,
                email = userInfo.email,
                name = userInfo.name,
                pictureUrl = userInfo.pictureUrl,
                locale = userInfo.locale
            )
            // save user to db
            userDao.saveUserInfoGoogleOneTap(userSession)
            // set user cookie
            call.sessions.set(userSession)
            call.respond(HttpStatusCode.OK)
        } else {
            throw IllegalArgumentException(&quot;Invalid token&quot;)
        }
    }

    // testUsers: Map&lt;String, UserInfo&gt;
    private fun verifyTestUsers(token: String): UserInfo? = testUsers[token]
}

Basically, this is how you can get a cookie for any of your test users.

Next, to make calls, you need a client:

suspend fun ApplicationTestBuilder.myAuthClient(token: String = testUserToken) = createClient {
    install(HttpCookies)
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
            encodeDefaults = true
        })
    }
}.also {
    it.post(&quot;/auth/google&quot;) {
        parameter(&quot;token&quot;, token)
    }
}

Here on each client creation you call your auth method and get credentials for a test user.

Now you are ready to make authenticated calls:

internal class TestAuth {

    @Test
    fun `test auth`() = testApplication {
        val client = myAuthClient() 
        // here `client` already has a Bearer cookie
        
        val response = client.post(&quot;/my/route/requiring/auth&quot;)
        assert(response.body&lt;String&gt;().isNotEmpty())
    }
}

huangapple
  • 本文由 发表于 2023年2月19日 15:32:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75498620.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定