• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.json
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.test.assertFailsWithSerial
9 import kotlin.test.*
10 
11 class JsonCoerceInputValuesTest : JsonTestBase() {
12     @Serializable
13     data class WithBoolean(val b: Boolean = false)
14 
15     @Serializable
16     data class WithEnum(val e: SampleEnum = SampleEnum.OptionC)
17 
18     @Serializable
19     data class MultipleValues(
20         val data: StringData,
21         val data2: IntData = IntData(0),
22         val i: Int = 42,
23         val e: SampleEnum = SampleEnum.OptionA,
24         val foo: String
25     )
26 
27     @Serializable
28     data class NullableEnumHolder(
29         val enum: SampleEnum?
30     )
31 
32     @Serializable
33     class Uncoercable(
34         val s: String
35     )
36 
37     @Serializable
38     class UncoercableEnum(
39         val e: SampleEnum
40     )
41 
42     @Serializable
43     data class NullableEnumWithDefault(
44         val e: SampleEnum? = SampleEnum.OptionC
45     )
46 
47     val json = Json {
48         coerceInputValues = true
49         isLenient = true
50     }
51 
52     private val jsonNonLenient = Json(json) { isLenient = false }
53 
54 
55     private fun <T> doTest(inputs: List<String>, expected: T, serializer: KSerializer<T>) {
56         for (input in inputs) {
57             parametrizedTest(json) {
58                 assertEquals(expected, decodeFromString(serializer, input), "Failed on input: $input")
59             }
60         }
61     }
62 
63     @Test
64     fun testUseDefaultOnNonNullableBoolean() = doTest(
65         listOf(
66             """{"b":false}""",
67             """{"b":null}""",
68             """{}""",
69         ),
70         WithBoolean(),
71         WithBoolean.serializer()
72     )
73 
74     @Test
75     fun testUseDefaultOnUnknownEnum() {
76         doTest(
77             listOf(
78                 """{"e":unknown_value}""",
79                 """{"e":"unknown_value"}""",
80                 """{"e":null}""",
81                 """{}""",
82             ),
83             WithEnum(),
84             WithEnum.serializer()
85         )
86         assertFailsWithSerial("JsonDecodingException") {
87             json.decodeFromString(WithEnum.serializer(), """{"e":{"x":"definitely not a valid enum value"}}""")
88         }
89         assertFailsWithSerial("JsonDecodingException") { // test user still sees exception on missing quotes
90             jsonNonLenient.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""")
91         }
92     }
93 
94     @Test
95     fun testUseDefaultInMultipleCases() {
96         val testData = mapOf(
97             """{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}""" to MultipleValues(
98                 StringData("foo"),
99                 foo = "bar"
100             ),
101             """{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}""" to MultipleValues(
102                 StringData(
103                     "foo"
104                 ), IntData(42), foo = "bar"
105             ),
106             """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}""" to MultipleValues(
107                 StringData("foo"),
108                 IntData(42),
109                 i = 0,
110                 foo = "bar"
111             ),
112             """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}""" to MultipleValues(
113                 StringData("foo"),
114                 IntData(42),
115                 i = 0,
116                 e = SampleEnum.OptionC,
117                 foo = "bar"
118             ),
119         )
120         for ((input, expected) in testData) {
121             assertEquals(expected, json.decodeFromString(MultipleValues.serializer(), input), "Failed on input: $input")
122         }
123     }
124 
125     @Test
126     fun testNullSupportForEnums() = parametrizedTest(json) {
127         var decoded = decodeFromString<NullableEnumHolder>("""{"enum": null}""")
128         assertNull(decoded.enum)
129 
130         decoded = decodeFromString<NullableEnumHolder>("""{"enum": OptionA}""")
131         assertEquals(SampleEnum.OptionA, decoded.enum)
132     }
133 
134     @Test
135     fun propertiesWithoutDefaultValuesDoNotChangeErrorMsg() {
136         val json2 = Json(json) { coerceInputValues = false }
137         parametrizedTest { mode ->
138             val e1 = assertFailsWith<SerializationException>() { json.decodeFromString<Uncoercable>("""{"s":null}""", mode) }
139             val e2 = assertFailsWith<SerializationException>() { json2.decodeFromString<Uncoercable>("""{"s":null}""", mode) }
140             assertEquals(e2.message, e1.message)
141         }
142     }
143 
144     @Test
145     fun propertiesWithoutDefaultValuesDoNotChangeErrorMsgEnum() {
146         val json2 = Json(json) { coerceInputValues = false }
147         parametrizedTest { mode ->
148             val e1 = assertFailsWith<SerializationException> { json.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) }
149             val e2 = assertFailsWith<SerializationException> { json2.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) }
150             assertEquals(e2.message, e1.message)
151         }
152     }
153 
154     @Test
155     fun testNullableEnumWithoutDefault() {
156         val j = Json(json) { explicitNulls = false }
157         parametrizedTest { mode ->
158             assertEquals(NullableEnumHolder(null), j.decodeFromString("{}"))
159             assertEquals(NullableEnumHolder(null), j.decodeFromString("""{"enum":"incorrect"}"""))
160         }
161     }
162 
163     @Test
164     fun testNullableEnumWithoutDefaultDoesNotCoerceExplicitly() {
165         val j = Json(json) { explicitNulls = true }
166         parametrizedTest { mode ->
167             assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("{}") }
168             assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("""{"enum":"incorrect"}""") }
169         }
170     }
171 
172     @Test
173     fun testNullableEnumWithDefault() {
174         val j = Json(json) { explicitNulls = false }
175         parametrizedTest { mode ->
176             assertEquals(NullableEnumWithDefault(), j.decodeFromString("{}"))
177             assertEquals(NullableEnumWithDefault(), j.decodeFromString("""{"e":"incorrect"}"""))
178             assertEquals(NullableEnumWithDefault(null), j.decodeFromString("""{"e":null}"""))
179         }
180     }
181 }
182