Last active
January 12, 2023 16:25
-
-
Save mbezjak/a76b737cd6330e60b60c78b7e2c8fb9e to your computer and use it in GitHub Desktop.
Wrapping malli to produce own errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{:malli/required "%s is required" | |
:malli/extra-key "System doesn't recognize property %s" | |
:malli/null "%s must be empty" | |
:malli/some "%s must not be null" | |
:malli/invalid-type "%s has invalid type" | |
:malli/invalid "%s value is invalid" | |
:malli/not "%s value is invalid" | |
:malli/boolean "%s must be a boolean" | |
:malli/true "%s must have a value of `true`" | |
:malli/false "%s must have a value of `false`" | |
:malli/enum "%s value must be one of: %s" | |
:malli/time "%s must be a temporal value" | |
:malli/number "%s must be a number" | |
:malli/int "%s must be a whole number" | |
:malli/integer "%s must be a whole number" | |
:malli/positive-int "%s must be a positive whole number" | |
:malli/negative-int "%s must be a negative whole number" | |
:malli/natural-int "%s must be a natural whole number" | |
:malli/positive "%s must be a positive number" | |
:malli/negative "%s must be a negative number" | |
:malli/zero "%s must be zero" | |
:malli/decimal "%s must be a big decimal" | |
:malli/float "%s must be a float" | |
:malli/double "%s must be a double" | |
:malli/ratio "%s must be a ratio" | |
:malli/rational "%s must be a rational number" | |
:malli/int-between "%s must be a whole number between %s and %s" | |
:malli/int-const "%s must be a number with exactly value %s" | |
:malli/int-with-max "%s must be a whole number lower than or equal to %s" | |
:malli/int-with-min "%s must be a whole number greater than or equal to %s" | |
:malli/greater-or-equal-to "%s must be greater or equal to %s" | |
:malli/greater-than "%s must be greater than %s" | |
:malli/lower-or-equal-to "%s must be lower or equal to %s" | |
:malli/lower-than "%s must be lower than %s" | |
:malli/char "%s must be a character" | |
:malli/string "%s must be a text" | |
:malli/string-with-length-between "%s must be a text with length between %s and %s" | |
:malli/string-with-length-max "%s must be a text with length at most %s" | |
:malli/string-with-length-min "%s must be a text with length at least %s" | |
:malli/bytes "%s must be a byte array" | |
:malli/uuid "%s must be UUID" | |
:malli/uri "%s must be URI" | |
:malli/symbol "%s must be a symbol" | |
:malli/qualified-symbol "%s must be a qualified symbol" | |
:malli/simple-symbol "%s must be a simple symbol" | |
:malli/keyword "%s must be a keyword" | |
:malli/qualified-keyword "%s must be a qualified keyword" | |
:malli/simple-keyword "%s must be a simple keyword" | |
:malli/ident "%s must be a symbol or a keyword" | |
:malli/qualified-ident "%s must be a qualified symbol or a keyword" | |
:malli/simple-ident "%s must be a simple symbol or a keyword" | |
:malli/empty "%s must be empty" | |
:malli/seq "%s must be a seq" | |
:malli/vector "%s must be a vector" | |
:malli/list "%s must be a list" | |
:malli/set "%s must be a set" | |
:malli/map "%s must be a map" | |
:malli/coll "%s must be a collection" | |
:malli/sequential "%s must be sequential" | |
:malli/associative "%s must be associative" | |
:malli/indexed "%s must be indexed" | |
:malli/seqable "%s must be seqable" | |
:malli/fn "%s must be fn" | |
:malli/ifn "%s must be ifn" | |
:malli/equal "%s must be %s" | |
:malli/not-equal "%s must not be %s" | |
:malli/size "%s size is invalid" | |
:malli/tuple-size "%s tuple size is invalid" | |
:malli/multi-dispatch-value "Dispatch value on %s is invalid" | |
:malli/input-remaining "%s has too many elements"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my.company.malli-validator | |
"A wrapper around malli, integrated with `my.company.errors`." | |
(:require | |
[clojure.set :as set] | |
[clojure.string :as string] | |
[malli.core :as malli] | |
[malli.error :as me] | |
[my.company.errors :as errors] | |
[my.company.kw :as kw])) | |
(def ^:private malli-code->error-code | |
{'nil? :malli/null | |
'some? :malli/some | |
'boolean? :malli/boolean | |
'true? :malli/true | |
'false? :malli/false | |
'number? :malli/number | |
'int? :malli/int | |
'integer? :malli/integer | |
'pos-int? :malli/positive-int | |
'neg-int? :malli/negative-int | |
'nat-int? :malli/natural-int | |
'pos? :malli/positive | |
'neg? :malli/negative | |
'zero? :malli/zero | |
'decimal? :malli/decimal | |
'float? :malli/float | |
'double? :malli/double | |
'ratio? :malli/ratio | |
'rational? :malli/rational | |
'char? :malli/char | |
'string? :malli/string | |
'bytes? :malli/bytes | |
'inst? :malli/time | |
'uuid? :malli/uuid | |
'uri? :malli/uri | |
'symbol? :malli/symbol | |
'qualified-symbol? :malli/qualified-symbol | |
'simple-symbol? :malli/simple-symbol | |
'keyword? :malli/keyword | |
'qualified-keyword? :malli/qualified-keyword | |
'simple-keyword? :malli/simple-keyword | |
'ident? :malli/ident | |
'qualified-ident? :malli/qualified-ident | |
'simple-ident? :malli/simple-ident | |
'empty? :malli/empty | |
'seq? :malli/seq | |
'vector? :malli/vector | |
'list? :malli/list | |
'set? :malli/set | |
'map? :malli/map | |
'coll? :malli/coll | |
'sequential? :malli/sequential | |
'associative? :malli/associative | |
'indexed? :malli/indexed | |
'seqable? :malli/seqable | |
'fn? :malli/fn | |
'ifn? :malli/ifn | |
:=> :malli/fn | |
:function :malli/fn | |
:boolean :malli/boolean | |
:int :malli/int | |
:double :malli/double | |
:string :malli/string | |
:uuid :malli/uuid | |
:symbol :malli/symbol | |
:qualified-symbol :malli/qualified-symbol | |
:keyword :malli/keyword | |
:qualified-keyword :malli/qualified-keyword | |
:> :malli/greater-than | |
:>= :malli/greater-or-equal-to | |
:< :malli/lower-than | |
:<= :malli/lower-or-equal-to | |
:= :malli/equal | |
:not= :malli/not-equal | |
:re :malli/invalid | |
:enum :malli/enum | |
:nil :malli/null | |
:not :malli/not | |
::malli/input-remaining :malli/input-remaining | |
::malli/missing-key :malli/required | |
::malli/extra-key :malli/extra-key | |
::malli/invalid-type :malli/invalid-type | |
::malli/limits :malli/size | |
::malli/tuple-size :malli/tuple-size | |
::malli/invalid-dispatch-value :malli/multi-dispatch-value}) | |
;; Don't know how to reproduce those errors | |
(def ^:private cannot-happen-keys | |
#{'any? | |
:any | |
::malli/end-of-input | |
:malli.error/misspelled-key | |
:malli.error/misspelled-value | |
:malli.error/unknown}) | |
(def ^:private no-malli-default-errors-keys #{:function :not}) | |
(defn check-compatibility-with-malli! [message-tpls] | |
(let [malli-keys (-> me/default-errors keys set) | |
transition-keys (-> malli-code->error-code keys set) | |
extra-keys (set/difference transition-keys malli-keys cannot-happen-keys no-malli-default-errors-keys) | |
missing-keys (set/difference malli-keys transition-keys cannot-happen-keys)] | |
(assert (empty? extra-keys) | |
(format "Updated malli lib has compatibility issues. Transition keys are no longer needed: %s" | |
(string/join ", " (sort extra-keys)))) | |
(assert (empty? missing-keys) | |
(format "Updated malli lib has compatibility issues. Transition keys are missing: %s" | |
(string/join ", " (sort missing-keys))))) | |
(let [translation-codes (-> malli-code->error-code vals set) | |
tpl-codes (-> message-tpls keys set) | |
missing-keys (set/difference translation-codes tpl-codes)] | |
(assert (empty? missing-keys) | |
(format "Missing codes in error-templates.edn: %s" | |
(string/join ", " (sort missing-keys)))))) | |
(defn- extract-from-fn-props [props] | |
(let [path (:error/path props) | |
code (:error/code props)] | |
(merge {:code (or code :malli/invalid)} | |
(when path | |
{::path path | |
::in path})))) | |
(defn- expand-int-type [{:keys [min max]} value] | |
(cond | |
(nil? value) {:code :malli/required} | |
(not (int? value)) {:code :malli/int} | |
(and min (= min max)) {:code :malli/int-const :args [min]} | |
(and min max) {:code :malli/int-between :args [min max]} | |
min {:code :malli/int-with-min :args [min]} | |
max {:code :malli/int-with-max :args [max]})) | |
(defn- expand-string-type [{:keys [min max]} value] | |
(cond | |
(nil? value) {:code :malli/required} | |
(not (string? value)) {:code :malli/string} | |
(and min (empty? value)) {:code :malli/required} | |
(and min max) {:code :malli/string-with-length-between :args [min max]} | |
min {:code :malli/string-with-length-min :args [min]} | |
max {:code :malli/string-with-length-max :args [max]})) | |
(defn- humanize-field [field] | |
(if (keyword? field) | |
(kw/str field) | |
field)) | |
(defn- assoc-breadcrumb-info [error] | |
(let [in (::in error) | |
human-breadcrumbs (str "[" (string/join " -> " (map humanize-field in)) "]")] | |
(assoc error | |
:breadcrumbs in | |
:args (cons human-breadcrumbs (:args error))))) | |
(defn- malli->error [root-schema root-value malli-error] | |
(let [error-schema (:schema malli-error) | |
error-value (:value malli-error) | |
schema-type (malli/type error-schema) | |
schema-props (malli/properties error-schema) | |
schema-children (malli/children error-schema)] | |
(assoc-breadcrumb-info | |
(merge {::root-schema root-schema | |
::root-value root-value | |
::schema error-schema | |
::value error-value | |
::path (:path malli-error) | |
::in (:in malli-error)} | |
(if-let [type (:type malli-error)] | |
{:code (get malli-code->error-code type)} | |
{:code (get malli-code->error-code schema-type)}) | |
(case schema-type | |
:enum {:args [(string/join ", " schema-children)]} | |
:> {:args [(first schema-children)]} | |
:>= {:args [(first schema-children)]} | |
:< {:args [(first schema-children)]} | |
:<= {:args [(first schema-children)]} | |
:= {:args [(first schema-children)]} | |
:not= {:args [(first schema-children)]} | |
:fn (extract-from-fn-props schema-props) | |
:int (expand-int-type schema-props error-value) | |
:string (expand-string-type schema-props error-value) | |
nil))))) | |
(defn validate [schema value] | |
(when-let [{:keys [schema value errors]} (malli/explain schema value)] | |
(->> errors | |
(map #(malli->error schema value %)) | |
(errors/make)))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my.company.malli-validator-test | |
(:require | |
[clojure.test :refer [deftest is testing]] | |
[my.company.error :as error] | |
[my.company.errors :as errors] | |
[my.company.malli-validator :as sut]) | |
(:import | |
(java.net URI))) | |
(deftest validate | |
(testing "Error keys collected" | |
(let [expected-keys #{::sut/root-schema ::sut/root-value ::sut/schema ::sut/value ::sut/path ::sut/in | |
:my.company.error/code :my.company.error/breadcrumbs :my.company.error/args}] | |
(is (= expected-keys (set (keys (first (sut/validate [:int] "a")))))) | |
(is (= expected-keys (set (keys (first (sut/validate [:map [:a :int]] {:a "a"})))))))) | |
(testing "Path values" | |
(is (= [[0] [1]] (map ::sut/path (sut/validate [:and :string [:re #"a+"]] nil)))) | |
(is (= [[1]] (map ::sut/path (sut/validate [:and :string [:re #"a+"]] "")))) | |
(is (= [[:a]] (map ::sut/path (sut/validate [:fn {:error/path [:a]} (constantly false)] {})))) | |
(is (= [[]] (map ::sut/path (sut/validate [:fn (constantly false)] {}))))) | |
(testing "Breadcrumbs" | |
(is (= [[]] (map error/breadcrumbs (sut/validate [:int] "")))) | |
(is (= [[:a]] (map error/breadcrumbs (sut/validate [:map [:a :int]] {})))) | |
(is (= [[:a]] (map error/breadcrumbs (sut/validate [:fn {:error/path [:a]} (constantly false)] {}))))) | |
(testing "Arguments" | |
(is (= ["[]"] (error/args (first (sut/validate [:int] ""))))) | |
(is (= ["[a]"] (error/args (first (sut/validate [:map [:a :int]] {}))))) | |
(is (= ["[test-batch-code]"] (error/args (first (sut/validate [:map [:test-batch-code :string]] {}))))) | |
(is (= ["[a]"] (error/args (first (sut/validate [:fn {:error/path [:a]} (constantly false)] {}))))) | |
(is (= ["[0]"] (error/args (first (sut/validate [:tuple :int :int] ["a" 2]))))) | |
(is (= ["[a -> 0 -> b]"] (error/args (first (sut/validate [:map [:a [:sequential [:map [:b :string]]]]] {:a [{:b 2}]}))))) | |
(is (= ["[]" 1] (error/args (first (sut/validate [:= 1] 2))))) | |
(is (= ["[]" 1] (error/args (first (sut/validate [:not= 1] 1)))))) | |
;; Schema categories: https://github.com/metosin/malli#built-in-schemas | |
(testing "malli.core/predicate-schemas" | |
(is (nil? (sut/validate [any?] nil))) | |
(is (nil? (sut/validate [any?] 1))) | |
(is (nil? (sut/validate [nil?] nil))) | |
(is (= [:malli/null] (errors/codes (sut/validate [nil?] 1)))) | |
(is (nil? (sut/validate [some?] 1))) | |
(is (nil? (sut/validate [some?] ""))) | |
(is (= [:malli/some] (errors/codes (sut/validate [some?] nil)))) | |
(is (nil? (sut/validate [boolean?] true))) | |
(is (nil? (sut/validate [boolean?] false))) | |
(is (= [:malli/boolean] (errors/codes (sut/validate [boolean?] nil)))) | |
(is (nil? (sut/validate [true?] true))) | |
(is (= [:malli/true] (errors/codes (sut/validate [true?] nil)))) | |
(is (nil? (sut/validate [false?] false))) | |
(is (= [:malli/false] (errors/codes (sut/validate [false?] nil)))) | |
(is (nil? (sut/validate [number?] 1))) | |
(is (= [:malli/number] (errors/codes (sut/validate [number?] nil)))) | |
(is (nil? (sut/validate [int?] 1))) | |
(is (= [:malli/int] (errors/codes (sut/validate [int?] nil)))) | |
(is (= [:malli/int] (errors/codes (sut/validate [int?] "a")))) | |
(is (nil? (sut/validate [integer?] 1))) | |
(is (nil? (sut/validate [integer?] 1N))) | |
(is (= [:malli/integer] (errors/codes (sut/validate [integer?] 1.0)))) | |
(is (nil? (sut/validate [pos-int?] 1))) | |
(is (= [:malli/positive-int] (errors/codes (sut/validate [pos-int?] nil)))) | |
(is (nil? (sut/validate [neg-int?] -1))) | |
(is (= [:malli/negative-int] (errors/codes (sut/validate [neg-int?] nil)))) | |
(is (nil? (sut/validate [nat-int?] 0))) | |
(is (nil? (sut/validate [nat-int?] 1))) | |
(is (= [:malli/natural-int] (errors/codes (sut/validate [nat-int?] nil)))) | |
(is (nil? (sut/validate [pos?] 1))) | |
(is (= [:malli/positive] (errors/codes (sut/validate [pos?] 0)))) | |
(is (nil? (sut/validate [neg?] -1))) | |
(is (= [:malli/negative] (errors/codes (sut/validate [neg?] 0)))) | |
(is (nil? (sut/validate [zero?] 0))) | |
(is (nil? (sut/validate [zero?] 0.0))) | |
(is (= [:malli/zero] (errors/codes (sut/validate [zero?] 1)))) | |
(is (nil? (sut/validate [decimal?] 1.1M))) | |
(is (= [:malli/decimal] (errors/codes (sut/validate [decimal?] 1)))) | |
(is (nil? (sut/validate [float?] (float 1.1)))) | |
(is (= [:malli/float] (errors/codes (sut/validate [float?] 1)))) | |
(is (nil? (sut/validate [double?] 1.1))) | |
(is (= [:malli/double] (errors/codes (sut/validate [double?] 1)))) | |
(is (nil? (sut/validate [ratio?] 1/2))) | |
(is (= [:malli/ratio] (errors/codes (sut/validate [ratio?] 1)))) | |
(is (nil? (sut/validate [rational?] 1))) | |
(is (nil? (sut/validate [rational?] 1.0M))) | |
(is (nil? (sut/validate [rational?] 1/2))) | |
(is (= [:malli/rational] (errors/codes (sut/validate [rational?] nil)))) | |
(is (nil? (sut/validate [char?] \a))) | |
(is (= [:malli/char] (errors/codes (sut/validate [char?] nil)))) | |
(is (nil? (sut/validate [string?] ""))) | |
(is (nil? (sut/validate [string?] "abc"))) | |
(is (= [:malli/string] (errors/codes (sut/validate [string?] nil)))) | |
(is (= [:malli/string] (errors/codes (sut/validate [string?] 1)))) | |
(is (nil? (sut/validate [bytes?] (byte-array [1 2])))) | |
(is (= [:malli/bytes] (errors/codes (sut/validate [bytes?] nil)))) | |
(is (nil? (sut/validate [inst?] #inst "2020-12-31"))) | |
(is (nil? (sut/validate [inst?] #inst "2020-12-31T10:20:30"))) | |
(is (= [:malli/time] (errors/codes (sut/validate [inst?] nil)))) | |
(is (nil? (sut/validate [uuid?] #uuid "cdc87015-bafa-4f58-b52e-edcc1d9905bc"))) | |
(is (= [:malli/uuid] (errors/codes (sut/validate [uuid?] nil)))) | |
(is (nil? (sut/validate [uri?] (URI. "index.html")))) | |
(is (= [:malli/uri] (errors/codes (sut/validate [uri?] nil)))) | |
(is (nil? (sut/validate [symbol?] 'a))) | |
(is (nil? (sut/validate [symbol?] 'a/b))) | |
(is (= [:malli/symbol] (errors/codes (sut/validate [symbol?] nil)))) | |
(is (nil? (sut/validate [qualified-symbol?] 'a/b))) | |
(is (= [:malli/qualified-symbol] (errors/codes (sut/validate [qualified-symbol?] nil)))) | |
(is (nil? (sut/validate [simple-symbol?] 'a))) | |
(is (= [:malli/simple-symbol] (errors/codes (sut/validate [simple-symbol?] nil)))) | |
(is (nil? (sut/validate [keyword?] :a))) | |
(is (nil? (sut/validate [keyword?] :a/b))) | |
(is (= [:malli/keyword] (errors/codes (sut/validate [keyword?] nil)))) | |
(is (nil? (sut/validate [qualified-keyword?] :a/b))) | |
(is (= [:malli/qualified-keyword] (errors/codes (sut/validate [qualified-keyword?] nil)))) | |
(is (nil? (sut/validate [simple-keyword?] :a))) | |
(is (= [:malli/simple-keyword] (errors/codes (sut/validate [simple-keyword?] nil)))) | |
(is (nil? (sut/validate [ident?] :a))) | |
(is (nil? (sut/validate [ident?] :a/b))) | |
(is (nil? (sut/validate [ident?] 'a))) | |
(is (nil? (sut/validate [ident?] 'a/b))) | |
(is (= [:malli/ident] (errors/codes (sut/validate [ident?] nil)))) | |
(is (nil? (sut/validate [qualified-ident?] :a/b))) | |
(is (nil? (sut/validate [qualified-ident?] 'a/b))) | |
(is (= [:malli/qualified-ident] (errors/codes (sut/validate [qualified-ident?] nil)))) | |
(is (nil? (sut/validate [simple-ident?] :a))) | |
(is (nil? (sut/validate [simple-ident?] 'a))) | |
(is (= [:malli/simple-ident] (errors/codes (sut/validate [simple-ident?] nil)))) | |
(is (nil? (sut/validate [empty?] ""))) | |
(is (nil? (sut/validate [empty?] '()))) | |
(is (nil? (sut/validate [empty?] []))) | |
(is (nil? (sut/validate [empty?] #{}))) | |
(is (nil? (sut/validate [empty?] {}))) | |
(is (= [:malli/empty] (errors/codes (sut/validate [empty?] [:a :b])))) | |
(is (nil? (sut/validate [seq?] '()))) | |
(is (nil? (sut/validate [seq?] (range 1 3)))) | |
(is (= [:malli/seq] (errors/codes (sut/validate [seq?] "")))) | |
(is (nil? (sut/validate [vector?] []))) | |
(is (= [:malli/vector] (errors/codes (sut/validate [vector?] "")))) | |
(is (nil? (sut/validate [list?] '()))) | |
(is (= [:malli/list] (errors/codes (sut/validate [list?] "")))) | |
(is (nil? (sut/validate [set?] #{}))) | |
(is (= [:malli/set] (errors/codes (sut/validate [set?] "")))) | |
(is (nil? (sut/validate [map?] {}))) | |
(is (= [:malli/map] (errors/codes (sut/validate [map?] "")))) | |
(is (nil? (sut/validate [coll?] '()))) | |
(is (nil? (sut/validate [coll?] []))) | |
(is (nil? (sut/validate [coll?] #{}))) | |
(is (nil? (sut/validate [coll?] {}))) | |
(is (= [:malli/coll] (errors/codes (sut/validate [coll?] "")))) | |
(is (nil? (sut/validate [sequential?] '()))) | |
(is (nil? (sut/validate [sequential?] []))) | |
(is (= [:malli/sequential] (errors/codes (sut/validate [sequential?] "")))) | |
(is (nil? (sut/validate [associative?] []))) | |
(is (nil? (sut/validate [associative?] {}))) | |
(is (= [:malli/associative] (errors/codes (sut/validate [associative?] "")))) | |
(is (nil? (sut/validate [indexed?] []))) | |
(is (= [:malli/indexed] (errors/codes (sut/validate [indexed?] "")))) | |
(is (nil? (sut/validate [seqable?] nil))) | |
(is (nil? (sut/validate [seqable?] ""))) | |
(is (nil? (sut/validate [seqable?] '()))) | |
(is (nil? (sut/validate [seqable?] []))) | |
(is (nil? (sut/validate [seqable?] #{}))) | |
(is (nil? (sut/validate [seqable?] {}))) | |
(is (= [:malli/seqable] (errors/codes (sut/validate [seqable?] 1)))) | |
(is (nil? (sut/validate [fn?] (fn [])))) | |
(is (nil? (sut/validate [fn?] #()))) | |
(is (nil? (sut/validate [fn?] inc))) | |
(is (= [:malli/fn] (errors/codes (sut/validate [fn?] 1)))) | |
(is (= [:malli/fn] (errors/codes (sut/validate [fn?] {})))) | |
(is (nil? (sut/validate [ifn?] (fn [])))) | |
(is (nil? (sut/validate [ifn?] #()))) | |
(is (nil? (sut/validate [ifn?] inc))) | |
(is (nil? (sut/validate [ifn?] {}))) | |
(is (= [:malli/ifn] (errors/codes (sut/validate [ifn?] 1))))) | |
(testing "malli.core/comparator-schemas" | |
(is (nil? (sut/validate [:> 1] 2))) | |
(is (= [:malli/greater-than] (errors/codes (sut/validate [:> 1] 0)))) | |
(is (nil? (sut/validate [:>= 1] 1))) | |
(is (= [:malli/greater-or-equal-to] (errors/codes (sut/validate [:>= 1] 0)))) | |
(is (nil? (sut/validate [:< 1] 0))) | |
(is (= [:malli/lower-than] (errors/codes (sut/validate [:< 1] 2)))) | |
(is (nil? (sut/validate [:<= 1] 1))) | |
(is (= [:malli/lower-or-equal-to] (errors/codes (sut/validate [:<= 1] 2)))) | |
(is (nil? (sut/validate [:= 1] 1))) | |
(is (= [:malli/equal] (errors/codes (sut/validate [:= 1] nil)))) | |
(is (nil? (sut/validate [:not= 1] 0))) | |
(is (= [:malli/not-equal] (errors/codes (sut/validate [:not= 1] 1))))) | |
(testing "malli.core/type-schemas" | |
(is (nil? (sut/validate [:any] nil))) | |
(is (nil? (sut/validate [:any] 1))) | |
(is (nil? (sut/validate [:nil] nil))) | |
(is (= [:malli/null] (errors/codes (sut/validate [:nil] "a")))) | |
(is (nil? (sut/validate [:boolean] true))) | |
(is (nil? (sut/validate [:boolean] false))) | |
(is (= [:malli/boolean] (errors/codes (sut/validate [:boolean] nil)))) | |
(is (= [:malli/boolean] (errors/codes (sut/validate [:boolean] "a")))) | |
(is (nil? (sut/validate [:int] 1))) | |
(is (nil? (sut/validate [:int {:min 1}] 1))) | |
(is (nil? (sut/validate [:int {:max 1}] 1))) | |
(is (nil? (sut/validate [:int {:min 1 :max 2}] 1))) | |
(is (nil? (sut/validate [:int {:min 1 :max 1}] 1))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:int] nil)))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:int {:min 1}] nil)))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:int] "a")))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:int {:min 1}] "a")))) | |
(is (= [:malli/int-with-min] (errors/codes (sut/validate [:int {:min 1}] 0)))) | |
(is (= [:malli/int-with-max] (errors/codes (sut/validate [:int {:max 1}] 2)))) | |
(is (= [:malli/int-between] (errors/codes (sut/validate [:int {:min 1 :max 2}] 0)))) | |
(is (= [:malli/int-const] (errors/codes (sut/validate [:int {:min 1 :max 1}] 0)))) | |
(is (nil? (sut/validate [:double] 1.1))) | |
(is (= [:malli/double] (errors/codes (sut/validate [:double] 1)))) | |
(is (nil? (sut/validate [:string] ""))) | |
(is (nil? (sut/validate [:string] "abc"))) | |
(is (nil? (sut/validate [:string {:min 1}] "a"))) | |
(is (nil? (sut/validate [:string {:max 1}] "a"))) | |
(is (nil? (sut/validate [:string {:min 1 :max 2}] "a"))) | |
(is (nil? (sut/validate [:string {:min 1 :max 1}] "a"))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:string] nil)))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:string {:min 1}] "")))) | |
(is (= [:malli/string] (errors/codes (sut/validate [:string] 1)))) | |
(is (= [:malli/string] (errors/codes (sut/validate [:string] 1)))) | |
(is (= [:malli/string-with-length-min] (errors/codes (sut/validate [:string {:min 2}] "a")))) | |
(is (= [:malli/string-with-length-max] (errors/codes (sut/validate [:string {:max 1}] "abc")))) | |
(is (= [:malli/string-with-length-between] (errors/codes (sut/validate [:string {:min 1 :max 2}] "abc")))) | |
(is (nil? (sut/validate [:uuid] #uuid "cdc87015-bafa-4f58-b52e-edcc1d9905bc"))) | |
(is (= [:malli/uuid] (errors/codes (sut/validate [:uuid] "1")))) | |
(is (nil? (sut/validate [:symbol] 'a))) | |
(is (nil? (sut/validate [:symbol] 'a/b))) | |
(is (= [:malli/symbol] (errors/codes (sut/validate [:symbol] 1)))) | |
(is (nil? (sut/validate [:qualified-symbol] 'a/b))) | |
(is (= [:malli/qualified-symbol] (errors/codes (sut/validate [:qualified-symbol] 'a)))) | |
(is (nil? (sut/validate [:keyword] :a))) | |
(is (nil? (sut/validate [:keyword] :a/b))) | |
(is (= [:malli/keyword] (errors/codes (sut/validate [:keyword] 1)))) | |
(is (nil? (sut/validate [:qualified-keyword] :a/b))) | |
(is (= [:malli/qualified-keyword] (errors/codes (sut/validate [:qualified-keyword] :a))))) | |
(testing "malli.core/sequence-schemas" | |
(is (nil? (sut/validate [:+ :string] ["a"]))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:+ :string] [])))) | |
(is (nil? (sut/validate [:* :string] []))) | |
(is (= [:malli/invalid-type] (errors/codes (sut/validate [:* :string] nil)))) | |
(is (nil? (sut/validate [:? :string] []))) | |
(is (nil? (sut/validate [:? :string] ["a"]))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:? :string] ["a" "b"])))) | |
(is (nil? (sut/validate [:repeat {:min 1 :max 2} :string] ["a"]))) | |
(is (nil? (sut/validate [:repeat {:min 1 :max 2} :string] ["a" "b"]))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:repeat {:min 1 :max 2} :string] [])))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:repeat {:min 2 :max 3} :string] ["a"])))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:repeat {:min 1 :max 2} :string] ["a" "b" "c"])))) | |
(is (nil? (sut/validate [:cat :string :int] ["a" 1]))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:cat :string :int] [])))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:cat :string :int] ["a"])))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:cat :string :int] ["a" 1 1])))) | |
(is (nil? (sut/validate [:catn [:s :string] [:i :int]] ["a" 1]))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:catn [:s :string] [:i :int]] [])))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:catn [:s :string] [:i :int]] ["a"])))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:catn [:s :string] [:i :int]] ["a" 1 1])))) | |
(is (nil? (sut/validate [:alt :string :int] ["a"]))) | |
(is (nil? (sut/validate [:alt :string :int] [1]))) | |
(is (= [:malli/required :malli/required] (errors/codes (sut/validate [:alt :string :int] [])))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:alt :string :int] ["a" 1])))) | |
(is (nil? (sut/validate [:altn [:s :string] [:i :int]] ["a"]))) | |
(is (nil? (sut/validate [:altn [:s :string] [:i :int]] [1]))) | |
(is (= [:malli/required :malli/required] (errors/codes (sut/validate [:altn [:s :string] [:i :int]] [])))) | |
(is (= [:malli/input-remaining] (errors/codes (sut/validate [:altn [:s :string] [:i :int]] ["a" 1]))))) | |
(testing "malli.core/base-schemas" | |
(is (nil? (sut/validate [:re "a+"] "a"))) | |
(is (nil? (sut/validate [:re "abc"] "abcdef"))) | |
(is (= [:malli/invalid] (errors/codes (sut/validate [:re #"a+"] nil)))) | |
(is (= [:malli/invalid] (errors/codes (sut/validate [:re #"a+"] "")))) | |
(is (= [:malli/invalid] (errors/codes (sut/validate [:re #"^abc$"] "abcdef")))) | |
(is (nil? (sut/validate [:enum "a" "b"] "a"))) | |
(is (= [:malli/enum] (errors/codes (sut/validate [:enum "a" "b"] nil)))) | |
(is (= [:malli/enum] (errors/codes (sut/validate [:enum "a" "b"] "c")))) | |
(is (nil? (sut/validate [:map [:a :boolean]] {:a true}))) | |
(is (nil? (sut/validate [:map [:a :string]] {:a "a"}))) | |
(is (nil? (sut/validate [:map [:a :int]] {:a 1}))) | |
(is (nil? (sut/validate [:map {:closed true} [:a :int]] {:a 1}))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:map [:a :boolean]] {})))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:map [:a :string]] {})))) | |
(is (= [:malli/required] (errors/codes (sut/validate [:map [:a :int]] {})))) | |
(is (= [:malli/extra-key] (errors/codes (sut/validate [:map {:closed true} [:a :int]] {:a 1 :b 2})))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:map [:a :int]] {:a "abc"})))) | |
(is (= [:malli/string] (errors/codes (sut/validate [:map [:a :string]] {:a 1})))) | |
(is (= [:malli/invalid-type] (errors/codes (sut/validate [:map [:a :int]] nil)))) | |
(is (nil? (sut/validate [:map-of :keyword :string] {}))) | |
(is (nil? (sut/validate [:map-of :keyword :string] {:a "a"}))) | |
(is (nil? (sut/validate [:map-of :keyword :string] {:a "a" :b "b"}))) | |
(is (= [:malli/string] (errors/codes (sut/validate [:map-of :keyword :string] {:a 1})))) | |
(is (= [:malli/keyword] (errors/codes (sut/validate [:map-of :keyword :string] {"a" "a"})))) | |
(is (nil? (sut/validate [:tuple :int :int] [1 2]))) | |
(is (nil? (sut/validate [:tuple :int :string] [1 "a"]))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:tuple :int :int] ["a" 2])))) | |
(is (= [:malli/int :malli/string] (errors/codes (sut/validate [:tuple :int :string] ["a" 2])))) | |
(is (= [:malli/tuple-size] (errors/codes (sut/validate [:tuple :int :int] [1])))) | |
(is (nil? (sut/validate [:vector :int] []))) | |
(is (nil? (sut/validate [:vector {:min 1} :int] [1]))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:vector :int] ["a"])))) | |
(is (= [:malli/size] (errors/codes (sut/validate [:vector {:min 1} :int] [])))) | |
(is (nil? (sut/validate [:set :int] #{}))) | |
(is (nil? (sut/validate [:set {:min 1} :int] #{1}))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:set :int] #{"a"})))) | |
(is (= [:malli/size] (errors/codes (sut/validate [:set {:min 1} :int] #{})))) | |
(is (nil? (sut/validate [:sequential :int] []))) | |
(is (nil? (sut/validate [:sequential :int] '()))) | |
(is (nil? (sut/validate [:sequential {:min 1} :int] [1]))) | |
(is (nil? (sut/validate [:sequential {:min 1} :int] '(1)))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:sequential :int] ["a"])))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:sequential :int] '("a"))))) | |
(is (= [:malli/size] (errors/codes (sut/validate [:sequential {:min 1} :int] [])))) | |
(is (= [:malli/size] (errors/codes (sut/validate [:sequential {:min 1} :int] '())))) | |
(is (nil? (sut/validate [:and :string [:re #"a+"]] "a"))) | |
(is (= [:malli/required :malli/invalid] (errors/codes (sut/validate [:and :string [:re #"a+"]] nil)))) | |
(is (= [:malli/invalid] (errors/codes (sut/validate [:and :string [:re #"a+"]] "b")))) | |
(is (nil? (sut/validate [:or pos-int? neg-int?] 1))) | |
(is (nil? (sut/validate [:or pos-int? neg-int?] -1))) | |
(is (= [:malli/positive-int :malli/negative-int] (errors/codes (sut/validate [:or pos-int? neg-int?] 0)))) | |
(is (= [:malli/positive-int :malli/negative-int] (errors/codes (sut/validate [:or pos-int? neg-int?] nil)))) | |
(is (nil? (sut/validate [:orn [:p pos-int?] [:n neg-int?]] 1))) | |
(is (nil? (sut/validate [:orn [:p pos-int?] [:n neg-int?]] -1))) | |
(is (= [:malli/positive-int :malli/negative-int] (errors/codes (sut/validate [:orn [:p pos-int?] [:n neg-int?]] 0)))) | |
(is (= [:malli/positive-int :malli/negative-int] (errors/codes (sut/validate [:orn [:p pos-int?] [:n neg-int?]] nil)))) | |
(is (nil? (sut/validate [:not :int] :a))) | |
(is (nil? (sut/validate [:not :int] "a"))) | |
(is (= [:malli/not] (errors/codes (sut/validate [:not :int] 1)))) | |
(is (nil? (sut/validate [:maybe :int] 1))) | |
(is (nil? (sut/validate [:maybe :int] nil))) | |
(is (= [:malli/int] (errors/codes (sut/validate [:maybe :int] "a")))) | |
(is (= [:malli/invalid] (errors/codes (sut/validate [:fn (constantly false)] {})))) | |
(is (= [:test.batch/invalid] (errors/codes (sut/validate [:fn {:error/code :test.batch/invalid} | |
(constantly false)] | |
{})))) | |
(is (nil? (sut/validate [:=> [:cat :double] :double] (fn ^double [^double x] x)))) | |
(is (= [:malli/fn] (errors/codes (sut/validate [:=> [:cat :double] :double] 1.5)))) | |
(is (nil? (sut/validate [:function | |
[:=> [:cat :double] :double] | |
[:=> [:cat :double :double] :double]] | |
(fn ^double | |
([^double x] x) | |
([^double x ^double y] (+ x y)))))) | |
(is (= [:malli/fn] (errors/codes (sut/validate [:function | |
[:=> [:cat :int] :int] | |
[:=> [:cat :int :int] :int]] | |
1)))) | |
(let [schema [:multi {:dispatch :type} | |
["A" [:map [:type :string] [:value :string]]] | |
["B" [:map [:type :string] [:value :int]]]]] | |
(is (nil? (sut/validate schema {:type "A" :value "a"}))) | |
(is (nil? (sut/validate schema {:type "B" :value 1}))) | |
(is (= [:malli/multi-dispatch-value] (errors/codes (sut/validate schema {:value 1234}))))) | |
(is (nil? (sut/validate [:schema {:registry {::foobar [:and :string [:= "foobar"]]}} | |
::foobar] | |
"foobar"))) | |
(is (= [:malli/equal] | |
(errors/codes (sut/validate [:schema {:registry {::foobar [:and :string [:= "foobar"]]}} | |
::foobar] | |
"a")))) | |
(is (nil? (sut/validate [:schema {:registry {::foobar [:and :string [:= "foobar"]] | |
::foobar-or-int [:or [:ref ::foobar] :int]}} | |
::foobar-or-int] | |
"foobar"))) | |
(is (nil? (sut/validate [:schema {:registry {::foobar [:and :string [:= "foobar"]] | |
::foobar-or-int [:or [:ref ::foobar] :int]}} | |
::foobar-or-int] | |
1))) | |
(is (= [:malli/equal :malli/int] | |
(errors/codes (sut/validate [:schema {:registry {::foobar [:and :string [:= "foobar"]] | |
::foobar-or-int [:or [:ref ::foobar] :int]}} | |
::foobar-or-int] | |
"a")))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment