From e5f5e72357ec97e43eea177c4fc18d6c47a9bd25 Mon Sep 17 00:00:00 2001 From: Guillaume ERETEO Date: Wed, 11 Mar 2026 16:52:06 +0100 Subject: [PATCH 1/5] Add :spec support to SequenceOfType and SetOfType When provided, the :spec predicate is combined with s/and on the s/coll-of spec, using s/and-spec-impl to avoid closure serialization issues with eval. Follows the same pattern as MapType. This enables adding size constraints to collection fields, e.g.: (f/seq-of f/any-str :spec (pred/max-len 1000)) Backward compatible: no :spec means existing behavior unchanged. --- src/flanders/spec.clj | 18 ++++++++++++---- test/flanders/spec_test.clj | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/flanders/spec.clj b/src/flanders/spec.clj index 87428b33..d60d66b1 100644 --- a/src/flanders/spec.clj +++ b/src/flanders/spec.clj @@ -108,16 +108,26 @@ map-kw)) SequenceOfType - (->spec' [{:keys [type]} ns f] + (->spec' [{:keys [type spec]} ns f] (let [result-kw (keyword ns "seq-of")] (eval `(s/def ~result-kw ~(f type (str ns "." "seq-of")))) - (eval `(s/coll-of ~result-kw)))) + (let [coll-spec (eval `(s/coll-of ~result-kw))] + (if spec + (s/and-spec-impl [`(s/coll-of ~result-kw) `~spec] + [coll-spec spec] + nil) + coll-spec)))) SetOfType - (->spec' [{:keys [type]} ns f] + (->spec' [{:keys [type spec]} ns f] (let [result-kw (keyword ns "set-of")] (eval `(s/def ~result-kw ~(f type (str ns "." "set-of")))) - (eval `(s/coll-of ~result-kw :kind set?)))) + (let [coll-spec (eval `(s/coll-of ~result-kw :kind set?))] + (if spec + (s/and-spec-impl [`(s/coll-of ~result-kw :kind set?) `~spec] + [coll-spec spec] + nil) + coll-spec)))) SignatureType (->spec' [{:keys [parameters rest-parameter return]} ns f] diff --git a/test/flanders/spec_test.clj b/test/flanders/spec_test.clj index 0ca3a6cb..da4c4120 100644 --- a/test/flanders/spec_test.clj +++ b/test/flanders/spec_test.clj @@ -115,6 +115,48 @@ (fs/->spec (f/set-of (f/set-of f/any-str)) "test-set") #{#{"foo"}}))) +(defn max-len [n] + (fn [coll] (<= (count coll) n))) + +(deftest test-seq-of-with-spec + (testing "seq-of with custom spec predicate (max length)" + (is (s/valid? + (fs/->spec (f/seq-of f/any-str :spec #(<= (count %) 3)) + "test-seq-spec-1") + ["a" "b" "c"])) + (is ((complement s/valid?) + (fs/->spec (f/seq-of f/any-str :spec #(<= (count %) 3)) + "test-seq-spec-2") + ["a" "b" "c" "d"]))) + (testing "seq-of with closure spec (e.g. from pred/max-len)" + (is (s/valid? + (fs/->spec (f/seq-of f/any-str :spec (max-len 2)) + "test-seq-spec-closure-1") + ["a" "b"])) + (is ((complement s/valid?) + (fs/->spec (f/seq-of f/any-str :spec (max-len 2)) + "test-seq-spec-closure-2") + ["a" "b" "c"]))) + (testing "seq-of without spec still works" + (is (s/valid? + (fs/->spec (f/seq-of f/any-str) "test-seq-spec-3") + ["a" "b" "c" "d"])))) + +(deftest test-set-of-with-spec + (testing "set-of with custom spec predicate (max length)" + (is (s/valid? + (fs/->spec (f/set-of f/any-str :spec #(<= (count %) 2)) + "test-set-spec-1") + #{"a" "b"})) + (is ((complement s/valid?) + (fs/->spec (f/set-of f/any-str :spec #(<= (count %) 2)) + "test-set-spec-2") + #{"a" "b" "c"}))) + (testing "set-of without spec still works" + (is (s/valid? + (fs/->spec (f/set-of f/any-str) "test-set-spec-3") + #{"a" "b" "c"})))) + (deftest sig-spec-test (let [spec-key (fs/->spec (f/sig :parameters [(f/int)]) "foo")] (is (match (s/describe spec-key) From 23212bcb0861cdf5d1a2f7069d32bb6737a02a8d Mon Sep 17 00:00:00 2001 From: Guillaume ERETEO Date: Mon, 16 Mar 2026 18:04:45 +0100 Subject: [PATCH 2/5] trigger CI From 9f5e25a434adc3409a16ff92a1708cde3aa94c9d Mon Sep 17 00:00:00 2001 From: Guillaume ERETEO Date: Mon, 16 Mar 2026 22:14:52 +0100 Subject: [PATCH 3/5] trigger CI From f8a0974418c2a401dd78877ac5665189ec41ed4b Mon Sep 17 00:00:00 2001 From: Guillaume Buisson Date: Thu, 26 Mar 2026 14:45:33 +0100 Subject: [PATCH 4/5] ci: upgrade runners from ubuntu-20.04 to ubuntu-latest ubuntu-20.04 runners were retired by GitHub, causing all CI jobs to timeout after 24h waiting for a runner. --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a4918654..2e2333a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ on: jobs: setup: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} @@ -48,7 +48,7 @@ jobs: ;; esac lint: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v4 @@ -71,7 +71,7 @@ jobs: needs: setup strategy: matrix: ${{fromJson(needs.setup.outputs.matrix)}} - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout @@ -100,7 +100,7 @@ jobs: env: CMD: ${{ matrix.cmd }} all-pr-checks: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 needs: [test, lint] steps: From e09a24f950f47ab88bdef7a0735299598ea413d6 Mon Sep 17 00:00:00 2001 From: Guillaume ERETEO Date: Thu, 26 Mar 2026 16:04:10 +0100 Subject: [PATCH 5/5] Exclude integration tests from default test run The ocsf-test/test-all-ocsf-versions test requires Docker and external repos (ocsf-server, ocsf-schema) which are not available in CI. Co-Authored-By: Claude Opus 4.6 --- project.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/project.clj b/project.clj index 40dba740..0e154fdb 100644 --- a/project.clj +++ b/project.clj @@ -20,6 +20,11 @@ ["change" "version" "leiningen.release/bump-version"] ["vcs" "commit"] ["vcs" "push"]] + ;; exclude ^:integration tests by default (e.g., ocsf-test/test-all-ocsf-versions requires Docker) + ;; run them explicitly with: lein test :integration + :test-selectors {:default (complement :integration) + :integration :integration + :all (constantly true)} :resource-paths ["resources"] :profiles {:dev {:resource-paths ["test-resources"]