Skip to content

Commit 950180d

Browse files
author
Clemens Vasters
committed
fix: add maxLength validation and comprehensive type coverage tests for schema validator
- Added missing maxLength to STRING_VALIDATION_KEYWORDS - Added maxLength validation in _check_string_validation method - Added tests for all 27 primitive types via test_all_primitive_types_valid - Added tests for all 7 compound types via test_compound_types_recognized - Added tests for numeric validation keywords on all numeric types - Added tests for string-based numeric types (int64, uint64, int128, uint128, decimal) - Added maxLength validation tests (valid, negative, not-integer) - Added unknown type rejection test - Total schema validator tests increased from 68 to 122
1 parent 78f77b2 commit 950180d

File tree

2 files changed

+186
-1
lines changed

2 files changed

+186
-1
lines changed

samples/py/json_structure_schema_validator.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class JSONStructureSchemaCoreValidator:
5454

5555
# Extended keywords for validation
5656
NUMERIC_VALIDATION_KEYWORDS = {"minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"}
57-
STRING_VALIDATION_KEYWORDS = {"minLength", "pattern", "format"}
57+
STRING_VALIDATION_KEYWORDS = {"minLength", "maxLength", "pattern", "format"}
5858
ARRAY_VALIDATION_KEYWORDS = {"minItems", "maxItems", "uniqueItems", "contains", "minContains", "maxContains"}
5959
OBJECT_VALIDATION_KEYWORDS = {"minProperties", "maxProperties", "minEntries", "maxEntries",
6060
"dependentRequired", "patternProperties", "patternKeys",
@@ -545,6 +545,11 @@ def _check_string_validation(self, obj, path):
545545
val = obj["minLength"]
546546
if not isinstance(val, int) or val < 0:
547547
self._err("'minLength' must be a non-negative integer.", f"{path}/minLength")
548+
549+
if "maxLength" in obj:
550+
val = obj["maxLength"]
551+
if not isinstance(val, int) or val < 0:
552+
self._err("'maxLength' must be a non-negative integer.", f"{path}/maxLength")
548553

549554
if "pattern" in obj:
550555
val = obj["pattern"]

samples/py/test_json_structure_schema_validator.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,3 +873,183 @@ def test_validation_metaschema_enables_extensions():
873873
source_text = json.dumps(schema)
874874
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
875875
assert errors == []
876+
877+
878+
# =============================================================================
879+
# Tests for all primitive types
880+
# =============================================================================
881+
882+
# Test all primitive types are recognized
883+
@pytest.mark.parametrize("primitive_type", [
884+
"string", "number", "integer", "boolean", "null",
885+
"int8", "uint8", "int16", "uint16", "int32", "uint32",
886+
"int64", "uint64", "int128", "uint128",
887+
"float8", "float", "double", "decimal",
888+
"date", "datetime", "time", "duration",
889+
"uuid", "uri", "binary", "jsonpointer"
890+
])
891+
def test_all_primitive_types_valid(primitive_type):
892+
"""Test that all primitive types are recognized as valid."""
893+
schema = {
894+
"$schema": "https://json-structure.org/meta/core/v0/#",
895+
"$id": f"https://example.com/schema/{primitive_type}_test",
896+
"name": f"{primitive_type.capitalize()}Schema",
897+
"type": primitive_type
898+
}
899+
source_text = json.dumps(schema)
900+
errors = validate_json_structure_schema_core(schema, source_text)
901+
assert errors == [], f"Type '{primitive_type}' should be valid but got errors: {errors}"
902+
903+
904+
# Test that unknown types are rejected
905+
def test_unknown_type_rejected():
906+
schema = {
907+
"$schema": "https://json-structure.org/meta/core/v0/#",
908+
"$id": "https://example.com/schema/unknown_type",
909+
"name": "UnknownTypeSchema",
910+
"type": "unknowntype"
911+
}
912+
source_text = json.dumps(schema)
913+
errors = validate_json_structure_schema_core(schema, source_text)
914+
assert any("not a recognized" in err for err in errors)
915+
916+
917+
# Test maxLength validation
918+
def test_maxlength_valid():
919+
schema = {
920+
"$schema": "https://json-structure.org/meta/extended/v0/#",
921+
"$id": "https://example.com/schema/maxlength_valid",
922+
"name": "MaxLengthSchema",
923+
"$uses": ["JSONStructureValidation"],
924+
"type": "string",
925+
"maxLength": 100
926+
}
927+
source_text = json.dumps(schema)
928+
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
929+
assert errors == []
930+
931+
932+
def test_maxlength_negative():
933+
schema = {
934+
"$schema": "https://json-structure.org/meta/extended/v0/#",
935+
"$id": "https://example.com/schema/maxlength_negative",
936+
"name": "MaxLengthNegativeSchema",
937+
"$uses": ["JSONStructureValidation"],
938+
"type": "string",
939+
"maxLength": -1
940+
}
941+
source_text = json.dumps(schema)
942+
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
943+
assert any("maxLength" in err for err in errors)
944+
945+
946+
def test_maxlength_not_integer():
947+
schema = {
948+
"$schema": "https://json-structure.org/meta/extended/v0/#",
949+
"$id": "https://example.com/schema/maxlength_not_int",
950+
"name": "MaxLengthNotIntSchema",
951+
"$uses": ["JSONStructureValidation"],
952+
"type": "string",
953+
"maxLength": "100"
954+
}
955+
source_text = json.dumps(schema)
956+
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
957+
assert any("maxLength" in err for err in errors)
958+
959+
960+
# Test compound types are valid
961+
@pytest.mark.parametrize("compound_type", ["object", "array", "set", "map", "tuple", "choice", "any"])
962+
def test_compound_types_recognized(compound_type):
963+
"""Test that compound types are recognized."""
964+
# Build appropriate schema for each compound type
965+
if compound_type == "object":
966+
schema = {
967+
"$schema": "https://json-structure.org/meta/core/v0/#",
968+
"$id": f"https://example.com/schema/{compound_type}_test",
969+
"name": "ObjectSchema",
970+
"type": "object",
971+
"properties": {"prop": {"type": "string"}}
972+
}
973+
elif compound_type in ["array", "set"]:
974+
schema = {
975+
"$schema": "https://json-structure.org/meta/core/v0/#",
976+
"$id": f"https://example.com/schema/{compound_type}_test",
977+
"name": f"{compound_type.capitalize()}Schema",
978+
"type": compound_type,
979+
"items": {"type": "string"}
980+
}
981+
elif compound_type == "map":
982+
schema = {
983+
"$schema": "https://json-structure.org/meta/core/v0/#",
984+
"$id": f"https://example.com/schema/{compound_type}_test",
985+
"name": "MapSchema",
986+
"type": "map",
987+
"values": {"type": "string"}
988+
}
989+
elif compound_type == "tuple":
990+
schema = {
991+
"$schema": "https://json-structure.org/meta/core/v0/#",
992+
"$id": f"https://example.com/schema/{compound_type}_test",
993+
"name": "TupleSchema",
994+
"type": "tuple",
995+
"properties": {"a": {"type": "string"}, "b": {"type": "int32"}},
996+
"tuple": ["a", "b"]
997+
}
998+
elif compound_type == "choice":
999+
schema = {
1000+
"$schema": "https://json-structure.org/meta/core/v0/#",
1001+
"$id": f"https://example.com/schema/{compound_type}_test",
1002+
"name": "ChoiceSchema",
1003+
"type": "choice",
1004+
"choices": {"opt1": {"type": "string"}, "opt2": {"type": "int32"}}
1005+
}
1006+
else: # any
1007+
schema = {
1008+
"$schema": "https://json-structure.org/meta/core/v0/#",
1009+
"$id": f"https://example.com/schema/{compound_type}_test",
1010+
"name": "AnySchema",
1011+
"type": "any"
1012+
}
1013+
1014+
source_text = json.dumps(schema)
1015+
errors = validate_json_structure_schema_core(schema, source_text)
1016+
assert errors == [], f"Compound type '{compound_type}' should be valid but got errors: {errors}"
1017+
1018+
1019+
# Test validation keywords on all numeric types
1020+
@pytest.mark.parametrize("numeric_type", [
1021+
"number", "integer", "float", "double", "float8",
1022+
"int8", "uint8", "int16", "uint16", "int32", "uint32"
1023+
])
1024+
def test_numeric_validation_keywords(numeric_type):
1025+
"""Test that numeric validation keywords work on numeric types."""
1026+
schema = {
1027+
"$schema": "https://json-structure.org/meta/extended/v0/#",
1028+
"$id": f"https://example.com/schema/{numeric_type}_validation",
1029+
"name": f"{numeric_type.capitalize()}ValidationSchema",
1030+
"$uses": ["JSONStructureValidation"],
1031+
"type": numeric_type,
1032+
"minimum": 0,
1033+
"maximum": 100
1034+
}
1035+
source_text = json.dumps(schema)
1036+
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
1037+
assert errors == [], f"Numeric validation on '{numeric_type}' should be valid but got errors: {errors}"
1038+
1039+
1040+
# Test validation keywords on string-based numeric types (int64, uint64, int128, uint128, decimal)
1041+
@pytest.mark.parametrize("string_numeric_type", ["int64", "uint64", "int128", "uint128", "decimal"])
1042+
def test_string_numeric_validation_keywords(string_numeric_type):
1043+
"""Test that string-based numeric types require string values for validation keywords."""
1044+
schema = {
1045+
"$schema": "https://json-structure.org/meta/extended/v0/#",
1046+
"$id": f"https://example.com/schema/{string_numeric_type}_validation",
1047+
"name": f"{string_numeric_type.capitalize()}ValidationSchema",
1048+
"$uses": ["JSONStructureValidation"],
1049+
"type": string_numeric_type,
1050+
"minimum": "0",
1051+
"maximum": "1000000000000"
1052+
}
1053+
source_text = json.dumps(schema)
1054+
errors = validate_json_structure_schema_core(schema, source_text, extended=True)
1055+
assert errors == [], f"String-based numeric validation on '{string_numeric_type}' should be valid but got errors: {errors}"

0 commit comments

Comments
 (0)