diff --git a/src/validation.jl b/src/validation.jl index cb671ca..3e3cb2d 100644 --- a/src/validation.jl +++ b/src/validation.jl @@ -589,7 +589,7 @@ _is_type(::Real, ::Val{:number}) = true _is_type(::Nothing, ::Val{:null}) = true _is_type(::Missing, ::Val{:null}) = true _is_type(::AbstractDict, ::Val{:object}) = true -_is_type(::String, ::Val{:string}) = true +_is_type(::AbstractString, ::Val{:string}) = true # Note that Julia treat's Bool <: Number, but JSON-Schema distinguishes them. _is_type(::Bool, ::Val{:number}) = false _is_type(::Bool, ::Val{:integer}) = false @@ -726,7 +726,7 @@ end # 6.3.1 function _validate( - x::String, + x::AbstractString, schema, ::Val{:maxLength}, val::Union{Integer,Float64}, @@ -740,7 +740,7 @@ end # 6.3.2 function _validate( - x::String, + x::AbstractString, schema, ::Val{:minLength}, val::Union{Integer,Float64}, @@ -754,7 +754,7 @@ end # 6.3.3 function _validate( - x::String, + x::AbstractString, schema, ::Val{:pattern}, val::String, diff --git a/test/runtests.jl b/test/runtests.jl index 3ef77c0..da335ba 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,7 @@ import ZipFile include("additional_patterns.jl") include("integer_floats.jl") +include("string_instances.jl") const TEST_SUITE_URL = "https://github.com/json-schema-org/JSON-Schema-Test-Suite/archive/23.1.0.zip" diff --git a/test/string_instances.jl b/test/string_instances.jl new file mode 100644 index 0000000..79df65c --- /dev/null +++ b/test/string_instances.jl @@ -0,0 +1,57 @@ +@testset "AbstractString instances" begin + for wrap in (identity, s -> SubString(s), s -> LazyString("", s)) + text = wrap("αβ") + @test isvalid(JSONSchema.schema(typeof(text)), text) + @test isvalid(JSONSchema.Schema(Dict("type" => "string")), text) + nullable = JSONSchema.Schema(Dict("type" => ["string", "null"])) + @test isvalid(nullable, text) + @test isvalid(nullable, nothing) + + for (keyword, limit, value, expected) in ( + ("maxLength", 2, "αβ", true), + ("maxLength", 1, "αβ", false), + ("maxLength", 0, "", true), + ("minLength", 1, "", false), + ("minLength", 2, "αβ", true), + ("minLength", 3, "αβ", false), + ("maxLength", 1, "e\u0301", false), + ) + schema = JSONSchema.Schema(Dict(keyword => limit)) + @test isvalid(schema, wrap(value)) == expected + end + for (pattern, value, expected) in ( + (raw"^αβ$", "αβ", true), + ("^β", "αβ", false), + ("β", "αβ", true), + ("", "", true), + ) + schema = JSONSchema.Schema(Dict("pattern" => pattern)) + @test isvalid(schema, wrap(value)) == expected + end + + for (rule, bad, reason) in ( + (Dict("maxLength" => 1), "αβ", "maxLength"), + (Dict("minLength" => 3), "αβ", "minLength"), + (Dict("pattern" => "^z"), "αβ", "pattern"), + ) + schema = JSONSchema.Schema( + Dict("properties" => Dict("names" => Dict("items" => rule))), + ) + issue = JSONSchema.validate(schema, Dict("names" => [wrap(bad)])) + @test issue isa JSONSchema.SingleIssue && + issue.reason == reason && + JSONSchema.json_pointer(issue) == "#/names/0" + end + typed = JSONSchema.Schema(Dict("type" => "string", "minLength" => 2)) + @test isvalid(typed, text) + @test !isvalid(typed, wrap("α")) + end + + for data in (1, true, nothing, ["αβ"]) + @test !isvalid(JSONSchema.Schema(Dict("type" => "string")), data) + @test isvalid( + JSONSchema.Schema(Dict("minLength" => 5, "pattern" => "^z")), + data, + ) + end +end