Skip to content

Commit 9fb5ac4

Browse files
author
Oleg
committed
Add not assertion
1 parent 7953849 commit 9fb5ac4

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-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
@@ -16,6 +16,7 @@ import smirnov.oleg.json.schema.internal.factories.array.UniqueItemsAssertionFac
1616
import smirnov.oleg.json.schema.internal.factories.condition.AllOfAssertionFactory
1717
import smirnov.oleg.json.schema.internal.factories.condition.AnyOfAssertionFactory
1818
import smirnov.oleg.json.schema.internal.factories.condition.IfThenElseAssertionFactory
19+
import smirnov.oleg.json.schema.internal.factories.condition.NotAssertionFactory
1920
import smirnov.oleg.json.schema.internal.factories.general.ConstAssertionFactory
2021
import smirnov.oleg.json.schema.internal.factories.general.EnumAssertionFactory
2122
import smirnov.oleg.json.schema.internal.factories.general.TypeAssertionFactory
@@ -60,6 +61,7 @@ private val factories: List<AssertionFactory> = listOf(
6061
IfThenElseAssertionFactory,
6162
AllOfAssertionFactory,
6263
AnyOfAssertionFactory,
64+
NotAssertionFactory,
6365
)
6466

6567
class SchemaLoader {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package smirnov.oleg.json.schema.internal.factories.condition
2+
3+
import kotlinx.serialization.json.JsonElement
4+
import smirnov.oleg.json.pointer.JsonPointer
5+
import smirnov.oleg.json.schema.ErrorCollector
6+
import smirnov.oleg.json.schema.ValidationError
7+
import smirnov.oleg.json.schema.internal.AssertionContext
8+
import smirnov.oleg.json.schema.internal.JsonSchemaAssertion
9+
import smirnov.oleg.json.schema.internal.LoadingContext
10+
import smirnov.oleg.json.schema.internal.factories.AbstractAssertionFactory
11+
12+
@Suppress("unused")
13+
internal object NotAssertionFactory : AbstractAssertionFactory("not") {
14+
override fun createFromProperty(element: JsonElement, context: LoadingContext): JsonSchemaAssertion {
15+
require(context.isJsonSchema(element)) { "$property must be a valid JSON schema" }
16+
val assertion = context.schemaFrom(element)
17+
return NotAssertion(context.schemaPath, assertion)
18+
}
19+
}
20+
21+
private class NotAssertion(
22+
private val path: JsonPointer,
23+
private val delegate: JsonSchemaAssertion,
24+
) : JsonSchemaAssertion {
25+
override fun validate(element: JsonElement, context: AssertionContext, errorCollector: ErrorCollector): Boolean {
26+
if (!delegate.validate(element, context, ErrorCollector.EMPTY)) {
27+
return true
28+
}
29+
errorCollector.onError(
30+
ValidationError(
31+
schemaPath = path,
32+
objectPath = context.objectPath,
33+
message = "element must not be valid against child JSON schema but was",
34+
)
35+
)
36+
return false
37+
}
38+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package smirnov.oleg.json.schema.assertions.condition
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 JsonSchemaNotValidationTest : FunSpec() {
20+
init {
21+
JsonSchema.fromDescription(
22+
"""
23+
{
24+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
25+
"not": {
26+
"type": "object"
27+
}
28+
}
29+
""".trimIndent()
30+
).also { schema ->
31+
listOf(
32+
JsonPrimitive(42),
33+
JsonPrimitive(42.5),
34+
JsonPrimitive("test"),
35+
JsonPrimitive(true),
36+
JsonNull,
37+
buildJsonArray { },
38+
).forEach {
39+
test("not an object $it passes validation") {
40+
val errors = mutableListOf<ValidationError>()
41+
val valid = schema.validate(it, errors::add)
42+
43+
valid shouldBe true
44+
errors shouldHaveSize 0
45+
}
46+
}
47+
48+
test("object fails validation") {
49+
val jsonObject = buildJsonObject { }
50+
val errors = mutableListOf<ValidationError>()
51+
val valid = schema.validate(jsonObject, errors::add)
52+
53+
jsonObject.asClue {
54+
valid shouldBe false
55+
errors.shouldContainExactly(
56+
ValidationError(
57+
schemaPath = JsonPointer("/not"),
58+
objectPath = JsonPointer.ROOT,
59+
message = "element must not be valid against child JSON schema but was",
60+
),
61+
)
62+
}
63+
}
64+
}
65+
66+
test("reports if value is not a valid JSON schema") {
67+
shouldThrow<IllegalArgumentException> {
68+
JsonSchema.fromDescription(
69+
"""
70+
{
71+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
72+
"not": 42
73+
}
74+
""".trimIndent()
75+
)
76+
}.message shouldBe "not must be a valid JSON schema"
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)