Skip to content

Commit

Permalink
testbreaker
Browse files Browse the repository at this point in the history
  • Loading branch information
erosb committed Dec 1, 2024
1 parent ac42dfe commit 9e5097b
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 30 deletions.
17 changes: 17 additions & 0 deletions src/main/kotlin/com/github/erosb/jsonsKema/DynamicPath.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.erosb.jsonsKema

class DynamicPath {

private val path = mutableListOf<String>()

fun <P> inSegmentPath(seg: String, cb: () -> P?): P? {
path.add(seg)
val rval = cb()
path.removeLast()
return rval
}

fun asPointer(): JsonPointer = JsonPointer(path)


}
3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/MaxItems.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ internal val maxItemsLoader: KeywordLoader = { ctx ->

data class MaxItemsValidationFailure(
override val schema: MaxItemsSchema,
override val instance: IJsonArray<*>
override val instance: IJsonArray<*>,
val dynamicPath: JsonPointer
) : ValidationFailure("expected maximum items: ${schema.maxItems}, found ${instance.length()}", schema, instance, Keyword.MAX_ITEMS)
3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/MinItems.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ internal val minItemsLoader: KeywordLoader = { ctx ->

data class MinItemsValidationFailure(
override val schema: MinItemsSchema,
override val instance: IJsonArray<*>
override val instance: IJsonArray<*>,
val dynamicPath: JsonPointer
) : ValidationFailure("expected minimum items: ${schema.minItems}, found only ${instance.length()}", schema, instance, Keyword.MIN_ITEMS)
13 changes: 6 additions & 7 deletions src/main/kotlin/com/github/erosb/jsonsKema/SchemaVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ abstract class SchemaVisitor<P> {

private val dynamicScope = mutableListOf<CompositeSchema>()

protected var dynamicPath: JsonPointer = JsonPointer()

private fun findSubschemaByDynamicAnchor(scope: CompositeSchema, lookupValue: String): Schema? {
if (scope.dynamicAnchor == lookupValue) {
return scope
Expand Down Expand Up @@ -83,14 +81,15 @@ abstract class SchemaVisitor<P> {
return result
}


private var dynamicPath: DynamicPath = DynamicPath()

protected fun inPathSegment(seg: String, cb: () -> P?): P? {
val orig = dynamicPath
dynamicPath += seg
val rval = cb()
dynamicPath = orig
return rval
return dynamicPath.inSegmentPath(seg, cb)
}

protected fun dynamicPath(): JsonPointer = dynamicPath.asPointer()

protected fun inPathSegment(seg: Keyword, cb: () -> P?): P? = inPathSegment(seg.value, cb)

open fun visitTrueSchema(schema: TrueSchema): P? = visitChildren(schema)
Expand Down
44 changes: 24 additions & 20 deletions src/main/kotlin/com/github/erosb/jsonsKema/Validator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ data class ValidatorConfig @JvmOverloads constructor(
class ValidatorConfigBuilder {

var readWriteContext: ReadWriteContext = ReadWriteContext.NONE
var validateFormat: FormatValidationPolicy = FormatValidationPolicy.DEPENDS_ON_VOCABULARY;
var validateFormat: FormatValidationPolicy = FormatValidationPolicy.DEPENDS_ON_VOCABULARY
val additionalFormatValidators: MutableMap<String, FormatValidator> = mutableMapOf()

fun readWriteContext(readWriteContext: ReadWriteContext): ValidatorConfigBuilder {
Expand Down Expand Up @@ -217,7 +217,7 @@ private class DefaultValidator(
actualType,
this.schema,
instance,
dynamicPath
dynamicPath()
)
}
}
Expand Down Expand Up @@ -406,19 +406,23 @@ private class DefaultValidator(
}
}

override fun visitMinItemsSchema(schema: MinItemsSchema): ValidationFailure? = instance.maybeArray { array ->
if (array.length() < schema.minItems.toInt()) {
MinItemsValidationFailure(schema, array)
} else {
null
override fun visitMinItemsSchema(schema: MinItemsSchema): ValidationFailure? = inPathSegment(Keyword.MIN_ITEMS) {
instance.maybeArray { array ->
if (array.length() < schema.minItems.toInt()) {
MinItemsValidationFailure(schema, array, dynamicPath())
} else {
null
}
}
}

override fun visitMaxItemsSchema(schema: MaxItemsSchema): ValidationFailure? = instance.maybeArray { array ->
if (array.length() > schema.maxItems.toInt()) {
MaxItemsValidationFailure(schema, array)
} else {
null
override fun visitMaxItemsSchema(schema: MaxItemsSchema): ValidationFailure? = inPathSegment(Keyword.MAX_ITEMS) {
instance.maybeArray { array ->
if (array.length() > schema.maxItems.toInt()) {
MaxItemsValidationFailure(schema, array, dynamicPath())
} else {
null
}
}
}

Expand Down Expand Up @@ -465,7 +469,7 @@ private class DefaultValidator(
override fun visitMaximumSchema(schema: MaximumSchema): ValidationFailure? = inPathSegment(Keyword.MAXIMUM) {
instance.maybeNumber {
if (it.value.toDouble() > schema.maximum.toDouble()) {
MaximumValidationFailure(schema, it, dynamicPath)
MaximumValidationFailure(schema, it, dynamicPath())
} else {
null
}
Expand All @@ -476,7 +480,7 @@ private class DefaultValidator(
override fun visitMinimumSchema(schema: MinimumSchema): ValidationFailure? = inPathSegment(Keyword.MINIMUM) {
instance.maybeNumber {
if (it.value.toDouble() < schema.minimum.toDouble()) {
MinimumValidationFailure(schema, it, dynamicPath)
MinimumValidationFailure(schema, it, dynamicPath())
} else {
null
}
Expand Down Expand Up @@ -518,7 +522,7 @@ private class DefaultValidator(
if (occurrences.containsKey(elem)) {
return@maybeArray UniqueItemsValidationFailure(
listOf(occurrences[elem]!!, index), schema,
array, dynamicPath
array, dynamicPath()
)
}
occurrences[elem] = index
Expand All @@ -543,7 +547,7 @@ private class DefaultValidator(
}
null
} else {
ItemsValidationFailure(failures.toMap(), schema, array, dynamicPath)
ItemsValidationFailure(failures.toMap(), schema, array, dynamicPath())
}
}
}
Expand Down Expand Up @@ -571,7 +575,7 @@ private class DefaultValidator(
instance.maybeArray { array ->
if (array.length() == 0) {
val minContainsIsZero = schema.minContains == 0
return@maybeArray if (minContainsIsZero) null else ContainsValidationFailure("no array items are valid against \"contains\" subschema, expected minimum is ${schema.minContains}", schema, array, dynamicPath)
return@maybeArray if (minContainsIsZero) null else ContainsValidationFailure("no array items are valid against \"contains\" subschema, expected minimum is ${schema.minContains}", schema, array, dynamicPath())
}
var successCount = 0
for (idx in 0 until array.length()) {
Expand All @@ -585,14 +589,14 @@ private class DefaultValidator(
}
}
if (schema.maxContains != null && schema.maxContains.toInt() < successCount) {
return@maybeArray ContainsValidationFailure("$successCount array items are valid against \"contains\" subschema, expected maximum is 1", schema, array, dynamicPath)
return@maybeArray ContainsValidationFailure("$successCount array items are valid against \"contains\" subschema, expected maximum is 1", schema, array, dynamicPath())
}
if (successCount < schema.minContains.toInt()) {
val prefix = if (successCount == 0) "no array items are" else if (successCount == 1) "only 1 array item is" else "only $successCount array items are"
return@maybeArray ContainsValidationFailure("$prefix valid against \"contains\" subschema, expected minimum is ${schema.minContains.toInt()}", schema, array, dynamicPath)
return@maybeArray ContainsValidationFailure("$prefix valid against \"contains\" subschema, expected minimum is ${schema.minContains.toInt()}", schema, array, dynamicPath())
}
return@maybeArray if (schema.maxContains == null && schema.minContains == 1 && successCount == 0) {
ContainsValidationFailure("expected at least 1 array item to be valid against \"contains\" subschema, found 0", schema, array, dynamicPath)
ContainsValidationFailure("expected at least 1 array item to be valid against \"contains\" subschema, found 0", schema, array, dynamicPath())
} else {
null
}
Expand Down
17 changes: 17 additions & 0 deletions src/test/kotlin/com/github/erosb/jsonsKema/ArrayValidationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,21 @@ class ArrayValidationTest {
assertEquals(expected, actual)
}
}

@Test
fun minMaxItemsValidation() {
val schema = SchemaLoader(JsonParser("""
{
"minItems": 3,
"maxItems": 1
}
""".trimIndent())())()

val actual = Validator.forSchema(schema).validate(JsonParser("""
[1, 2]
""".trimIndent())())!!

assertEquals(JsonPointer("maxItems"), (actual.causes.filter { it.keyword == Keyword.MAX_ITEMS }.first() as MaxItemsValidationFailure).dynamicPath)
assertEquals(JsonPointer("minItems"), (actual.causes.filter { it.keyword == Keyword.MIN_ITEMS }.first() as MinItemsValidationFailure).dynamicPath)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class IfThenElseSchemaTest {
}

@Test
fun `subschemas contain onyl 2 clauses`() {
fun `subschemas contain only 2 clauses`() {
val subject = IfThenElseSchema(ifSchema, thenSchema, null, UnknownSource)
assertEquals(listOf(ifSchema, thenSchema), subject.subschemas())
}
Expand Down

0 comments on commit 9e5097b

Please sign in to comment.