Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions source/JSONSchema-Core-Tests/JSONSchemaTests.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,40 @@ JSONSchemaTests >> testMultipleConstraintsOnAnyObject [
should: [ schema validate: '1234567' ] raise: JSONConstraintError
]

{ #category : 'tests' }
JSONSchemaTests >> testPatternPropertiesWithEscapedDotInCharacterClassIsSupported [
"Regression: the OAI OpenAPI-3.0 meta-schema uses [a-zA-Z0-9\.\-_]+ for component-name
patternProperties keys (components.schemas/responses/parameters/etc.) - Pharo's Regex
engine raised 'bad backslash escape' on the escaped dot inside the character class."
| schema |
schema := JSONSchema fromString: '{"type":"object","patternProperties":{"^[a-zA-Z0-9\\.\\-_]+$":{"type":"string"}},"additionalProperties":false}'.
self shouldnt: [ schema validate: {'Foo.Bar-1_2'->'ok'} asDictionary ] raise: Error.
self should: [ schema validate: {'has space'->'ok'} asDictionary ] raise: JSONSchemaError
]

{ #category : 'tests' }
JSONSchemaTests >> testPatternPropertiesWithNonCapturingGroupIsSupported [
"Same regression as #testPatternWithNonCapturingGroupIsSupported, but for the
patternProperties keyword (matches property *names*, separate code path/call site)."
| schema |
schema := JSONSchema fromString: '{"type":"object","patternProperties":{"^[1-5](?:\\d{2}|XX)$":{"type":"string"}},"additionalProperties":false}'.
self shouldnt: [ schema validate: {'200'->'ok'} asDictionary ] raise: Error.
self shouldnt: [ schema validate: {'2XX'->'ok'} asDictionary ] raise: Error.
self should: [ schema validate: {'999'->'ok'} asDictionary ] raise: JSONSchemaError
]

{ #category : 'tests' }
JSONSchemaTests >> testPatternWithNonCapturingGroupIsSupported [
"Regression: the OAI OpenAPI-3.0 meta-schema's Responses object uses the pattern
'^[1-5](?:\d{2}|XX)$' - Pharo's Regex engine used to raise 'Invalid lookaround
expression ?:' on the (?:...) syntax, crashing any validation that hit it."
| schema |
schema := JSONSchema fromString: '{"type":"string","pattern":"^[1-5](?:\\d{2}|XX)$"}'.
self shouldnt: [ schema validate: '200' ] raise: Error.
self shouldnt: [ schema validate: '2XX' ] raise: Error.
self should: [ schema validate: '999' ] raise: JSONConstraintError
]

{ #category : 'tests' }
JSONSchemaTests >> testSchemaReadArray [

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ JSONSchemaPatternConstraint >> validate [

{ #category : 'validation' }
JSONSchemaPatternConstraint >> validate: aString [
(pattern asRegex search: aString) ifFalse: [
(pattern asECMARegex search: aString) ifFalse: [
JSONConstraintError signal: aString, ' does not match pattern ', pattern ]
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ JSONSchemaPatternPropertiesConstraint >> validate: aDictionary [
is Boolean true = accept all)."
| resolvedPatterns |
resolvedPatterns := patternProperties associations collect: [ :assoc |
{ assoc key asRegex. self resolveVisitor visitSchemaSpec: assoc value } ].
{ assoc key asECMARegex. self resolveVisitor visitSchemaSpec: assoc value } ].
aDictionary keysAndValuesDo: [ :key :value |
| matchedPattern |
matchedPattern := false.
Expand Down
29 changes: 29 additions & 0 deletions source/JSONSchema-Core/String.extension.st
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
Extension { #name : 'String' }

{ #category : '*jsonschema-core' }
String >> asECMARegex [
"Pharo's Regex engine doesn't accept some ECMA-262 syntax that real-world schemas use
(e.g. the OAI OpenAPI meta-schema), even though it is semantically equivalent for the
boolean match/no-match checks pattern/patternProperties need:
- non-capturing groups (?:...) - rewritten to a plain capturing group (...)
- an escaped literal dot \. inside a character class [...] - rewritten to a bare . (both
mean 'literal dot' inside a class; Pharo's engine only accepts the unescaped form there)"
^ self asECMARegexSource asRegex
]

{ #category : '*jsonschema-core' }
String >> asECMARegexSource [
"See #asECMARegex. Kept separate so the rewritten source itself is testable/inspectable."
| source result inClass i char |
source := self copyReplaceAll: '(?:' with: '('.
result := WriteStream on: String new.
i := 1.
inClass := false.
[ i <= source size ] whileTrue: [
char := source at: i.
(char == $[ and: [ inClass not ]) ifTrue: [ inClass := true ].
(char == $] and: [ inClass ]) ifTrue: [ inClass := false ].
(inClass and: [ char == $\ and: [ i < source size and: [ (source at: i + 1) == $. ] ] ])
ifTrue: [ result nextPut: $.. i := i + 2 ]
ifFalse: [ result nextPut: char. i := i + 1 ] ].
^ result contents
]

{ #category : '*jsonschema-core' }
String >> asJSONSchema [
^ JSONSchema string
Expand Down
Loading