@@ -20,7 +20,13 @@ package org.jetbrains.kotlinx.spark.api/*-
2020import ch.tutteli.atrium.api.fluent.en_GB.*
2121import ch.tutteli.atrium.api.verbs.expect
2222import ch.tutteli.atrium.creating.Expect
23+ import ch.tutteli.atrium.logic._logic
24+ import ch.tutteli.atrium.logic._logicAppend
25+ import ch.tutteli.atrium.logic.collect
2326import io.kotest.core.spec.style.ShouldSpec
27+ import io.kotest.matchers.types.shouldBeTypeOf
28+ import org.apache.spark.sql.types.ArrayType
29+ import org.apache.spark.sql.types.IntegerType
2430import org.jetbrains.kotlinx.spark.api.struct.model.DataType.StructType
2531import org.jetbrains.kotlinx.spark.api.struct.model.DataType.TypeName
2632import org.jetbrains.kotlinx.spark.api.struct.model.ElementType.ComplexElement
@@ -29,7 +35,6 @@ import org.jetbrains.kotlinx.spark.api.struct.model.Struct
2935import org.jetbrains.kotlinx.spark.api.struct.model.StructField
3036import kotlin.reflect.typeOf
3137
32-
3338@OptIn(ExperimentalStdlibApi ::class )
3439class TypeInferenceTest : ShouldSpec ({
3540 context("org.jetbrains.spark.api.org.jetbrains.spark.api.schema") {
@@ -39,21 +44,21 @@ class TypeInferenceTest : ShouldSpec({
3944 val struct = Struct .fromJson(schema(typeOf<Pair <String , Test <Int >>>()).prettyJson())!!
4045 should("contain correct typings") {
4146 expect(struct.fields).notToBeNull().contains.inAnyOrder.only.entries(
42- hasField("first", "string"),
43- hasStruct("second",
44- hasField("vala", "integer"),
45- hasStruct("tripl1",
46- hasField("first", "integer"),
47- hasStruct("second",
48- hasField("vala2", "long"),
49- hasStruct("para2",
50- hasField("first", "long"),
51- hasField("second", "string")
47+ hasField("first", "string"),
48+ hasStruct("second",
49+ hasField("vala", "integer"),
50+ hasStruct("tripl1",
51+ hasField("first", "integer"),
52+ hasStruct("second",
53+ hasField("vala2", "long"),
54+ hasStruct("para2",
55+ hasField("first", "long"),
56+ hasField("second", "string")
57+ )
58+ ),
59+ hasField("third", "integer")
5260 )
53- ),
54- hasField("third", "integer")
5561 )
56- )
5762 )
5863 }
5964 }
@@ -65,23 +70,23 @@ class TypeInferenceTest : ShouldSpec({
6570 val struct = Struct .fromJson(schema(typeOf<Pair <String , Test <Int >>>()).prettyJson())!!
6671 should("contain correct typings") {
6772 expect(struct.fields).notToBeNull().contains.inAnyOrder.only.entries(
68- hasField("first", "string"),
69- hasStruct("second",
70- hasField("vala", "integer"),
71- hasStruct("tripl1",
72- hasField("first", "integer"),
73- hasStruct("second",
74- hasField("vala2", "long"),
75- hasStruct("para2",
76- hasField("first", "long"),
77- hasStruct("second",
78- hasField("vala3", "double")
79- )
73+ hasField("first", "string"),
74+ hasStruct("second",
75+ hasField("vala", "integer"),
76+ hasStruct("tripl1",
77+ hasField("first", "integer"),
78+ hasStruct("second",
79+ hasField("vala2", "long"),
80+ hasStruct("para2",
81+ hasField("first", "long"),
82+ hasStruct("second",
83+ hasField("vala3", "double")
84+ )
85+ )
86+ ),
87+ hasField("third", "integer")
8088 )
81- ),
82- hasField("third", "integer")
8389 )
84- )
8590 )
8691 }
8792 }
@@ -91,9 +96,9 @@ class TypeInferenceTest : ShouldSpec({
9196 val struct = Struct .fromJson(schema(typeOf<Test >()).prettyJson())!!
9297 should("return correct types too") {
9398 expect(struct.fields).notToBeNull().contains.inAnyOrder.only.entries(
94- hasField("a", "string"),
95- hasField("b", "integer"),
96- hasField("c", "double")
99+ hasField("a", "string"),
100+ hasField("b", "integer"),
101+ hasField("c", "double")
97102 )
98103 }
99104 }
@@ -113,8 +118,8 @@ class TypeInferenceTest : ShouldSpec({
113118 isOfType("array")
114119 feature { f(it::elementType) }.notToBeNull().isA<ComplexElement > {
115120 feature { f(it.value::fields) }.notToBeNull().contains.inAnyOrder.only.entries(
116- hasField("first", "integer"),
117- hasField("second", "long")
121+ hasField("first", "integer"),
122+ hasField("second", "long")
118123 )
119124 }
120125 }
@@ -129,7 +134,7 @@ class TypeInferenceTest : ShouldSpec({
129134 isOfType("array")
130135 feature { f(it::elementType) }.notToBeNull().isA<ComplexElement > {
131136 feature { f(it.value::fields) }.notToBeNull().contains.inAnyOrder.only.entries(
132- hasField("e", "string")
137+ hasField("e", "string")
133138 )
134139 }
135140 }
@@ -166,15 +171,44 @@ class TypeInferenceTest : ShouldSpec({
166171 }
167172 }
168173 }
169- context("data class with props in order lon → lat ") {
170- data class Test (val lon: Double , val lat: Double )
174+ context("data class with nullable list inside ") {
175+ data class Sample (val optionList: List < Int > ? )
171176
172- val struct = Struct .fromJson(schema(typeOf<Test >()).prettyJson())!!
173- should("Not change order of fields") {
174- expect(struct.fields).notToBeNull().containsExactly(
175- hasField("lon", "double"),
176- hasField("lat", "double")
177- )
177+ val struct = Struct .fromJson(schema(typeOf<Sample >()).prettyJson())!!
178+ should("show that list is nullable and element is not") {
179+ expect(struct)
180+ .feature("some", { fields }) {
181+ notToBeNull().contains.inOrder.only.entry {
182+ this
183+ .feature("field name", { name }) { toBe("optionList") }
184+ .feature("optionList is nullable", { nullable }) { toBe(true) }
185+ .feature("optionList", { type }) {
186+ this
187+ .isA<StructType >()
188+ .feature("element type of optionList", { value.elementType }) { toBe(SimpleElement ("integer")) }
189+ .feature("optionList contains null", { value.containsNull }) { toBe(false) }
190+ .feature("optionList type", { value }) { isOfType("array") }
191+ }
192+ }
193+ }
194+ }
195+ should("generate valid serializer schema") {
196+ expect(encoder<Sample >().schema()) {
197+ this
198+ .feature("data type", { this.fields()?.toList() }) {
199+ this.notToBeNull().contains.inOrder.only.entry {
200+ this
201+ .feature("element name", { name() }) { toBe("optionList") }
202+ .feature("field type", { dataType() }) {
203+ this
204+ .isA<ArrayType >()
205+ .feature("element type", { elementType() }) { isA<IntegerType >() }
206+ .feature("element nullable", { containsNull() }) { toBe(false) }
207+ }
208+ .feature("optionList nullable", { nullable() }) { toBe(true) }
209+ }
210+ }
211+ }
178212 }
179213 }
180214})
@@ -184,15 +218,15 @@ private fun Expect<Struct>.isOfType(type: String) {
184218}
185219
186220private fun hasStruct (
187- name : String ,
188- expectedField : Expect <StructField >.() -> Unit ,
189- vararg expectedFields : Expect <StructField >.() -> Unit ,
221+ name : String ,
222+ expectedField : Expect <StructField >.() -> Unit ,
223+ vararg expectedFields : Expect <StructField >.() -> Unit ,
190224): Expect <StructField >.() -> Unit {
191225 return {
192226 feature { f(it::name) }.toBe(name)
193227 feature { f(it::type) }.isA<StructType > {
194228 feature { f(it.value::fields) }.notToBeNull().contains.inAnyOrder.only.entries(expectedField,
195- * expectedFields)
229+ * expectedFields)
196230 }
197231 }
198232}
0 commit comments