From 44c164112ff41c8a2be30fa70c1ed62cbb912a84 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 10:24:28 -0500 Subject: [PATCH 01/15] fix conditional predicate wrapping --- src/flanders/core.cljc | 3 +-- test/flanders/core_test.clj | 4 ++++ test/flanders/schema_test.clj | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/flanders/core.cljc b/src/flanders/core.cljc index 974d6014..a6011c6b 100644 --- a/src/flanders/core.cljc +++ b/src/flanders/core.cljc @@ -90,8 +90,7 @@ (if (nil? p+t) [types tests] (recur more (conj types t) - (conj tests (let [p (if (= :else p) (constantly true) p)] - #(when (p %) %))))))] + (conj tests (if (= :else p) (constantly true) p)))))] (ft/map->EitherType {:choices types :tests tests}))) diff --git a/test/flanders/core_test.clj b/test/flanders/core_test.clj index 5e8c8201..b2ef821a 100644 --- a/test/flanders/core_test.clj +++ b/test/flanders/core_test.clj @@ -46,3 +46,7 @@ (deftest sig-test (is (instance? SignatureType (f/sig))) (is (thrown? java.lang.AssertionError (f/sig :parameters 10)))) + +(deftest boolean-generator-test + (is ()) + ) diff --git a/test/flanders/schema_test.clj b/test/flanders/schema_test.clj index 6c58c7bb..525f8c8e 100644 --- a/test/flanders/schema_test.clj +++ b/test/flanders/schema_test.clj @@ -159,3 +159,9 @@ (is (= {:example "string" :description "Str"} (->swagger (assoc f/any-str :description "Str")))) (is (= {:example "a" :description "Str"} (->swagger (f/enum #{"b" "c" "a"} :description "Str")))) (is (= {:example "default" :description "Str"} (->swagger (assoc f/any-str :description "Str" :default "default"))))) + +(deftest conditional-test + (is (nil? (s/check + (fs/->schema (f/conditional + :else f/any-bool)) + false)))) From 6843cfab961ce14e26f7739c010fe3b56311da93 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 10:25:35 -0500 Subject: [PATCH 02/15] rm --- test/flanders/core_test.clj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/flanders/core_test.clj b/test/flanders/core_test.clj index b2ef821a..5e8c8201 100644 --- a/test/flanders/core_test.clj +++ b/test/flanders/core_test.clj @@ -46,7 +46,3 @@ (deftest sig-test (is (instance? SignatureType (f/sig))) (is (thrown? java.lang.AssertionError (f/sig :parameters 10)))) - -(deftest boolean-generator-test - (is ()) - ) From e991002ea72492b1b8fe3d499f4c19d8cf98d13f Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 10:28:55 -0500 Subject: [PATCH 03/15] wip --- test/flanders/malli_test.clj | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/flanders/malli_test.clj b/test/flanders/malli_test.clj index f395a947..f914e5c5 100644 --- a/test/flanders/malli_test.clj +++ b/test/flanders/malli_test.clj @@ -225,3 +225,15 @@ (-> Example fm/->malli ms/transform)))) + +(deftest conditional-test + (is (m/validate + (fm/->malli (f/conditional + boolean? f/any-bool)) + false)) + ;;FIXME should add tests to schema + (is (not (m/validate + ;;TODO + (fm/->malli (f/conditional + (constantly false) f/any-bool)) + false)))) From c70aa9507e23378aa5d0cd01590ccc66eec25f2c Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 11:52:42 -0500 Subject: [PATCH 04/15] wip --- .github/workflows/build.yml | 60 ++++++++++++++--------------------- project.clj | 5 +-- src/flanders/core.cljc | 2 +- src/flanders/malli.clj | 33 +++++++++++++++---- test/flanders/malli_test.clj | 26 ++++++++++++--- test/flanders/schema_test.clj | 1 + 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aba0f5dc..5d8165f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,54 +13,43 @@ on: jobs: setup: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@3.7 + uses: DeLaGuardo/setup-clojure@12.5 with: - lein: 2.9.8 + lein: 2.11.2 - name: Cache project dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.m2/repository ~/.gitlibs key: ${{ runner.os }}-clojure-${{ hashFiles('**/project.clj', '**/deps.edn') }} restore-keys: | - ${{ runner.os }}-clojure + ${{ runner.os }}-clojure- - name: Warm deps cache run: | for i in {1..10}; do lein deps :tree && break; done - - id: set-matrix - run: | - set -x - case "${GITHUB_EVENT_NAME}" in - #scheduled) - # echo '::set-output name=matrix::{"jdk":["8","11","17","21"],"cmd":["test"]}}' - # ;; - *) - echo '::set-output name=matrix::{"jdk":["8","11","17","21"],"cmd":["test"]}}' - ;; - esac lint: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '8' + java-version: '21' - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@12.1 + uses: DeLaGuardo/setup-clojure@12.5 with: - clj-kondo: '2023.12.15' + clj-kondo: '2024.09.27' - name: Check clojure code run: @@ -68,36 +57,33 @@ jobs: test: needs: setup strategy: - matrix: ${{fromJson(needs.setup.outputs.matrix)}} - runs-on: ubuntu-20.04 + matrix: + jdk: ["21" "22"] + runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache project dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.m2/repository ~/.gitlibs key: ${{ runner.os }}-clojure-${{ hashFiles('**/project.clj', '**/deps.edn') }} restore-keys: | - ${{ runner.os }}-clojure + ${{ runner.os }}-clojure- - name: Prepare java - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.jdk }} - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@3.7 + uses: DeLaGuardo/setup-clojure@12.5 with: - lein: 2.9.8 - - run: | - set -x - eval 'lein do clean, compile :all, ${CMD}' - env: - CMD: ${{ matrix.cmd }} + lein: 2.11.2 + - run: lein do clean, compile :all, test all-pr-checks: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [test, lint] steps: - run: echo "All tests pass!" diff --git a/project.clj b/project.clj index d67f726e..a08f840e 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,7 @@ :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :pedantic? :abort - :dependencies [[org.clojure/clojure "1.11.3"] + :dependencies [[org.clojure/clojure "1.11.4"] [org.clojure/core.match "1.0.0"] [cheshire "5.9.0"] @@ -24,4 +24,5 @@ :profiles {:dev {:dependencies [[org.clojure/test.check "1.1.1"] - [metosin/malli "0.13.0"]]}}) + [metosin/malli "0.16.4"] + [org.babashka/sci "0.9.44"]]}}) diff --git a/src/flanders/core.cljc b/src/flanders/core.cljc index a6011c6b..4789770b 100644 --- a/src/flanders/core.cljc +++ b/src/flanders/core.cljc @@ -90,7 +90,7 @@ (if (nil? p+t) [types tests] (recur more (conj types t) - (conj tests (if (= :else p) (constantly true) p)))))] + (conj tests (if (= :else p) any? p)))))] (ft/map->EitherType {:choices types :tests tests}))) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index eb682e96..36619fa5 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -42,14 +42,33 @@ ;; Branches EitherType - (->malli' [{:keys [choices key?]} opts] - ;;TODO `choices` allows dispatch like :multi, but they're in the wrong format + (->malli' [{:keys [tests choices key?]} opts] + ;; e.g., (f/conditional #(= false %) f/any-bool) will choose true as an example (let [f #(->malli' % opts) - choice-schemas (map f choices) - s (m/schema (into [:or] choice-schemas))] - (if key? - {:op :default-key :schema s} - s))) + ;; note: if test is more narrow than the choice, the example will be wrong. + choice-schemas (eduction (map f) choices) + g (gensym)] + (if-some [tests (some-> (not-empty tests) vec)] + (let [ntests (count tests)] + (into [:multi {:dispatch (fn [v] + (or (some #(when ((nth tests %) v) %) (range ntests)) + :dispatch-failed))}] + (map-indexed (fn [i s] + [i (let [s (m/schema s opts)] + (m/-update-properties s assoc :gen/schema + ;; :multi assumes schemas for generators passes preds, must assert + ;; explicitly. :and + :fn could fail, but throws an error pointing + ;; to this schema, so putting a property to help debugging. + ;; if you find yourself here, trying looking for `conditional` schemas + ;; in CTIM where the predicate does not match the same values as the schema. + ;; e.g., (f/conditional #(= false %) f/any-bool) + ;; should be (f/conditional #(= false %) (f/enum false)) + [:and {::if-this-fails-see :flanders.malli/->malli} s [:fn (nth tests i)]]))])) + choice-schemas)) + (let [s (m/schema (into [:or] choice-schemas))] + (if key? + {:op :default-key :schema s} + s))))) MapEntry (->malli' [{:keys [key type required? key?] :as entry} opts] diff --git a/test/flanders/malli_test.clj b/test/flanders/malli_test.clj index f914e5c5..106fedba 100644 --- a/test/flanders/malli_test.clj +++ b/test/flanders/malli_test.clj @@ -7,6 +7,7 @@ [flanders.core :as f] [flanders.malli :as fm] [malli.core :as m] + [malli.generator :as mg] [malli.swagger :as ms] [malli.util :as mu])) @@ -98,11 +99,20 @@ (->malli-frm (f/either :choices [(f/bool) (f/str)])))) (is (= [:map {:closed true} [:malli.core/default [:map-of [:or :boolean :string] :any]]] (->malli-frm (f/map [(f/entry (f/either :choices [(f/bool) (f/str)]) f/any)]))))) + (testing "conditional" + (is (= [:multi {:dispatch true} [0 :boolean]] + (-> (->malli-frm (f/conditional boolean? f/any-bool)) + (update-in [1 :dispatch] fn?)))) + (is (= [:multi {:dispatch true} [0 :boolean] [1 :string]] + (-> (->malli-frm (f/conditional boolean? f/any-bool string? f/any-str)) + (update-in [1 :dispatch] fn?))))) (testing "sig" (is (= [:=> [:cat :int] :int] (->malli-frm (f/sig :parameters [(f/int)] :return (f/int))))) (is (= [:map {:closed true} [:malli.core/default [:map-of [:=> [:cat :int] :int] :any]]] - (->malli-frm (f/map [(f/entry (f/sig :parameters [(f/int)] :return (f/int)) f/any)])))))) + (->malli-frm (f/map [(f/entry (f/sig :parameters [(f/int)] :return (f/int)) f/any)]))))) + + ) (deftest test-valid-schema (is (= [:map {:closed true @@ -231,9 +241,17 @@ (fm/->malli (f/conditional boolean? f/any-bool)) false)) - ;;FIXME should add tests to schema (is (not (m/validate - ;;TODO (fm/->malli (f/conditional (constantly false) f/any-bool)) - false)))) + false))) + ;;TODO examples will be wrong if predicate doesn't exactly match schema. + ;; could solve this probabilistically or throw an error. + #_ + (is (false? (fm/->malli (f/conditional + (constantly false) f/any-bool)))) + (is (thrown-with-msg? Exception + #":malli\.generator/and-generator-failure" + (mg/generate (fm/->malli (f/conditional + (constantly false) f/any-bool)))))) + diff --git a/test/flanders/schema_test.clj b/test/flanders/schema_test.clj index 525f8c8e..7389df0e 100644 --- a/test/flanders/schema_test.clj +++ b/test/flanders/schema_test.clj @@ -160,6 +160,7 @@ (is (= {:example "a" :description "Str"} (->swagger (f/enum #{"b" "c" "a"} :description "Str")))) (is (= {:example "default" :description "Str"} (->swagger (assoc f/any-str :description "Str" :default "default"))))) +;; idea: convert EitherType => s/conditional using preconditions (deftest conditional-test (is (nil? (s/check (fs/->schema (f/conditional From bb091aa1b060cc0f4e46fa3a0eecd4c85dde91f8 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 12:00:11 -0500 Subject: [PATCH 05/15] some? --- src/flanders/core.cljc | 3 ++- src/flanders/malli.clj | 2 +- src/flanders/schema.cljc | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/flanders/core.cljc b/src/flanders/core.cljc index 4789770b..974d6014 100644 --- a/src/flanders/core.cljc +++ b/src/flanders/core.cljc @@ -90,7 +90,8 @@ (if (nil? p+t) [types tests] (recur more (conj types t) - (conj tests (if (= :else p) any? p)))))] + (conj tests (let [p (if (= :else p) (constantly true) p)] + #(when (p %) %))))))] (ft/map->EitherType {:choices types :tests tests}))) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index 36619fa5..88e5c0b1 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -51,7 +51,7 @@ (if-some [tests (some-> (not-empty tests) vec)] (let [ntests (count tests)] (into [:multi {:dispatch (fn [v] - (or (some #(when ((nth tests %) v) %) (range ntests)) + (or (some #(when (some? ((nth tests %) v)) %) (range ntests)) :dispatch-failed))}] (map-indexed (fn [i s] [i (let [s (m/schema s opts)] diff --git a/src/flanders/schema.cljc b/src/flanders/schema.cljc index e7a51572..20b17f8e 100644 --- a/src/flanders/schema.cljc +++ b/src/flanders/schema.cljc @@ -57,7 +57,7 @@ EitherType (->schema' [{:keys [choices tests] :as dll} f] (-> (let [choice-schemas (map f choices)] - (apply s/conditional (mapcat vector tests choice-schemas))) + (apply s/conditional (mapcat vector (map #(comp some? %) tests) choice-schemas))) (describe dll))) MapEntry From 21d253628ac5f75f0fa33b29e54c2a13c4d21cea Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 13:21:23 -0500 Subject: [PATCH 06/15] revert --- src/flanders/malli.clj | 2 +- src/flanders/schema.cljc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index 88e5c0b1..36619fa5 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -51,7 +51,7 @@ (if-some [tests (some-> (not-empty tests) vec)] (let [ntests (count tests)] (into [:multi {:dispatch (fn [v] - (or (some #(when (some? ((nth tests %) v)) %) (range ntests)) + (or (some #(when ((nth tests %) v) %) (range ntests)) :dispatch-failed))}] (map-indexed (fn [i s] [i (let [s (m/schema s opts)] diff --git a/src/flanders/schema.cljc b/src/flanders/schema.cljc index 20b17f8e..e7a51572 100644 --- a/src/flanders/schema.cljc +++ b/src/flanders/schema.cljc @@ -57,7 +57,7 @@ EitherType (->schema' [{:keys [choices tests] :as dll} f] (-> (let [choice-schemas (map f choices)] - (apply s/conditional (mapcat vector (map #(comp some? %) tests) choice-schemas))) + (apply s/conditional (mapcat vector tests choice-schemas))) (describe dll))) MapEntry From 06d2b8405878b398774990b8da8172358ca7e25e Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 14:10:06 -0500 Subject: [PATCH 07/15] todo --- CHANGES.md | 2 + project.clj | 2 +- src/flanders/core.cljc | 3 +- src/flanders/malli.clj | 32 +++++++++------ test/flanders/malli_test.clj | 75 +++++++++++++++++++++++++---------- test/flanders/schema_test.clj | 41 ++++++++++++++++--- 6 files changed, 112 insertions(+), 43 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fb44a077..06ce11d2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,7 @@ # NEXT +- fix `f/conditional` schemas that accept `nil` or `false` + # 1.0.2 - 16th July 2024 - fix string escaping when generating markdown diff --git a/project.clj b/project.clj index a08f840e..6f91a901 100644 --- a/project.clj +++ b/project.clj @@ -25,4 +25,4 @@ :profiles {:dev {:dependencies [[org.clojure/test.check "1.1.1"] [metosin/malli "0.16.4"] - [org.babashka/sci "0.9.44"]]}}) + [prismatic/schema-generators "0.1.5" :exclusions [prismatic/schema]]]}}) diff --git a/src/flanders/core.cljc b/src/flanders/core.cljc index 974d6014..4789770b 100644 --- a/src/flanders/core.cljc +++ b/src/flanders/core.cljc @@ -90,8 +90,7 @@ (if (nil? p+t) [types tests] (recur more (conj types t) - (conj tests (let [p (if (= :else p) (constantly true) p)] - #(when (p %) %))))))] + (conj tests (if (= :else p) any? p)))))] (ft/map->EitherType {:choices types :tests tests}))) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index 36619fa5..6c66a53a 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -46,24 +46,32 @@ ;; e.g., (f/conditional #(= false %) f/any-bool) will choose true as an example (let [f #(->malli' % opts) ;; note: if test is more narrow than the choice, the example will be wrong. - choice-schemas (eduction (map f) choices) + choice-schemas (mapv f choices) g (gensym)] (if-some [tests (some-> (not-empty tests) vec)] - (let [ntests (count tests)] + (let [ntests (count tests) + _ (run! (fn [i] + (let [guard (nth tests i) + schema (nth choice-schemas i)] + (when-not (-> schema (m/properties opts) :json-schema/example guard) + (println (format "[flanders.malli] WARNING: generated example for %s does not satisfy guard: %s" + (m/form schema) + (str guard)))))) + (range ntests))] (into [:multi {:dispatch (fn [v] (or (some #(when ((nth tests %) v) %) (range ntests)) :dispatch-failed))}] (map-indexed (fn [i s] - [i (let [s (m/schema s opts)] - (m/-update-properties s assoc :gen/schema - ;; :multi assumes schemas for generators passes preds, must assert - ;; explicitly. :and + :fn could fail, but throws an error pointing - ;; to this schema, so putting a property to help debugging. - ;; if you find yourself here, trying looking for `conditional` schemas - ;; in CTIM where the predicate does not match the same values as the schema. - ;; e.g., (f/conditional #(= false %) f/any-bool) - ;; should be (f/conditional #(= false %) (f/enum false)) - [:and {::if-this-fails-see :flanders.malli/->malli} s [:fn (nth tests i)]]))])) + [i (m/-update-properties s assoc :gen/schema + ;; :multi assumes schemas for generators passes preds, must assert + ;; explicitly. :and + :fn could fail, but throws an error pointing + ;; to this schema, so putting a property to help debugging. + ;; if you find yourself here, trying looking for `conditional` schemas + ;; in CTIM where the predicate does not match the same values as the schema. + ;; e.g., (f/conditional #(= false %) f/any-bool) + ;; should be (f/conditional #(= false %) (f/enum false)) + (m/form [:and {::if-this-fails-see :flanders.malli/->malli} s [:fn (nth tests i)]] + opts))])) choice-schemas)) (let [s (m/schema (into [:or] choice-schemas))] (if key? diff --git a/test/flanders/malli_test.clj b/test/flanders/malli_test.clj index 106fedba..9caa3603 100644 --- a/test/flanders/malli_test.clj +++ b/test/flanders/malli_test.clj @@ -1,5 +1,6 @@ (ns flanders.malli-test - (:require [clojure.test :refer [deftest is testing]] + (:require [clojure.string :as str] + [clojure.test :refer [deftest is testing]] [clojure.walk :as w] [flanders.examples :refer [Example @@ -100,9 +101,14 @@ (is (= [:map {:closed true} [:malli.core/default [:map-of [:or :boolean :string] :any]]] (->malli-frm (f/map [(f/entry (f/either :choices [(f/bool) (f/str)]) f/any)]))))) (testing "conditional" - (is (= [:multi {:dispatch true} [0 :boolean]] + (is (= [:multi {:dispatch true} [0 [:boolean #:gen{:schema + [:and #:flanders.malli{:if-this-fails-see :flanders.malli/->malli} + ;;this prints as [:boolean nil] ?? + [:boolean {:json-schema/example true}] + [:fn boolean?]]}]]] (-> (->malli-frm (f/conditional boolean? f/any-bool)) - (update-in [1 :dispatch] fn?)))) + (update-in [1 :dispatch] fn?) + ))) (is (= [:multi {:dispatch true} [0 :boolean] [1 :string]] (-> (->malli-frm (f/conditional boolean? f/any-bool string? f/any-str)) (update-in [1 :dispatch] fn?))))) @@ -111,9 +117,12 @@ (->malli-frm (f/sig :parameters [(f/int)] :return (f/int))))) (is (= [:map {:closed true} [:malli.core/default [:map-of [:=> [:cat :int] :int] :any]]] (->malli-frm (f/map [(f/entry (f/sig :parameters [(f/int)] :return (f/int)) f/any)]))))) - ) +(m/form [:and {::if-this-fails-see :flanders.malli/->malli} [:boolean {:json-schema/example true}] [:fn any?]]) +(m/form [:schema {:gen/schema [:and {::if-this-fails-see :flanders.malli/->malli} (m/schema [:boolean {:json-schema/example true}]) [:fn any?]]} + any?]) + (deftest test-valid-schema (is (= [:map {:closed true :json-schema/example {:foo "string" @@ -237,21 +246,43 @@ ms/transform)))) (deftest conditional-test - (is (m/validate - (fm/->malli (f/conditional - boolean? f/any-bool)) - false)) - (is (not (m/validate - (fm/->malli (f/conditional - (constantly false) f/any-bool)) - false))) - ;;TODO examples will be wrong if predicate doesn't exactly match schema. - ;; could solve this probabilistically or throw an error. - #_ - (is (false? (fm/->malli (f/conditional - (constantly false) f/any-bool)))) - (is (thrown-with-msg? Exception - #":malli\.generator/and-generator-failure" - (mg/generate (fm/->malli (f/conditional - (constantly false) f/any-bool)))))) - + (testing "predicates that return true for false work" + (is (m/validate + (fm/->malli (f/conditional + boolean? f/any-bool)) + false)) + (is (m/validate + (fm/->malli (f/conditional + false? (f/bool :equals false))) + false))) + (testing "predicates that return true for nil work" + (with-out-str ;; suppress expected warning on conditions + (is (m/validate + (fm/->malli (f/conditional + nil? f/any)) + nil)))) + (testing "predicates that return false for false and nil work" + (with-out-str ;; suppress expected warning on conditions + (is (not (m/validate + (fm/->malli (f/conditional + (constantly false) f/any)) + false))) + (is (not (m/validate + (fm/->malli (f/conditional + (constantly false) f/any)) + nil))))) + (testing "warning if predicate and schema disagree" + (is (str/starts-with? + (binding [*print-length* nil + *print-level* nil + *print-namespace-maps* false] + (with-out-str + (fm/->malli (f/conditional + false? f/any-bool)))) + "[flanders.malli] WARNING: generated example for [:boolean {:json-schema/example true}] does not satisfy guard: clojure.core$false_QMARK"))) + (testing "condition predicates are taken into account in generators" + (is (thrown-with-msg? Exception + #":malli\.generator/and-generator-failure" + (with-out-str ;; suppress expected generated example warning + (mg/generate (fm/->malli (f/conditional + (constantly false) f/any-bool)))))))) diff --git a/test/flanders/schema_test.clj b/test/flanders/schema_test.clj index 7389df0e..e5964fbd 100644 --- a/test/flanders/schema_test.clj +++ b/test/flanders/schema_test.clj @@ -6,7 +6,8 @@ [flanders.schema :as fs] [ring.swagger.json-schema :as js] [schema.core :as s] - [schema-tools.core :as st])) + [schema-tools.core :as st] + [schema-generators.generators :as sg])) (deftest test-valid-schema (is @@ -160,9 +161,37 @@ (is (= {:example "a" :description "Str"} (->swagger (f/enum #{"b" "c" "a"} :description "Str")))) (is (= {:example "default" :description "Str"} (->swagger (assoc f/any-str :description "Str" :default "default"))))) -;; idea: convert EitherType => s/conditional using preconditions (deftest conditional-test - (is (nil? (s/check - (fs/->schema (f/conditional - :else f/any-bool)) - false)))) + (testing "predicates that return true for false work" + (is (nil? (s/check + (fs/->schema (f/conditional + :else f/any-bool)) + false))) + (is (nil? (s/check + (fs/->schema (f/conditional + false? (f/bool :equals false))) + false)))) + (testing "predicates that return true for nil work" + (is (nil? (s/check + (fs/->schema (f/conditional + :else f/any)) + nil))) + (is (nil? (s/check + (fs/->schema (f/conditional + nil? f/any)) + nil)))) + (testing "predicates that return false for false and nil work" + (is (s/check + (fs/->schema (f/conditional + (constantly false) f/any)) + false)) + (is (s/check + (fs/->schema (f/conditional + (constantly false) f/any)) + nil))) + (testing "condition predicates are taken into account in generators" + (is (thrown-with-msg? Exception + #"Couldn't satisfy such-that predicate" + (sg/generate + (fs/->schema (f/conditional + (constantly false) f/any-bool))))))) From ed476cdac0bc84ff5fc3d0eada37494920bf4ea6 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 14:24:35 -0500 Subject: [PATCH 08/15] wip --- src/flanders/malli.clj | 5 +++-- test/flanders/malli_test.clj | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index 6c66a53a..4343eb7f 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -70,8 +70,9 @@ ;; in CTIM where the predicate does not match the same values as the schema. ;; e.g., (f/conditional #(= false %) f/any-bool) ;; should be (f/conditional #(= false %) (f/enum false)) - (m/form [:and {::if-this-fails-see :flanders.malli/->malli} s [:fn (nth tests i)]] - opts))])) + (m/form [:and {::if-this-fails-see :flanders.malli/->malli} + s + [:fn (nth tests i)]]))])) choice-schemas)) (let [s (m/schema (into [:or] choice-schemas))] (if key? diff --git a/test/flanders/malli_test.clj b/test/flanders/malli_test.clj index 9caa3603..7ce14283 100644 --- a/test/flanders/malli_test.clj +++ b/test/flanders/malli_test.clj @@ -101,15 +101,21 @@ (is (= [:map {:closed true} [:malli.core/default [:map-of [:or :boolean :string] :any]]] (->malli-frm (f/map [(f/entry (f/either :choices [(f/bool) (f/str)]) f/any)]))))) (testing "conditional" - (is (= [:multi {:dispatch true} [0 [:boolean #:gen{:schema - [:and #:flanders.malli{:if-this-fails-see :flanders.malli/->malli} - ;;this prints as [:boolean nil] ?? - [:boolean {:json-schema/example true}] - [:fn boolean?]]}]]] + (is (= [:multi {:dispatch true} + [0 [:boolean #:gen{:schema + [:and #:flanders.malli{:if-this-fails-see :flanders.malli/->malli} + ;; FIXME nil due to form-no-swagger in this namespace + [:boolean nil] + [:fn boolean?]]}]]] (-> (->malli-frm (f/conditional boolean? f/any-bool)) - (update-in [1 :dispatch] fn?) - ))) - (is (= [:multi {:dispatch true} [0 :boolean] [1 :string]] + (update-in [1 :dispatch] fn?)))) + (is (= [:multi {:dispatch true} + [0 [:boolean #:gen{:schema [:and #:flanders.malli{:if-this-fails-see :flanders.malli/->malli} + [:boolean nil] ;; FIXME nil due to form-no-swagger in this namespace + [:fn boolean?]]}]] + [1 [:string #:gen{:schema [:and #:flanders.malli{:if-this-fails-see :flanders.malli/->malli} + [:string nil] + [:fn string?]]}]]] (-> (->malli-frm (f/conditional boolean? f/any-bool string? f/any-str)) (update-in [1 :dispatch] fn?))))) (testing "sig" From 14350e16ecb0a046b2bbf3745b45a99afea7479a Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 14:25:19 -0500 Subject: [PATCH 09/15] wip --- test/flanders/malli_test.clj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/flanders/malli_test.clj b/test/flanders/malli_test.clj index 7ce14283..013fbf19 100644 --- a/test/flanders/malli_test.clj +++ b/test/flanders/malli_test.clj @@ -122,12 +122,7 @@ (is (= [:=> [:cat :int] :int] (->malli-frm (f/sig :parameters [(f/int)] :return (f/int))))) (is (= [:map {:closed true} [:malli.core/default [:map-of [:=> [:cat :int] :int] :any]]] - (->malli-frm (f/map [(f/entry (f/sig :parameters [(f/int)] :return (f/int)) f/any)]))))) - ) - -(m/form [:and {::if-this-fails-see :flanders.malli/->malli} [:boolean {:json-schema/example true}] [:fn any?]]) -(m/form [:schema {:gen/schema [:and {::if-this-fails-see :flanders.malli/->malli} (m/schema [:boolean {:json-schema/example true}]) [:fn any?]]} - any?]) + (->malli-frm (f/map [(f/entry (f/sig :parameters [(f/int)] :return (f/int)) f/any)])))))) (deftest test-valid-schema (is (= [:map {:closed true From 6d82f014bbb3e977b4dce52b4594e674562a4bac Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 14:33:44 -0500 Subject: [PATCH 10/15] wip --- src/flanders/spec.clj | 4 +++- test/flanders/spec_test.clj | 44 +++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/flanders/spec.clj b/src/flanders/spec.clj index 87428b33..ad0bef96 100644 --- a/src/flanders/spec.clj +++ b/src/flanders/spec.clj @@ -63,7 +63,9 @@ ;; Branches EitherType - (->spec' [{:keys [choices]} ns f] + (->spec' [{:keys [tests choices]} ns f] + (when (seq tests) + (throw (ex-info (str "WARNING: ")))) (let [choices (->> choices (map-indexed (fn [i c] diff --git a/test/flanders/spec_test.clj b/test/flanders/spec_test.clj index 0ca3a6cb..eabb3b5f 100644 --- a/test/flanders/spec_test.clj +++ b/test/flanders/spec_test.clj @@ -1,5 +1,6 @@ (ns flanders.spec-test (:require + [clojure.string :as str] [clojure.core.match :refer [match]] [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as stest] @@ -12,8 +13,8 @@ (use-fixtures :once (fn [t] (stest/instrument 'fs/->spec) - (t) - (stest/unstrument 'fs/->spec))) + (try (t) + (finally (stest/unstrument 'fs/->spec))))) (deftest test-valid-spec (is @@ -139,3 +140,42 @@ (s/form (fs/->spec (f/bool :equals true) "bool")))) (is (= #{false} (s/form (fs/->spec (f/bool :equals false) "bool"))))) + +(deftest conditional-test + (testing "predicates that return true for false work" + (is (s/valid? + (fs/->spec (f/conditional + boolean? f/any-bool) + (str `conditional-test)) + false)) + (is (s/valid? + (fs/->spec (f/conditional + false? (f/bool :equals false)) + (str "conditional-test")) + false))) + (testing "predicates that return true for nil work" + (is (s/valid? + (fs/->spec (f/conditional + nil? f/any) + (str "conditional-test")) + nil))) + (testing "predicates that return false for false and nil work" + (is (not (s/valid? + (fs/->spec (f/conditional + (constantly false) f/any) + (str "conditional-test")) + false))) + (is (not (s/valid? + (fs/->spec (f/conditional + (constantly false) f/any) + (str "conditional-test")) + nil)))) + (testing "condition predicates are taken into account in generators" + (is (s/exercise (fs/->spec (f/conditional + :else f/any-bool) + "conditional-test"))) + (is (thrown-with-msg? Exception + #":spec\.generator/and-generator-failure" + (s/exercise (fs/->spec (f/conditional + (constantly false) f/any-bool) + "conditional-test")))))) From e03fdba23552e52d1657793900f6b8f5658c2072 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Thu, 24 Oct 2024 14:51:05 -0500 Subject: [PATCH 11/15] wip --- src/flanders/spec.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/flanders/spec.clj b/src/flanders/spec.clj index ad0bef96..c8735fd0 100644 --- a/src/flanders/spec.clj +++ b/src/flanders/spec.clj @@ -64,14 +64,16 @@ EitherType (->spec' [{:keys [tests choices]} ns f] - (when (seq tests) - (throw (ex-info (str "WARNING: ")))) (let [choices (->> choices (map-indexed (fn [i c] (let [label (str "choice" i) spec (f c (str ns "." label))] - (eval `(s/def ~(keyword ns label) ~spec)) + (prn spec) + (eval `(s/def ~(keyword ns label) ~(if-some [t (nth tests i nil)] + ;;hmm this doesn't s/exercise + `(s/and ~spec ~t) + spec))) (assoc c :label (keyword label) :spec spec))))) From 3d7599464c228e2e9fd3defea77474ef11e322b5 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Tue, 25 Feb 2025 13:05:50 -0600 Subject: [PATCH 12/15] bump --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d8165f5..5878ab09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@12.5 + uses: DeLaGuardo/setup-clojure@main with: lein: 2.11.2 - name: Cache project dependencies @@ -47,7 +47,7 @@ jobs: java-version: '21' - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@12.5 + uses: DeLaGuardo/setup-clojure@main with: clj-kondo: '2024.09.27' @@ -78,7 +78,7 @@ jobs: distribution: 'temurin' java-version: ${{ matrix.jdk }} - name: Install clojure tools - uses: DeLaGuardo/setup-clojure@12.5 + uses: DeLaGuardo/setup-clojure@main with: lein: 2.11.2 - run: lein do clean, compile :all, test From 74a9d3d9cdea758848149890cb8cafe6a1860e0e Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Tue, 25 Feb 2025 13:24:50 -0600 Subject: [PATCH 13/15] lint --- src/flanders/malli.clj | 3 +-- test/flanders/schema_test.clj | 1 - test/flanders/spec_test.clj | 9 ++++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/flanders/malli.clj b/src/flanders/malli.clj index 1801be2c..571caee8 100644 --- a/src/flanders/malli.clj +++ b/src/flanders/malli.clj @@ -46,8 +46,7 @@ ;; e.g., (f/conditional #(= false %) f/any-bool) will choose true as an example (let [f #(->malli' % opts) ;; note: if test is more narrow than the choice, the example will be wrong. - choice-schemas (mapv f choices) - g (gensym)] + choice-schemas (mapv f choices)] (if-some [tests (some-> (not-empty tests) vec)] (let [ntests (count tests) _ (run! (fn [i] diff --git a/test/flanders/schema_test.clj b/test/flanders/schema_test.clj index 9fbe1998..d8d868dc 100644 --- a/test/flanders/schema_test.clj +++ b/test/flanders/schema_test.clj @@ -6,7 +6,6 @@ [flanders.schema :as fs] [ring.swagger.json-schema :as js] [schema.core :as s] - [schema-tools.core :as st] [schema-generators.generators :as sg])) (deftest test-valid-schema diff --git a/test/flanders/spec_test.clj b/test/flanders/spec_test.clj index eabb3b5f..132a6436 100644 --- a/test/flanders/spec_test.clj +++ b/test/flanders/spec_test.clj @@ -1,6 +1,5 @@ (ns flanders.spec-test (:require - [clojure.string :as str] [clojure.core.match :refer [match]] [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as stest] @@ -151,24 +150,24 @@ (is (s/valid? (fs/->spec (f/conditional false? (f/bool :equals false)) - (str "conditional-test")) + "conditional-test") false))) (testing "predicates that return true for nil work" (is (s/valid? (fs/->spec (f/conditional nil? f/any) - (str "conditional-test")) + "conditional-test") nil))) (testing "predicates that return false for false and nil work" (is (not (s/valid? (fs/->spec (f/conditional (constantly false) f/any) - (str "conditional-test")) + "conditional-test") false))) (is (not (s/valid? (fs/->spec (f/conditional (constantly false) f/any) - (str "conditional-test")) + "conditional-test") nil)))) (testing "condition predicates are taken into account in generators" (is (s/exercise (fs/->spec (f/conditional From 9b1779542b71379ade6914b588114d7c0829a84d Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Tue, 25 Feb 2025 13:25:30 -0600 Subject: [PATCH 14/15] revert --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index aedad574..51d9043c 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,7 @@ :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :pedantic? :abort - :dependencies [[org.clojure/clojure "1.11.4"] + :dependencies [[org.clojure/clojure "1.12.0"] [org.clojure/core.match "1.0.0"] [prismatic/schema "1.2.0"] [metosin/ring-swagger "1.0.0"] From 1a139d70abe87f1f87afaf177bd88bd0b4125187 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Tue, 25 Feb 2025 13:26:08 -0600 Subject: [PATCH 15/15] spec --- src/flanders/spec.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/flanders/spec.clj b/src/flanders/spec.clj index c8735fd0..a1d10b79 100644 --- a/src/flanders/spec.clj +++ b/src/flanders/spec.clj @@ -69,7 +69,6 @@ (fn [i c] (let [label (str "choice" i) spec (f c (str ns "." label))] - (prn spec) (eval `(s/def ~(keyword ns label) ~(if-some [t (nth tests i nil)] ;;hmm this doesn't s/exercise `(s/and ~spec ~t)