Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,25 @@ internal class QueryDecoder(

override fun decodeBoolean(): Boolean = parseStrictBoolean(decodeString())

override fun decodeInt(): Int = decodeString().toInt()
override fun decodeInt(): Int = scalarOrFail("Int", decodeString(), "value for '$currentName'", String::toIntOrNull)

override fun decodeLong(): Long = decodeString().toLong()
override fun decodeLong(): Long =
scalarOrFail("Long", decodeString(), "value for '$currentName'", String::toLongOrNull)

override fun decodeShort(): Short = decodeString().toShort()
override fun decodeShort(): Short =
scalarOrFail("Short", decodeString(), "value for '$currentName'", String::toShortOrNull)

override fun decodeByte(): Byte = decodeString().toByte()
override fun decodeByte(): Byte =
scalarOrFail("Byte", decodeString(), "value for '$currentName'", String::toByteOrNull)

override fun decodeDouble(): Double = decodeString().toDouble()
override fun decodeDouble(): Double =
scalarOrFail("Double", decodeString(), "value for '$currentName'", String::toDoubleOrNull)

override fun decodeFloat(): Float = decodeString().toFloat()
override fun decodeFloat(): Float =
scalarOrFail("Float", decodeString(), "value for '$currentName'", String::toFloatOrNull)

override fun decodeChar(): Char = decodeString().single()
override fun decodeChar(): Char =
scalarOrFail("Char", decodeString(), "value for '$currentName'") { it.singleOrNull() }

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = enumIndex(enumDescriptor, decodeString())
}
Expand All @@ -95,23 +101,31 @@ internal class QueryListDecoder(

override fun decodeBoolean(): Boolean = parseStrictBoolean(next())

override fun decodeInt(): Int = next().toInt()
override fun decodeInt(): Int = scalarOrFail("Int", next(), "list element", String::toIntOrNull)

override fun decodeLong(): Long = next().toLong()
override fun decodeLong(): Long = scalarOrFail("Long", next(), "list element", String::toLongOrNull)

override fun decodeShort(): Short = next().toShort()
override fun decodeShort(): Short = scalarOrFail("Short", next(), "list element", String::toShortOrNull)

override fun decodeByte(): Byte = next().toByte()
override fun decodeByte(): Byte = scalarOrFail("Byte", next(), "list element", String::toByteOrNull)

override fun decodeDouble(): Double = next().toDouble()
override fun decodeDouble(): Double = scalarOrFail("Double", next(), "list element", String::toDoubleOrNull)

override fun decodeFloat(): Float = next().toFloat()
override fun decodeFloat(): Float = scalarOrFail("Float", next(), "list element", String::toFloatOrNull)

override fun decodeChar(): Char = next().single()
override fun decodeChar(): Char = scalarOrFail("Char", next(), "list element") { it.singleOrNull() }

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = enumIndex(enumDescriptor, next())
}

/** Converts [raw] with [convert], failing with a [SerializationException] describing [kind] and [context] on error. */
private fun <T : Any> scalarOrFail(
kind: String,
raw: String,
context: String,
convert: (String) -> T?,
): T = convert(raw) ?: throw SerializationException("invalid $kind $context: '$raw'")

/**
* Parses a query value as a boolean, accepting only `"true"`/`"false"` case-insensitively.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,99 @@ class SerdeTest {
}
}

@Test
fun `decoding a malformed Int value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<Search>("q=x&page=abc")
}
}

@Test
fun `decoding an empty Int value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<Search>("q=x&page=")
}
}

@Test
fun `decoding a malformed Long value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=1&intValue=1&longValue=notanumber" +
"&floatValue=1.0&doubleValue=1.0&charValue=a&sort=ASC",
)
}
}

@Test
fun `decoding a malformed Short value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=notanumber&intValue=1&longValue=1" +
"&floatValue=1.0&doubleValue=1.0&charValue=a&sort=ASC",
)
}
}

@Test
fun `decoding a malformed Byte value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=notanumber&shortValue=1&intValue=1&longValue=1" +
"&floatValue=1.0&doubleValue=1.0&charValue=a&sort=ASC",
)
}
}

@Test
fun `decoding a malformed Double value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=1&intValue=1&longValue=1" +
"&floatValue=1.0&doubleValue=notanumber&charValue=a&sort=ASC",
)
}
}

@Test
fun `decoding a malformed Float value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=1&intValue=1&longValue=1" +
"&floatValue=notanumber&doubleValue=1.0&charValue=a&sort=ASC",
)
}
}

@Test
fun `decoding an empty Char value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=1&intValue=1&longValue=1" +
"&floatValue=1.0&doubleValue=1.0&charValue=&sort=ASC",
)
}
}

@Test
fun `decoding a multi-character Char value throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllScalars>(
"text=t&flag=true&byteValue=1&shortValue=1&intValue=1&longValue=1" +
"&floatValue=1.0&doubleValue=1.0&charValue=ab&sort=ASC",
)
}
}

@Test
fun `decoding a malformed list element throws a serialization exception`() {
assertFailsWith<SerializationException> {
QueryParametersFormat.decodeFromQueryString<AllLists>(
"bytes=abc&shorts=1&ints=1&longs=1&floats=1.0&doubles=1.0&chars=a&flags=true&sorts=ASC",
)
}
}

@Test
fun `decoding a nested serializable object is rejected`() {
assertFailsWith<SerializationException> {
Expand Down