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: 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"] 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)