Skip to content

Commit 536c687

Browse files
author
Oleg
committed
Add min properties assertion
1 parent c17b183 commit 536c687

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

src/commonMain/kotlin/smirnov/oleg/json/schema/internal/SchemaLoader.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import smirnov.oleg.json.schema.internal.factories.number.MaximumAssertionFactor
2222
import smirnov.oleg.json.schema.internal.factories.number.MinimumAssertionFactory
2323
import smirnov.oleg.json.schema.internal.factories.number.MultipleOfAssertionFactory
2424
import smirnov.oleg.json.schema.internal.factories.`object`.MaxPropertiesAssertionFactory
25+
import smirnov.oleg.json.schema.internal.factories.`object`.MinPropertiesAssertionFactory
2526
import smirnov.oleg.json.schema.internal.factories.string.MaxLengthAssertionFactory
2627
import smirnov.oleg.json.schema.internal.factories.string.MinLengthAssertionFactory
2728
import smirnov.oleg.json.schema.internal.factories.string.PatternAssertionFactory
@@ -44,6 +45,7 @@ private val factories: List<AssertionFactory> = listOf(
4445
UniqueItemsAssertionFactory,
4546
ContainsAssertionFactory,
4647
MaxPropertiesAssertionFactory,
48+
MinPropertiesAssertionFactory,
4749
)
4850

4951
class SchemaLoader {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package smirnov.oleg.json.schema.internal.factories.`object`
2+
3+
import kotlinx.serialization.json.JsonElement
4+
import kotlinx.serialization.json.JsonPrimitive
5+
import kotlinx.serialization.json.intOrNull
6+
import smirnov.oleg.json.schema.internal.JsonSchemaAssertion
7+
import smirnov.oleg.json.schema.internal.LoadingContext
8+
import smirnov.oleg.json.schema.internal.factories.AbstractAssertionFactory
9+
10+
@Suppress("unused")
11+
internal object MinPropertiesAssertionFactory : AbstractAssertionFactory("minProperties") {
12+
override fun createFromProperty(element: JsonElement, context: LoadingContext): JsonSchemaAssertion {
13+
require(element is JsonPrimitive && !element.isString) { "$property must be an integer" }
14+
val minPropertiesValue = requireNotNull(element.intOrNull) { "$property must be a valid integer" }
15+
require(minPropertiesValue >= 0) { "$property must be a non-negative integer" }
16+
return PropertiesNumberAssertion(
17+
context.schemaPath,
18+
minPropertiesValue,
19+
errorMessage = "must be greater or equal to",
20+
) { a, b -> a >= b }
21+
}
22+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package smirnov.oleg.json.schema.assertions.`object`
2+
3+
import io.kotest.assertions.asClue
4+
import io.kotest.assertions.throwables.shouldThrow
5+
import io.kotest.core.spec.style.FunSpec
6+
import io.kotest.matchers.collections.shouldContainExactly
7+
import io.kotest.matchers.collections.shouldHaveSize
8+
import io.kotest.matchers.shouldBe
9+
import kotlinx.serialization.json.JsonNull
10+
import kotlinx.serialization.json.JsonPrimitive
11+
import kotlinx.serialization.json.buildJsonArray
12+
import kotlinx.serialization.json.buildJsonObject
13+
import smirnov.oleg.json.pointer.JsonPointer
14+
import smirnov.oleg.json.schema.JsonSchema
15+
import smirnov.oleg.json.schema.KEY
16+
import smirnov.oleg.json.schema.ValidationError
17+
18+
@Suppress("unused")
19+
class JsonSchemaMinPropertiesValidationTest : FunSpec() {
20+
init {
21+
val schema = JsonSchema.fromDescription(
22+
"""
23+
{
24+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
25+
"minProperties": 2
26+
}
27+
""".trimIndent()
28+
)
29+
30+
listOf(
31+
buildJsonObject {
32+
put("test1", JsonPrimitive("a"))
33+
put("test2", JsonPrimitive("b"))
34+
},
35+
buildJsonObject {
36+
put("test1", JsonPrimitive("a"))
37+
put("test2", JsonPrimitive("b"))
38+
put("test3", JsonPrimitive("c"))
39+
},
40+
).forEach {
41+
test("object with ${it.size} properties passes validation") {
42+
val errors = mutableListOf<ValidationError>()
43+
val valid = schema.validate(it, errors::add)
44+
it.asClue {
45+
valid shouldBe true
46+
errors shouldHaveSize 0
47+
}
48+
}
49+
}
50+
51+
listOf(
52+
buildJsonObject { },
53+
buildJsonObject {
54+
put("test1", JsonPrimitive("a"))
55+
},
56+
).forEach {
57+
test("object with ${it.size} properties fails validation") {
58+
59+
val errors = mutableListOf<ValidationError>()
60+
val valid = schema.validate(it, errors::add)
61+
it.asClue {
62+
valid shouldBe false
63+
errors.shouldContainExactly(
64+
ValidationError(
65+
schemaPath = JsonPointer("/minProperties"),
66+
objectPath = JsonPointer.ROOT,
67+
message = "number of properties must be greater or equal to 2",
68+
)
69+
)
70+
}
71+
}
72+
}
73+
74+
test("reports negative value") {
75+
shouldThrow<IllegalArgumentException> {
76+
JsonSchema.fromDescription(
77+
"""
78+
{
79+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
80+
"minProperties": -1
81+
}
82+
""".trimIndent()
83+
)
84+
}.message shouldBe "minProperties must be a non-negative integer"
85+
}
86+
87+
listOf(
88+
JsonPrimitive(42.5),
89+
JsonPrimitive(Int.MAX_VALUE.toLong() + 1),
90+
JsonPrimitive(true),
91+
JsonNull,
92+
).forEach {
93+
test("reports not valid integer value $it") {
94+
shouldThrow<IllegalArgumentException> {
95+
JsonSchema.fromDescription(
96+
"""
97+
{
98+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
99+
"minProperties": $it
100+
}
101+
""".trimIndent()
102+
)
103+
}.message shouldBe "minProperties must be a valid integer"
104+
}
105+
}
106+
107+
listOf(
108+
JsonPrimitive("test"),
109+
buildJsonObject { },
110+
buildJsonArray { },
111+
).forEach {
112+
test("reports not integer value $it") {
113+
shouldThrow<IllegalArgumentException> {
114+
JsonSchema.fromDescription(
115+
"""
116+
{
117+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
118+
"minProperties": $it
119+
}
120+
""".trimIndent()
121+
)
122+
}.message shouldBe "minProperties must be an integer"
123+
}
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)