Skip to content

Commit 27e9291

Browse files
committed
Correct number type check for JS. Add clues for tests
1 parent ac152c4 commit 27e9291

File tree

5 files changed

+97
-52
lines changed
  • json-schema-validator-objects

5 files changed

+97
-52
lines changed

json-schema-validator-objects/build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ kotlin {
7272
}
7373
}
7474

75+
val noJsMain by creating {
76+
dependsOn(commonMain.get())
77+
}
78+
79+
jvmMain {
80+
dependsOn(noJsMain)
81+
}
82+
83+
wasmJsMain {
84+
dependsOn(noJsMain)
85+
}
86+
87+
nativeMain {
88+
dependsOn(noJsMain)
89+
}
90+
7591
commonTest {
7692
dependencies {
7793
implementation(libs.kotest.assertions.core)

json-schema-validator-objects/src/commonMain/kotlin/io/github/optimumcode/json/schema/objects/wrapper/Wrappers.kt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,23 @@ public fun wrapAsElement(
6565
}
6666
}
6767

68+
/**
69+
* Returns `true` if the [value] is an integer ([Byte], [Short], [Int], [Long]).
70+
* Otherwise, returns `false`.
71+
*
72+
* Required because JS platform matches all types except Long with `number` type.
73+
* Refer to the [KT-18177](https://youtrack.jetbrains.com/issue/KT-18177/) for additional details
74+
*/
75+
internal expect fun isInteger(value: Number): Boolean
76+
6877
private fun numberToSupportedTypeOrOriginal(obj: Any): Any =
69-
when (obj) {
70-
!is Number -> obj
71-
is Double, is Long -> obj
72-
is Byte, is Short, is Int -> obj.toLong()
73-
is Float -> obj.toDoubleSafe()
78+
when {
79+
obj !is Number -> obj
80+
obj is Long -> obj
81+
isInteger(obj) -> obj.toLong()
82+
obj is Double -> obj
83+
// due to KT-18177 this won't be invoked for Float on JS platform
84+
obj is Float -> obj.toDoubleSafe()
7485
else -> error("unsupported number type: ${obj::class}")
7586
}
7687

json-schema-validator-objects/src/commonTest/kotlin/io/github/optimumcode/json/schema/objects/wrapper/WrappersTest.kt

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ package io.github.optimumcode.json.schema.objects.wrapper
33
import io.github.optimumcode.json.schema.model.ArrayElement
44
import io.github.optimumcode.json.schema.model.ObjectElement
55
import io.github.optimumcode.json.schema.model.PrimitiveElement
6+
import io.kotest.assertions.asClue
67
import io.kotest.assertions.assertSoftly
78
import io.kotest.assertions.throwables.shouldNotThrowAny
89
import io.kotest.assertions.throwables.shouldThrow
10+
import io.kotest.core.Platform
11+
import io.kotest.core.platform
912
import io.kotest.core.spec.style.FunSpec
13+
import io.kotest.core.test.Enabled
1014
import io.kotest.matchers.booleans.shouldBeFalse
1115
import io.kotest.matchers.booleans.shouldBeTrue
1216
import io.kotest.matchers.collections.shouldContainExactly
@@ -44,57 +48,57 @@ class WrappersTest : FunSpec() {
4448
}
4549

4650
test("primitive wrapper for null") {
47-
wrapAsElement(null).shouldBeInstanceOf<PrimitiveElement> {
51+
wrapAsElement(null).shouldBeInstanceOf<PrimitiveElement> { el ->
4852
assertSoftly {
49-
it.isString.shouldBeFalse()
50-
it.isNumber.shouldBeFalse()
51-
it.isBoolean.shouldBeFalse()
52-
it.isNull.shouldBeTrue()
53-
it.content shouldBe "null"
54-
it.longOrNull.shouldBeNull()
55-
it.doubleOrNull.shouldBeNull()
53+
"isString".asClue { el.isString.shouldBeFalse() }
54+
"isNumber".asClue { el.isNumber.shouldBeFalse() }
55+
"isBoolean".asClue { el.isBoolean.shouldBeFalse() }
56+
"isNull".asClue { el.isNull.shouldBeTrue() }
57+
"content".asClue { el.content shouldBe "null" }
58+
"longOrNull".asClue { el.longOrNull.shouldBeNull() }
59+
"doubleOrNull".asClue { el.doubleOrNull.shouldBeNull() }
5660
}
5761
}
5862
}
5963

6064
test("primitive wrapper for boolean") {
61-
wrapAsElement(true).shouldBeInstanceOf<PrimitiveElement> {
65+
wrapAsElement(true).shouldBeInstanceOf<PrimitiveElement> { el ->
6266
assertSoftly {
63-
it.isString.shouldBeFalse()
64-
it.isNumber.shouldBeFalse()
65-
it.isBoolean.shouldBeTrue()
66-
it.isNull.shouldBeFalse()
67-
it.content shouldBe "true"
68-
it.longOrNull.shouldBeNull()
69-
it.doubleOrNull.shouldBeNull()
67+
"isString".asClue { el.isString.shouldBeFalse() }
68+
"isNumber".asClue { el.isNumber.shouldBeFalse() }
69+
"isBoolean".asClue { el.isBoolean.shouldBeTrue() }
70+
"isNull".asClue { el.isNull.shouldBeFalse() }
71+
"content".asClue { el.content shouldBe "true" }
72+
"longOrNull".asClue { el.longOrNull.shouldBeNull() }
73+
"doubleOrNull".asClue { el.doubleOrNull.shouldBeNull() }
7074
}
7175
}
7276
}
7377

7478
test("primitive wrapper for number") {
75-
wrapAsElement(42).shouldBeInstanceOf<PrimitiveElement> {
79+
wrapAsElement(42).shouldBeInstanceOf<PrimitiveElement> { el ->
7680
assertSoftly {
77-
it.isString.shouldBeFalse()
78-
it.isNumber.shouldBeTrue()
79-
it.isBoolean.shouldBeFalse()
80-
it.isNull.shouldBeFalse()
81-
it.content shouldBe "42"
82-
it.longOrNull shouldBe 42L
83-
it.doubleOrNull.shouldBeNull()
81+
"isString".asClue { el.isString.shouldBeFalse() }
82+
"isNumber".asClue { el.isNumber.shouldBeTrue() }
83+
"isBoolean".asClue { el.isBoolean.shouldBeFalse() }
84+
"isNull".asClue { el.isNull.shouldBeFalse() }
85+
"content".asClue { el.content shouldBe "42" }
86+
"longOrNull".asClue { el.longOrNull shouldBe 42L }
87+
"doubleOrNull".asClue { el.doubleOrNull.shouldBeNull() }
8488
}
8589
}
8690
}
8791

8892
test("primitive wrapper for string") {
89-
wrapAsElement("42").shouldBeInstanceOf<PrimitiveElement> {
93+
wrapAsElement("42").shouldBeInstanceOf<PrimitiveElement> { el ->
9094
assertSoftly {
91-
it.isString.shouldBeTrue()
92-
it.isNumber.shouldBeFalse()
93-
it.isBoolean.shouldBeFalse()
94-
it.isNull.shouldBeFalse()
95-
it.content shouldBe "42"
96-
it.longOrNull.shouldBeNull()
97-
it.doubleOrNull.shouldBeNull()
95+
"isString".asClue { el.isString.shouldBeTrue() }
96+
"isNumber".asClue { el.isNumber.shouldBeFalse() }
97+
"isBoolean".asClue { el.isBoolean.shouldBeFalse() }
98+
"isNull".asClue { el.isNull.shouldBeFalse() }
99+
"content".asClue { el.content shouldBe "42" }
100+
"longOrNull".asClue { el.longOrNull.shouldBeNull() }
101+
"doubleOrNull".asClue { el.doubleOrNull.shouldBeNull() }
98102
}
99103
}
100104
}
@@ -212,24 +216,32 @@ class WrappersTest : FunSpec() {
212216
}
213217
}
214218

215-
test("other number implementations are not allowed") {
216-
shouldThrow<IllegalStateException> {
217-
wrapAsElement(
218-
object : Number() {
219-
override fun toByte(): Byte = TODO("Not yet implemented")
219+
test("other number implementations are not allowed")
220+
.config(
221+
enabledOrReasonIf = {
222+
when (platform) {
223+
Platform.JS -> Enabled.disabled("you cannot create a class that is a Number on JS")
224+
else -> Enabled.enabled
225+
}
226+
},
227+
) {
228+
shouldThrow<IllegalStateException> {
229+
wrapAsElement(MyNumber())
230+
}.message.shouldStartWith("unsupported number type:")
231+
}
232+
}
233+
}
220234

221-
override fun toDouble(): Double = TODO("Not yet implemented")
235+
private class MyNumber : Number() {
236+
override fun toByte(): Byte = TODO("Not yet implemented")
222237

223-
override fun toFloat(): Float = TODO("Not yet implemented")
238+
override fun toDouble(): Double = TODO("Not yet implemented")
224239

225-
override fun toInt(): Int = TODO("Not yet implemented")
240+
override fun toFloat(): Float = TODO("Not yet implemented")
226241

227-
override fun toLong(): Long = TODO("Not yet implemented")
242+
override fun toInt(): Int = TODO("Not yet implemented")
228243

229-
override fun toShort(): Short = TODO("Not yet implemented")
230-
},
231-
)
232-
}.message.shouldStartWith("unsupported number type:")
233-
}
234-
}
244+
override fun toLong(): Long = TODO("Not yet implemented")
245+
246+
override fun toShort(): Short = TODO("Not yet implemented")
235247
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.github.optimumcode.json.schema.objects.wrapper
2+
3+
internal actual fun isInteger(value: Number): Boolean = js("return Number.isInteger(value)")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.github.optimumcode.json.schema.objects.wrapper
2+
3+
internal actual fun isInteger(value: Number): Boolean = value is Byte || value is Short || value is Int || value is Long

0 commit comments

Comments
 (0)