🐛 Fix ::sm/set schema validation

It has several corner cases where set specific type
is not checked. It also now checks for ordered type
specifically when ordered is specified
This commit is contained in:
Andrey Antukh 2025-10-14 18:51:05 +02:00
parent 25521b18ff
commit d4de367499
2 changed files with 89 additions and 14 deletions

View File

@ -423,38 +423,41 @@
(fn [{:keys [kind max min ordered] :as props} children _]
(let [kind (or (last children) kind)
pred
child-pred
(cond
(fn? kind) kind
(nil? kind) any?
:else (validator kind))
type-pred
(if ordered
d/ordered-set?
set?)
pred
(cond
(and max min)
(fn [value]
(let [size (count value)]
(and (set? value)
(<= min size max)
(every? pred value))))
(and (type-pred value)
(every? child-pred value)
(<= min (count value) max)))
min
(fn [value]
(let [size (count value)]
(and (set? value)
(<= min size)
(every? pred value))))
(and (type-pred value)
(every? child-pred value)
(<= min (count value))))
max
(fn [value]
(let [size (count value)]
(and (set? value)
(<= size max)
(every? pred value))))
(and (type-pred value)
(every? child-pred value)
(<= (count value) max)))
:else
(fn [value]
(every? pred value)))
(and (type-pred value)
(every? child-pred value))))
empty-set
(if ordered

View File

@ -6,6 +6,7 @@
(ns common-tests.schema-test
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[clojure.test :as t]))
@ -35,6 +36,77 @@
(t/is (true? (sm/validate schema #{})))
(t/is (false? (sm/validate schema #{"a"})))))
(t/testing "validate 2"
(let [candidate-1 ["a@b.com" "a@c.net"]
candidate-2 (into #{} candidate-1)
candidate-3 (into (d/ordered-set) candidate-1)
candidate-4 #{"a@b.com"}
candidate-5 (d/ordered-set "a@b.com")
schema-1 [::sm/set ::sm/email]
schema-2 [::sm/set {:ordered true} ::sm/email]
schema-3 [::sm/set {:ordered true :min 1} ::sm/email]
schema-4 [::sm/set {:min 1} ::sm/email]
schema-5 [::sm/set {:ordered true :max 1} ::sm/email]
schema-6 [::sm/set {:ordered true :min 1 :max 2} ::sm/email]
schema-7 [::sm/set {:min 1 :max 2} ::sm/email]]
(t/is (false? (sm/validate schema-1 [])))
(t/is (false? (sm/validate schema-1 candidate-1)))
(t/is (true? (sm/validate schema-1 candidate-2)))
(t/is (true? (sm/validate schema-1 candidate-3)))
(t/is (false? (sm/validate schema-2 [])))
(t/is (false? (sm/validate schema-2 candidate-1)))
(t/is (false? (sm/validate schema-2 candidate-2)))
(t/is (true? (sm/validate schema-2 candidate-3)))
(t/is (false? (sm/validate schema-3 [])))
(t/is (false? (sm/validate schema-3 candidate-1)))
(t/is (false? (sm/validate schema-3 candidate-2)))
(t/is (true? (sm/validate schema-3 candidate-3)))
(t/is (false? (sm/validate schema-3 candidate-4)))
(t/is (true? (sm/validate schema-3 candidate-5)))
(t/is (false? (sm/validate schema-3 (d/ordered-set))))
(t/is (false? (sm/validate schema-4 [])))
(t/is (false? (sm/validate schema-4 candidate-1)))
(t/is (true? (sm/validate schema-4 candidate-2)))
(t/is (true? (sm/validate schema-4 candidate-3)))
(t/is (true? (sm/validate schema-4 candidate-4)))
(t/is (true? (sm/validate schema-4 candidate-5)))
(t/is (false? (sm/validate schema-4 (d/ordered-set))))
(t/is (false? (sm/validate schema-4 #{})))
(t/is (false? (sm/validate schema-5 [])))
(t/is (false? (sm/validate schema-5 candidate-1)))
(t/is (false? (sm/validate schema-5 candidate-2)))
(t/is (false? (sm/validate schema-5 candidate-3)))
(t/is (false? (sm/validate schema-5 candidate-4)))
(t/is (true? (sm/validate schema-5 candidate-5)))
(t/is (true? (sm/validate schema-5 (d/ordered-set))))
(t/is (false? (sm/validate schema-5 #{})))
(t/is (false? (sm/validate schema-6 [])))
(t/is (false? (sm/validate schema-6 candidate-1)))
(t/is (false? (sm/validate schema-6 candidate-2)))
(t/is (true? (sm/validate schema-6 candidate-3)))
(t/is (false? (sm/validate schema-6 candidate-4)))
(t/is (true? (sm/validate schema-6 candidate-5)))
(t/is (false? (sm/validate schema-6 (d/ordered-set))))
(t/is (false? (sm/validate schema-6 #{})))
(t/is (false? (sm/validate schema-6 (conj candidate-3 "r@r.com"))))
(t/is (false? (sm/validate schema-7 [])))
(t/is (false? (sm/validate schema-7 candidate-1)))
(t/is (true? (sm/validate schema-7 candidate-2)))
(t/is (true? (sm/validate schema-7 candidate-3)))
(t/is (true? (sm/validate schema-7 candidate-4)))
(t/is (true? (sm/validate schema-7 candidate-5)))
(t/is (false? (sm/validate schema-7 (d/ordered-set))))
(t/is (false? (sm/validate schema-7 #{})))
(t/is (false? (sm/validate schema-7 (conj candidate-2 "r@r.com"))))
(t/is (false? (sm/validate schema-7 (conj candidate-3 "r@r.com"))))))
(t/testing "generate"
(let [schema [::sm/set ::sm/email]
value (sg/generate schema)]