Не подтверждена Коммит a467d42d создал по автору Rustam's avatar Rustam Зафиксировано автором GitHub
Просмотр файлов

KTOR-4248 Add more tests for WS converter (#2989)

владелец 89a39840
......@@ -9,15 +9,20 @@ import io.ktor.client.engine.cio.*
import io.ktor.client.engine.mock.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.tests.utils.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.server.websocket.WebSockets
import io.ktor.websocket.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import kotlin.test.*
......@@ -32,6 +37,7 @@ abstract class AbstractClientContentNegotiationTest : TestWithKtor() {
)
override val server: ApplicationEngine = embeddedServer(io.ktor.server.cio.CIO, serverPort) {
install(WebSockets)
routing {
createRoutes(this)
}
......@@ -39,6 +45,7 @@ abstract class AbstractClientContentNegotiationTest : TestWithKtor() {
protected abstract val defaultContentType: ContentType
protected abstract val customContentType: ContentType
protected abstract val webSocketsConverter: WebsocketContentConverter
@OptIn(InternalSerializationApi::class)
private suspend inline fun <reified T : Any> ApplicationCall.respond(
......@@ -67,6 +74,9 @@ abstract class AbstractClientContentNegotiationTest : TestWithKtor() {
configureContentNegotiation(defaultContentType)
block()
}
install(io.ktor.client.plugins.websocket.WebSockets) {
contentConverter = webSocketsConverter
}
}
}
......@@ -98,6 +108,11 @@ abstract class AbstractClientContentNegotiationTest : TestWithKtor() {
call.respondWithRequestBody(defaultContentType)
}
webSocket("ws") {
for (frame in incoming) {
outgoing.send(frame)
}
}
}
@Test
......@@ -277,6 +292,27 @@ abstract class AbstractClientContentNegotiationTest : TestWithKtor() {
}
}
@Test
fun testSerializeWebsocket() = testWithEngine(CIO) {
configureClient()
test { client ->
val session = client.webSocketSession { url(path = "/ws", port = serverPort) }
session.sendSerialized(User("user1", 23))
val user1 = session.receiveDeserialized<User>()
session.send(session.converter!!.serialize(User("user2", 32)))
val frame = session.incoming.receive()
val user2 = session.converter!!.deserialize<User>(frame)
session.close()
assertEquals("user1", user1.name)
assertEquals(23, user1.age)
assertEquals("user2", user2.name)
assertEquals(32, user2.age)
}
}
@Serializable
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
sealed class TestSealed {
......
......@@ -24,10 +24,9 @@ public interface WebsocketContentConverter {
*
* @param charset response charset
* @param typeInfo response body typeInfo
* @param contentType to which this data converter has been registered and that matches the client's [Accept] header
* @param value to be converted
*
* @return a converted [OutgoingContent] value, or null if [value] isn't suitable for this converter
* @return a converted [Frame] value, or null if [value] isn't suitable for this converter
*/
public suspend fun serialize(
charset: Charset,
......@@ -53,3 +52,29 @@ public interface WebsocketContentConverter {
*/
public fun isApplicable(frame: Frame): Boolean
}
/**
* Serializes a [value] to a WebSocket [Frame].
* This function could throw `WebsocketConverterNotFoundException` if the value is not suitable for conversion
*
* @param charset response charset
* @param value to be converted
*
* @return a converted [OutgoingContent] value, or null if [value] isn't suitable for this converter
*/
public suspend inline fun <reified T> WebsocketContentConverter.serialize(
value: T,
charset: Charset = Charsets.UTF_8
): Frame = serialize(charset, typeInfo<T>(), value as Any)
/**
* Deserializes [content] to the value of type [T]
*
* @return a converted value (deserialized) or throws `WebsocketConverterNotFoundException` if the context's
* subject is not suitable for this converter
*/
public suspend inline fun <reified T> WebsocketContentConverter.deserialize(
content: Frame,
charset: Charset = Charsets.UTF_8
): T = deserialize(charset, typeInfo<T>(), content) as T
......@@ -5,6 +5,7 @@
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.contentnegotiation.tests.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.gson.*
import org.junit.*
......@@ -13,6 +14,8 @@ class ClientGsonTest : AbstractClientContentNegotiationTest() {
override val defaultContentType: ContentType = ContentType.Application.Json
override val customContentType: ContentType = ContentType.parse("application/x-json")
override val webSocketsConverter: WebsocketContentConverter = GsonWebsocketContentConverter()
override fun ContentNegotiation.Config.configureContentNegotiation(contentType: ContentType) {
register(contentType, converter)
}
......
......@@ -34,7 +34,7 @@ public class JacksonWebsocketContentConverter(
}
try {
return withContext(Dispatchers.IO) {
val data = charset.newDecoder().decode(buildPacket { content.readBytes() })
val data = charset.newDecoder().decode(buildPacket { writeFully(content.readBytes()) })
objectmapper.readValue(data, objectmapper.constructType(typeInfo.reifiedType))
}
} catch (deserializeFailure: Exception) {
......
......@@ -11,6 +11,7 @@ import io.ktor.client.plugins.contentnegotiation.tests.*
import io.ktor.client.request.*
import io.ktor.client.tests.utils.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.request.*
......@@ -23,6 +24,7 @@ class ClientJacksonTest : AbstractClientContentNegotiationTest() {
override val defaultContentType: ContentType = ContentType.Application.Json
override val customContentType: ContentType = ContentType.parse("application/x-json")
override val webSocketsConverter: WebsocketContentConverter = JacksonWebsocketContentConverter()
override fun ContentNegotiation.Config.configureContentNegotiation(contentType: ContentType) {
register(contentType, converter)
......
......@@ -3,10 +3,11 @@
*/
package io.ktor.serialization.kotlinx.test.cbor
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.contentnegotiation.tests.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.*
import io.ktor.serialization.kotlinx.cbor.*
import io.ktor.server.application.*
import io.ktor.server.request.*
......@@ -19,6 +20,7 @@ import kotlinx.serialization.json.*
class CborClientKotlinxSerializationTest : AbstractClientContentNegotiationTest() {
override val defaultContentType: ContentType = ContentType.Application.Cbor
override val customContentType: ContentType = ContentType.parse("application/x-cbor")
override val webSocketsConverter: WebsocketContentConverter = KotlinxWebsocketSerializationConverter(DefaultCbor)
override fun ContentNegotiation.Config.configureContentNegotiation(contentType: ContentType) {
cbor(contentType = contentType)
......
......@@ -6,6 +6,7 @@ package io.ktor.serialization.kotlinx.test.json
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.contentnegotiation.tests.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.*
import io.ktor.serialization.kotlinx.json.*
......@@ -13,6 +14,7 @@ class JsonClientKotlinxSerializationTest : AbstractClientContentNegotiationTest(
private val converter = KotlinxSerializationConverter(DefaultJson)
override val defaultContentType: ContentType = ContentType.Application.Json
override val customContentType: ContentType = ContentType.parse("application/x-json")
override val webSocketsConverter: WebsocketContentConverter = KotlinxWebsocketSerializationConverter(DefaultJson)
override fun ContentNegotiation.Config.configureContentNegotiation(contentType: ContentType) {
register(contentType, converter)
......
......@@ -5,6 +5,7 @@
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.contentnegotiation.tests.*
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.*
import io.ktor.serialization.kotlinx.xml.*
import org.junit.*
......@@ -13,6 +14,7 @@ class XmlClientKotlinxSerializationTest : AbstractClientContentNegotiationTest()
private val converter = KotlinxSerializationConverter(DefaultXml)
override val defaultContentType: ContentType = ContentType.Application.Json
override val customContentType: ContentType = ContentType.parse("text/xml")
override val webSocketsConverter: WebsocketContentConverter = KotlinxWebsocketSerializationConverter(DefaultXml)
override fun ContentNegotiation.Config.configureContentNegotiation(contentType: ContentType) {
register(contentType, converter)
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать