summaryrefslogtreecommitdiff
path: root/formats/json-tests/commonTest/src/kotlinx/serialization
diff options
context:
space:
mode:
authorLeonid Startsev <sandwwraith@gmail.com>2023-08-07 19:35:38 +0200
committerLeonid Startsev <sandwwraith@users.noreply.github.com>2023-08-11 15:16:51 +0200
commite55f807ac344c5b147075dde924432f6b682642f (patch)
treefd72c6015d63d41ff6b6f1d948db04770bb81b4e /formats/json-tests/commonTest/src/kotlinx/serialization
parent7bf105eb3b730831b14a716786bc50605af6414a (diff)
downloadkotlinx.serialization-e55f807ac344c5b147075dde924432f6b682642f.tar.gz
Test & fix several exception messages from Json parser
To avoid cryptic and incorrect ones, such as `Expected quotation mark '"', but had '"' instead` or `unexpected token: 10`. Fixes #2360 Fixes #2399 Also remove @PublishedApi from BATCH_SIZE to remove it from public API dump.
Diffstat (limited to 'formats/json-tests/commonTest/src/kotlinx/serialization')
-rw-r--r--formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt165
1 files changed, 165 insertions, 0 deletions
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt
new file mode 100644
index 00000000..8c16ac01
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+
+class JsonErrorMessagesTest : JsonTestBase() {
+
+ @Test
+ fun testJsonTokensAreProperlyReported() = parametrizedTest { mode ->
+ val input1 = """{"boxed":4}"""
+ val input2 = """{"boxed":"str"}"""
+
+ val serString = serializer<Box<String>>()
+ val serInt = serializer<Box<Int>>()
+
+ checkSerializationException({
+ default.decodeFromString(serString, input1, mode)
+ }, { message ->
+ if (mode == JsonTestingMode.TREE)
+ assertContains(message, "String literal for key 'boxed' should be quoted.")
+ else
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 9: Expected quotation mark '\"', but had '4' instead at path: \$.boxed"
+ )
+ })
+
+ checkSerializationException({
+ default.decodeFromString(serInt, input2, mode)
+ }, { message ->
+ if (mode != JsonTestingMode.TREE)
+ // we allow number values to be quoted, so the message pointing to 's' is correct
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 9: Unexpected symbol 's' in numeric literal at path: \$.boxed"
+ )
+ else
+ assertContains(message, "Failed to parse literal as 'int' value")
+ })
+ }
+
+ @Test
+ fun testMissingClosingQuote() = parametrizedTest { mode ->
+ val input1 = """{"boxed:4}"""
+ val input2 = """{"boxed":"str}"""
+ val input3 = """{"boxed:"str"}"""
+ val serString = serializer<Box<String>>()
+ val serInt = serializer<Box<Int>>()
+
+ checkSerializationException({
+ default.decodeFromString(serInt, input1, mode)
+ }, { message ->
+ // For discussion:
+ // Technically, both of these messages are correct despite them being completely different.
+ // A `:` instead of `"` is a good guess, but `:`/`}` is a perfectly valid token inside Json string — for example,
+ // it can be some kind of path `{"foo:bar:baz":"my:resource:locator:{123}"}` or even URI used as a string key/value.
+ // So if the closing quote is missing, there's really no way to correctly tell where the key or value is supposed to end.
+ // Although we may try to unify these messages for consistency.
+ if (mode in setOf(JsonTestingMode.STREAMING, JsonTestingMode.TREE))
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 7: Expected quotation mark '\"', but had ':' instead at path: \$"
+ )
+ else
+ assertContains(
+ message, "Unexpected EOF at path: \$"
+ )
+ })
+
+ checkSerializationException({
+ default.decodeFromString(serString, input2, mode)
+ }, { message ->
+ if (mode in setOf(JsonTestingMode.STREAMING, JsonTestingMode.TREE))
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 13: Expected quotation mark '\"', but had '}' instead at path: \$"
+ )
+ else
+ assertContains(message, "Unexpected EOF at path: \$.boxed")
+ })
+
+ checkSerializationException({
+ default.decodeFromString(serString, input3, mode)
+ }, { message ->
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 9: Expected colon ':', but had 's' instead at path: \$"
+ )
+ })
+ }
+
+ @Test
+ fun testUnquoted() = parametrizedTest { mode ->
+ val input1 = """{boxed:str}"""
+ val input2 = """{"boxed":str}"""
+ val ser = serializer<Box<String>>()
+
+ checkSerializationException({
+ default.decodeFromString(ser, input1, mode)
+ }, { message ->
+ assertContains(
+ message,
+ """Unexpected JSON token at offset 1: Expected quotation mark '"', but had 'b' instead at path: ${'$'}"""
+ )
+ })
+
+ checkSerializationException({
+ default.decodeFromString(ser, input2, mode)
+ }, { message ->
+ if (mode == JsonTestingMode.TREE) assertContains(
+ message,
+ """String literal for key 'boxed' should be quoted."""
+ )
+ else assertContains(
+ message,
+ """Unexpected JSON token at offset 9: Expected quotation mark '"', but had 's' instead at path: ${'$'}.boxed"""
+ )
+ })
+ }
+
+ @Test
+ fun testNullLiteralForNotNull() = parametrizedTest { mode ->
+ val input = """{"boxed":null}"""
+ val ser = serializer<Box<String>>()
+ checkSerializationException({
+ default.decodeFromString(ser, input, mode)
+ }, { message ->
+ if (mode == JsonTestingMode.TREE)
+ assertContains(message, "Unexpected 'null' literal when non-nullable string was expected")
+ else
+ assertContains(
+ message,
+ "Unexpected JSON token at offset 9: Expected string literal but 'null' literal was found at path: \$.boxed"
+ )
+ })
+ }
+
+ @Test
+ fun testEof() = parametrizedTest { mode ->
+ val input = """{"boxed":"""
+ checkSerializationException({
+ default.decodeFromString<Box<String>>(input, mode)
+ }, { message ->
+ if (mode == JsonTestingMode.TREE)
+ assertContains(message, "Cannot read Json element because of unexpected end of the input at path: $")
+ else
+ assertContains(message, "Expected quotation mark '\"', but had 'EOF' instead at path: \$.boxed")
+
+ })
+
+ }
+
+ private fun checkSerializationException(action: () -> Unit, assertions: SerializationException.(String) -> Unit) {
+ val e = assertFailsWith(SerializationException::class, action)
+ assertNotNull(e.message)
+ e.assertions(e.message!!)
+ }
+
+}