diff --git a/deps.edn b/deps.edn index 25faf037..55a09e96 100644 --- a/deps.edn +++ b/deps.edn @@ -114,7 +114,13 @@ clojure-term-colors/clojure-term-colors {:mvn/version "0.1.0"} io.aviso/pretty {:mvn/version "1.1.1"} progrock/progrock {:mvn/version "0.1.2"} - djblue/portal {:mvn/version "0.19.0"}} + djblue/portal {:mvn/version "0.19.0"} + com.wsscode/cljc-misc {:mvn/version "2021.10.16"} + edn-query-language/eql {:mvn/version "2021.07.18"} + town.lilac/pyramid {:mvn/version "3.2.0"} + meta-merge/meta-merge {:mvn/version "1.0.0"} + com.exoscale/lingo {:mvn/version "1.0.0-alpha14"} + io.github.swirrl/dogstatsd {:mvn/version "0.1.39"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil}} :clj-nvd diff --git a/doc/dev.md b/doc/dev.md index c3e2876d..57fb9774 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -81,9 +81,19 @@ Test the native version: ## Tests for Libraries Babashka runs tests of libraries that are compatible with it through -`script/run_lib_tests`. To add tests for a new library that has a git repository -and run them, use the script `add-libtest.clj` e.g. `script/add-libtest.clj -'{listora/again {:mvn/version "1.0.0"}}' https://github.com/liwp/again --test`. +`script/run_lib_tests`. The script `add-libtest.clj` makes adding new libraries +fairly easy. Some examples: + +```sh +# To add tests for a new library on clojars +script/add-libtest.clj com.exoscale/lingo -t + +# To add tests for a new library that is git based only +script/add-libtest.clj '{borkdude/carve {:git/url "https://github.com/borkdude/carve" :sha "df552797a198b6701fb2d92390fce7c59205ea77"}}' -t + +# There are a number of options for specifying how to copy tests +script/add-libtest.clj -h +``` If the library you want to add doesn't work automatically, you can manually do the following: diff --git a/doc/libraries.csv b/doc/libraries.csv index 2665caa7..d0f9620e 100644 --- a/doc/libraries.csv +++ b/doc/libraries.csv @@ -12,10 +12,12 @@ clj-commons/clj-yaml,https://github.com/clj-commons/clj-yaml clj-commons/multigrep,https://github.com/clj-commons/multigrep clojure-csv/clojure-csv,https://github.com/davidsantiago/clojure-csv clojure-term-colors/clojure-term-colors,https://github.com/trhura/clojure-term-colors +com.exoscale/lingo,https://github.com/exoscale/lingo com.github.seancorfield/honeysql,https://github.com/seancorfield/honeysql com.grammarly/omniconf,https://github.com/grammarly/omniconf com.stuartsierra/component,https://github.com/stuartsierra/component com.stuartsierra/dependency,https://github.com/stuartsierra/dependency +com.wsscode/cljc-misc,https://github.com/wilkerlucio/cljc-misc comb/comb,https://github.com/weavejester/comb cprop/cprop,https://github.com/tolitius/cprop crispin/crispin,https://github.com/dunaj-project/crispin @@ -23,6 +25,7 @@ dev.nubank/docopt,https://github.com/nubank/docopt.clj djblue/portal,https://github.com/djblue/portal doric/doric,https://github.com/joegallo/doric douglass/clj-psql,https://github.com/DarinDouglass/clj-psql +edn-query-language/eql,https://github.com/edn-query-language/eql environ/environ,https://github.com/weavejester/environ exoscale/coax,https://github.com/exoscale/coax expound/expound,https://github.com/bhb/expound @@ -34,8 +37,10 @@ henryw374/cljc.java-time,https://github.com/henryw374/cljc.java-time hiccup/hiccup,http://github.com/weavejester/hiccup honeysql/honeysql,https://github.com/seancorfield/honeysql http-kit/http-kit,https://github.com/http-kit/http-kit +integrant/integrant,https://github.com/weavejester/integrant io.aviso/pretty,https://github.com/AvisoNovate/pretty io.github.cognitect-labs/test-runner,https://github.com/cognitect-labs/test-runner +io.github.swirrl/dogstatsd,https://github.com/swirrl/dogstatsd io.github.technomancy/limit-break,https://github.com/technomancy/limit-break io.helins/binf,https://github.com/helins/binf.cljc io.replikativ/hasch,https://github.com/replikativ/hasch @@ -44,6 +49,7 @@ lambdaisland/regal,https://github.com/lambdaisland/regal listora/again,https://github.com/liwp/again markdown-clj/markdown-clj,https://github.com/yogthos/markdown-clj medley/medley,https://github.com/weavejester/medley +meta-merge/meta-merge,https://github.com/weavejester/meta-merge minimallist/minimallist,https://github.com/green-coder/minimallist mvxcvi/arrangement,https://github.com/greglook/clj-arrangement orchestra/orchestra,https://github.com/jeaye/orchestra @@ -69,4 +75,5 @@ selmer/selmer,https://github.com/yogthos/Selmer slingshot/slingshot,https://github.com/scgilardi/slingshot table/table,https://github.com/cldwalker/table testdoc/testdoc,https://github.com/liquidz/testdoc +town.lilac/pyramid,https://github.com/lilactown/pyramid version-clj/version-clj,https://github.com/xsc/version-clj diff --git a/test-resources/lib_tests/bb-tested-libs.edn b/test-resources/lib_tests/bb-tested-libs.edn index 2eafea51..e3e31df0 100644 --- a/test-resources/lib_tests/bb-tested-libs.edn +++ b/test-resources/lib_tests/bb-tested-libs.edn @@ -96,4 +96,11 @@ io.aviso/pretty {:git-url "https://github.com/AvisoNovate/pretty", :test-namespaces (io.aviso.binary-test), :git-sha "155926f991f94addaf6f5c8621748924ab144988" :skip-windows true} progrock/progrock {:git-url "https://github.com/weavejester/progrock", :test-namespaces (progrock.core-test), :git-sha "9c277a3244c52bfde19c21add327d6e20b94fdf5"} ;; Don't run portal.jvm-test as it depends on headless chrome - djblue/portal {:git-url "https://github.com/djblue/portal", :test-namespaces (portal.test-runner portal.runtime.cson-test portal.runtime.fs-test portal.e2e portal.bench), :git-sha "64e4624bcf3bee2dd47e3d8e47982c709738eb11"}} + djblue/portal {:git-url "https://github.com/djblue/portal", :test-namespaces (portal.test-runner portal.runtime.cson-test portal.runtime.fs-test portal.e2e portal.bench), :git-sha "64e4624bcf3bee2dd47e3d8e47982c709738eb11"} + integrant/integrant {:git-url "https://github.com/weavejester/integrant", :test-namespaces (integrant.test.foo integrant.test.quz integrant.test.bar integrant.test.baz integrant.core-test), :git-sha "32a46f5dca8a6b563a6dddf88bec887be3201b08"} + com.wsscode/cljc-misc {:git-url "https://github.com/wilkerlucio/cljc-misc", :test-namespaces (com.wsscode.misc.uuid-test com.wsscode.misc.macros-test com.wsscode.misc.math-test com.wsscode.misc.coll-test com.wsscode.misc.refs-test), :git-sha "dc8e31a200f9cacf86af10b63e40fcb448c259f4"} + edn-query-language/eql {:git-url "https://github.com/edn-query-language/eql", :test-namespaces (edn-query-language.core-test), :git-sha "0d4f9745d98c3d20b81bb4bdce3e8e15db7fd094"} + town.lilac/pyramid {:git-url "https://github.com/lilactown/pyramid", :test-namespaces (pyramid.pull-test pyramid.query-test pyramid.core-test), :git-sha "3862d64bc78e789739515191b70a1f6591fd1b68"} + meta-merge/meta-merge {:git-url "https://github.com/weavejester/meta-merge", :test-namespaces (meta-merge.core-test), :git-sha "c968c38baccd4219fe0ba592d89af37ea8e426bf"} + com.exoscale/lingo {:git-url "https://github.com/exoscale/lingo", :test-namespaces (exoscale.lingo.test.core-test), :git-sha "30b5084fab28d24c99ec683e21535366910d9f2f"} + io.github.swirrl/dogstatsd {:git-url "https://github.com/swirrl/dogstatsd", :test-namespaces (swirrl.dogstatsd-test), :git-sha "e110caae452cd1185e65e389a359b69502076d61"}} diff --git a/test-resources/lib_tests/com/wsscode/misc/coll_test.cljc b/test-resources/lib_tests/com/wsscode/misc/coll_test.cljc new file mode 100644 index 00000000..852f3b25 --- /dev/null +++ b/test-resources/lib_tests/com/wsscode/misc/coll_test.cljc @@ -0,0 +1,257 @@ +(ns com.wsscode.misc.coll-test + (:require + [clojure.test :refer [deftest is are run-tests testing]] + [com.wsscode.misc.coll :as coll])) + +(deftest distinct-by-test + (is (= (coll/distinct-by :id + [{:id 1 + :name "foo"} + {:id 2 + :name "bar"} + {:id 1 + :name "other"}]) + [{:id 1 + :name "foo"} + {:id 2 + :name "bar"}]))) + +(deftest dedupe-by-test + (is (= (coll/dedupe-by :id + [{:id 1 + :name "foo"} + {:id 1 + :name "dedup-me"} + {:id 2 + :name "bar"} + {:id 1 + :name "other"}]) + [{:id 1 + :name "foo"} + {:id 2 + :name "bar"} + {:id 1 + :name "other"}]))) + +(deftest index-by-test + (is (= (coll/index-by :id + [{:id 1 + :name "foo"} + {:id 1 + :name "dedup-me"} + {:id 2 + :name "bar"} + {:id 1 + :name "other"}]) + {1 {:id 1, :name "other"}, 2 {:id 2, :name "bar"}}))) + +(deftest find-first-test + (is (= (coll/find-first even? [1 2 3 4]) + 2))) + +(deftest sconj-test + (is (= (coll/sconj nil 42) #{42})) + (is (set? (coll/sconj nil 42)))) + +(deftest vconj-test + (is (= (coll/vconj nil 42) [42])) + (is (vector? (coll/vconj nil 42)))) + +(deftest queue-test + (let [queue (-> (coll/queue) + (conj 1 2))] + (is (= queue [1 2])) + (is (= (peek queue) 1)) + (is (= (pop queue) [2]))) + + (let [queue (coll/queue [1 2])] + (is (= queue [1 2])) + (is (= (peek queue) 1)) + (is (= (pop queue) [2])))) + +(deftest map-keys-test + (is (= (coll/map-keys inc {1 :a 2 :b}) + {2 :a 3 :b}))) + +(deftest filter-keys-test + (is (= (coll/filter-keys simple-keyword? {1 :a 2 :b "foo" 3 :bar 4}) + {:bar 4}))) + +(deftest filter-vals-test + (is (= (coll/filter-vals simple-keyword? {1 :a 2 :b "foo" 3 :bar 4}) + {1 :a 2 :b}))) + +(deftest remove-keys-test + (is (= (coll/remove-keys number? {1 :a 2 :b "foo" 3 :bar 4}) + {"foo" 3 :bar 4}))) + +(deftest remove-vals-test + (is (= (coll/remove-vals number? {1 :a 2 :b "foo" 3 :bar 4}) + {1 :a 2 :b}))) + +(deftest map-vals-test + (is (= (coll/map-vals inc {:a 1 :b 2}) + {:a 2 :b 3}))) + +(deftest keys-set-test + (is (= (coll/keys-set {:a 1 :b 2}) #{:a :b})) + (is (= (coll/keys-set 5) nil))) + +(deftest merge-grow-test + (is (= (coll/merge-grow) {})) + (is (= (coll/merge-grow {:foo "bar"}) {:foo "bar"})) + + (testing "merge sets by union" + (is (= (coll/merge-grow {:foo #{:a}} {:foo #{:b}}) + {:foo #{:a :b}}))) + + (testing "merge maps" + (is (= (coll/merge-grow {:foo {:a 1}} {:foo {:b 2}}) + {:foo {:a 1 :b 2}}))) + + (testing "keep left value if right one is nil" + (is (= (coll/merge-grow {:foo {:a 1}} {:foo {:a nil}}) + {:foo {:a 1}})))) + +(deftest merge-defaults-test + (is (= (coll/merge-defaults {:a 1} {:b 2}) + {:a 1 :b 2})) + (is (= (coll/merge-defaults {:a 1} {:a 2}) + {:a 1}))) + +(deftest assoc-if-test + (is (= (coll/assoc-if {} :foo "bar") + {:foo "bar"})) + + (is (= (coll/assoc-if {} :foo nil) + {})) + + (is (= (coll/assoc-if {} :foo false) + {})) + + (is (= (coll/assoc-if {} :foo false :bar 30) + {:bar 30})) + + (is (= (coll/assoc-if {} :foo false :bar 30 :baz false) + {:bar 30}))) + +(deftest update-contained-test + (is (= (coll/update-contained {:foo 3} :foo inc) + {:foo 4})) + (is (= (coll/update-contained {:foo nil} :foo #(str % " bla")) + {:foo " bla"})) + (is (= (coll/update-contained {} :foo inc) + {}))) + +(deftest update-if-test + (is (= (coll/update-if {:foo 3} :foo inc) + {:foo 4})) + (is (= (coll/update-if {:foo nil} :foo inc) + {:foo nil})) + (is (= (coll/update-if {} :foo inc) + {}))) + +(defrecord CustomRecord []) + +(deftest native-map?-test + (is (= true (coll/native-map? {}))) + (is (= true (coll/native-map? {:foo "bar"}))) + (is (= true (coll/native-map? (zipmap (range 50) (range 50))))) + (is (= false (coll/native-map? (->CustomRecord))))) + +(deftest restore-order-test + (is (= (coll/restore-order + [{:my.entity/id 1} {:my.entity/id 2}] + :my.entity/id + [{:my.entity/id 2 + :my.entity/color :my.entity.color/green} + {:my.entity/id 1 + :my.entity/color :my.entity.color/purple}]) + [{:my.entity/id 1 + :my.entity/color :my.entity.color/purple} + {:my.entity/id 2 + :my.entity/color :my.entity.color/green}])) + (is (= (coll/restore-order + [{:my.entity/id 1} + {:my.entity/id 2} + {:my.entity/id 3}] + :my.entity/id + [{:my.entity/id 3 + :my.entity/color :my.entity.color/green} + {:my.entity/id 1 + :my.entity/color :my.entity.color/purple}]) + [{:my.entity/id 1 + :my.entity/color :my.entity.color/purple} + {:my.entity/id 2} + {:my.entity/id 3 + :my.entity/color :my.entity.color/green}])) + (is (= (coll/restore-order [{:my.entity/id 1} + {:my.entity/id 2} + {:my.entity/id 3}] + :my.entity/id + [{:my.entity/id 3 + :my.entity/color :my.entity.color/green} + {:my.entity/id 1 + :my.entity/color :my.entity.color/purple}] + (fn [x] (assoc x :my.entity/color nil))) + [{:my.entity/id 1 + :my.entity/color :my.entity.color/purple} + {:my.entity/id 2 + :my.entity/color nil} + {:my.entity/id 3 + :my.entity/color :my.entity.color/green}]))) + +(deftest conj-at-index-test + (is (= (coll/conj-at-index [:a :b] 0 :c) + [:c :a :b])) + (is (= (coll/conj-at-index [:a :b] 1 :c) + [:a :c :b])) + (is (= (coll/conj-at-index [:a :b] 2 :c) + [:a :b :c]))) + +(deftest index-of-test + (is (= (coll/index-of [:a {:id :b} :c] :not-here) + nil)) + (is (= (coll/index-of [:a {:id :b} :c] :a) + 0)) + (is (= (coll/index-of [:a {:id :b} :c] {:id :b}) + 1))) + +(deftest coll-append-ahead?-test + (is (true? (coll/coll-append-at-head? (list "foo")))) + (is (true? (coll/coll-append-at-head? (map identity ["foo"])))) + (is (false? (coll/coll-append-at-head? ["foo"]))) + (is (false? (coll/coll-append-at-head? #{"foo"})))) + +(deftest collection?-test + (is (true? (coll/collection? []))) + (is (true? (coll/collection? '()))) + (is (true? (coll/collection? #{}))) + (is (true? (coll/collection? (map identity [])))) + (is (false? (coll/collection? {})))) + +(deftest vector-compare-test + (is (= (coll/vector-compare [0] [1]) + -1)) + (is (= (coll/vector-compare [1] [0]) + 1)) + (is (= (coll/vector-compare [1] [1]) + 0)) + (is (= (coll/vector-compare [2 0] [2 1]) + -1)) + (is (= (coll/vector-compare [2 0 0] [2 1]) + -1))) + +(deftest iterate-while-test + (is (= (coll/iterate-while :n {:x 1 :n {:x 2 :n {:x 3}}}) + [{:x 1, :n {:x 2, :n {:x 3}}} {:x 2, :n {:x 3}} {:x 3}]))) + +(deftest deep-merge-test + (is (= (coll/deep-merge {:a 1} {:b 2}) + {:a 1 :b 2})) + + (is (= (coll/deep-merge {:a {:x 1 :foo "bar"}} {:a {:baz "f" :foo "2"}}) + {:a {:baz "f" :foo "2" :x 1}})) + + (is (= (coll/deep-merge {:a [{:a 1}]} {:a [{:b 2}]}) + {:a [{:b 2}]}))) diff --git a/test-resources/lib_tests/com/wsscode/misc/macros_test.clj b/test-resources/lib_tests/com/wsscode/misc/macros_test.clj new file mode 100644 index 00000000..bd259bb8 --- /dev/null +++ b/test-resources/lib_tests/com/wsscode/misc/macros_test.clj @@ -0,0 +1,14 @@ +(ns com.wsscode.misc.macros-test + (:require + [clojure.test :refer [deftest is are run-tests testing]] + [com.wsscode.misc.macros :as macros])) + +(deftest full-symbol-test + (is (= (macros/full-symbol + 'known/foo + "bar") + 'known/foo)) + (is (= (macros/full-symbol + 'foo + "bar") + 'bar/foo))) diff --git a/test-resources/lib_tests/com/wsscode/misc/math_test.cljc b/test-resources/lib_tests/com/wsscode/misc/math_test.cljc new file mode 100644 index 00000000..65a6daf3 --- /dev/null +++ b/test-resources/lib_tests/com/wsscode/misc/math_test.cljc @@ -0,0 +1,28 @@ +(ns com.wsscode.misc.math-test + (:require + [clojure.test :refer [deftest is are run-tests testing]] + [com.wsscode.misc.math :as math])) + +(deftest floor-test + (is (= (math/floor 30.2) 30)) + (is (= (math/floor 30.9) 30))) + +(deftest round-test + (is (= (math/round 30.2) 30)) + (is (= (math/round 30.6) 31))) + +(deftest ceil-test + (is (= (math/ceil 30.2) 31)) + (is (= (math/ceil 30.9) 31))) + +(deftest divmod-test + (is (= (math/divmod 10 3) + [3 1]))) + +(deftest parse-long-test + (is (= (math/parse-long "21") + 21))) + +(deftest parse-double-test + (is (= (math/parse-double "21.3") + 21.3))) diff --git a/test-resources/lib_tests/com/wsscode/misc/refs_test.cljc b/test-resources/lib_tests/com/wsscode/misc/refs_test.cljc new file mode 100644 index 00000000..34b64141 --- /dev/null +++ b/test-resources/lib_tests/com/wsscode/misc/refs_test.cljc @@ -0,0 +1,36 @@ +(ns com.wsscode.misc.refs-test + (:require + [clojure.test :refer [deftest is are run-tests testing]] + [com.wsscode.misc.refs :refer [atom?] :as refs])) + +(deftest kw-identical?-test + (is (not (refs/kw-identical? :foo :bar))) + (is (not (refs/kw-identical? :foo "foo"))) + (is (refs/kw-identical? :foo :foo)) + (is (refs/kw-identical? :foo (keyword "foo")))) + +(deftest atom?-test + (is (true? (atom? (atom "x")))) + (is (false? (atom? "x")))) + +(deftest greset!-test + (let [x (atom nil)] + (refs/greset! x "val") + (is (= @x "val"))) + + (let [x (volatile! nil)] + (refs/greset! x "val") + (is (= @x "val")))) + +(deftest gswap!-test + (let [x (atom 10)] + (refs/gswap! x inc) + (is (= @x 11))) + + (let [x (volatile! 10)] + (refs/gswap! x inc) + (is (= @x 11))) + + (let [x (volatile! 10)] + (refs/gswap! x + 1 2 3 4 5) + (is (= @x 25)))) diff --git a/test-resources/lib_tests/com/wsscode/misc/uuid_test.cljc b/test-resources/lib_tests/com/wsscode/misc/uuid_test.cljc new file mode 100644 index 00000000..b0bf6673 --- /dev/null +++ b/test-resources/lib_tests/com/wsscode/misc/uuid_test.cljc @@ -0,0 +1,7 @@ +(ns com.wsscode.misc.uuid-test + (:require + [clojure.test :refer [deftest is are run-tests testing]] + [com.wsscode.misc.uuid :as uuid])) + +(deftest cljc-random-uuid-test + (is (uuid? (uuid/cljc-random-uuid)))) diff --git a/test-resources/lib_tests/edn_query_language/core_test.cljc b/test-resources/lib_tests/edn_query_language/core_test.cljc new file mode 100644 index 00000000..17a62b30 --- /dev/null +++ b/test-resources/lib_tests/edn_query_language/core_test.cljc @@ -0,0 +1,386 @@ +(ns edn-query-language.core-test + (:require [clojure.spec.alpha :as s] + [clojure.spec.test.alpha :as s.test] + [clojure.test :refer [deftest is testing]] + [clojure.test.check :as tc] + [clojure.test.check.clojure-test :as test] + [clojure.test.check.generators :as gen] + [clojure.test.check.properties :as props] + [edn-query-language.core :as eql] + [edn-query-language.gen :as eql-gen])) + +(s.test/instrument) + +;; spec tests + +(defn valid-queries-props [] + (props/for-all [query (eql-gen/make-gen {} ::eql-gen/gen-query)] + (s/valid? ::eql/query query))) + +(test/defspec generator-makes-valid-queries {:max-size 12 :num-tests 50} (valid-queries-props)) + +(comment + (tc/quick-check 50 (valid-queries-props) :max-size 12)) + +;; lib tests + +(defn remove-meta [x] + (eql/transduce-children (map #(dissoc % :meta)) x)) + +(defn tquery->ast [query] + (remove-meta (eql/query->ast query))) + +(deftest test-query->ast + (testing "empty query" + (is (= (tquery->ast []) + {:type :root, :children []}))) + + (testing "single property" + (is (= (tquery->ast [:a]) + {:type :root, :children [{:type :prop, :dispatch-key :a, :key :a}]}))) + + (testing "multiple properties" + (is (= (tquery->ast [:a :b]) + {:type :root, + :children [{:type :prop, :dispatch-key :a, :key :a} + {:type :prop, :dispatch-key :b, :key :b}]}))) + + (testing "blank join" + (is (= (tquery->ast [{:a []}]) + {:type :root, + :children [{:type :join, :dispatch-key :a, :key :a, :query [], :children []}]}))) + + (testing "simple join" + (is (= (tquery->ast [{:a [:b]}]) + {:type :root, + :children [{:type :join, + :dispatch-key :a, + :key :a, + :query [:b], + :children [{:type :prop, :dispatch-key :b, :key :b}]}]}))) + + (testing "param expression" + (is (= (tquery->ast ['(:a {:foo "bar"})]) + {:type :root, + :children [{:type :prop, + :dispatch-key :a, + :key :a, + :params {:foo "bar"},}]}))) + + (testing "param join" + (is (= (tquery->ast ['({:a [:sub]} {:foo "bar"})]) + {:type :root, + :children [{:type :join, + :dispatch-key :a, + :key :a, + :query [:sub], + :children [{:type :prop, :dispatch-key :sub, :key :sub}], + :params {:foo "bar"},}]}))) + + (testing "param join 2" + (is (= (tquery->ast [{'(:a {:foo "bar"}) [:sub]}]) + {:type :root + :children [{:children [{:dispatch-key :sub + :key :sub + :type :prop}] + :dispatch-key :a + :key :a + :params {:foo "bar"} + :query [:sub] + :type :join}]}))) + + (testing "union query" + (is (= (tquery->ast [{:foo {:a [:b] + :c [:d]}}]) + {:type :root, + :children [{:type :join, + :dispatch-key :foo, + :key :foo, + :query {:a [:b], :c [:d]}, + :children [{:type :union, + :query {:a [:b], :c [:d]}, + :children [{:type :union-entry, + :union-key :a, + :query [:b], + :children [{:type :prop, :dispatch-key :b, :key :b}]} + {:type :union-entry, + :union-key :c, + :query [:d], + :children [{:type :prop, :dispatch-key :d, :key :d}]}]}]}]}))) + + (testing "unbounded recursion" + (is (= (tquery->ast '[{:item [:a :b {:parent ...}]}]) + '{:type :root, + :children [{:type :join, + :dispatch-key :item, + :key :item, + :query [:a :b {:parent ...}], + :children [{:type :prop, :dispatch-key :a, :key :a} + {:type :prop, :dispatch-key :b, :key :b} + {:type :join, :dispatch-key :parent, :key :parent, :query ...}]}]}))) + + (testing "bounded recursion" + (is (= (tquery->ast '[{:item [:a :b {:parent 5}]}]) + '{:type :root, + :children [{:type :join, + :dispatch-key :item, + :key :item, + :query [:a :b {:parent 5}], + :children [{:type :prop, :dispatch-key :a, :key :a} + {:type :prop, :dispatch-key :b, :key :b} + {:type :join, :dispatch-key :parent, :key :parent, :query 5}]}]}))) + + (testing "mutation expression" + (is (= (tquery->ast ['(a {})]) + '{:type :root, + :children [{:dispatch-key a, + :key a, + :params {}, + :type :call}]}))) + + (testing "mutation join expression" + (is (= (tquery->ast [{'(a {}) [:sub-query]}]) + '{:type :root, + :children [{:dispatch-key a, + :key a, + :params {}, + :type :call, + :query [:sub-query], + :children [{:type :prop, :dispatch-key :sub-query, :key :sub-query}]}]})))) + +(defn query<->ast-props [] + (props/for-all [query (eql-gen/make-gen {::eql-gen/gen-params + (fn [_] + (gen/map gen/keyword gen/string-alphanumeric))} + ::eql-gen/gen-query)] + (let [ast (-> query + eql/query->ast + eql/ast->query + eql/query->ast)] + (= ast (-> ast + eql/ast->query + eql/query->ast))))) + +(test/defspec query-ast-roundtrip {:max-size 12 :num-tests 100} (query<->ast-props)) + +(comment + (tc/quick-check 100 (query<->ast-props) :max-size 12)) + +(deftest test-ast->query + (is (= (eql/ast->query {:type :prop + :key :foo + :dispatch-key :foo}) + [:foo])) + + (is (= (eql/ast->query {:type :root + :children [{:type :prop + :dispatch-key :foo + :key :foo}]}) + [:foo]))) + +(deftest test-focus-subquery + (is (= (eql/focus-subquery [] []) + [])) + (is (= (eql/focus-subquery [:a :b :c] []) + [])) + (is (= (eql/focus-subquery [:a :b :c] [:d]) + [])) + (is (= (eql/focus-subquery [:a :b :c] [:a]) + [:a])) + (is (= (eql/focus-subquery [:a :b :c] [:a :b]) + [:a :b])) + (is (= (eql/focus-subquery [:a {:b [:d]}] [:a :b]) + [:a {:b [:d]}])) + (is (= (eql/focus-subquery [:a {:b [:c :d]}] [:a {:b [:c]}]) + [:a {:b [:c]}])) + (is (= (eql/focus-subquery [:a '({:b [:c :d]} {:param "value"})] [:a {:b [:c]}]) + [:a '({:b [:c]} {:param "value"})])) + + ; in union case, keys absent from focus will be pulled anyway, given ones will focus + (is (= (eql/focus-subquery [:a {:b {:c [:d :e] + :f [:g :h]}}] + [:a {:b {:f [:g]}}]) + [:a {:b {:c [:d :e] :f [:g]}}]))) + +(defn transduce-query [xform query] + (->> query eql/query->ast + (eql/transduce-children xform) + eql/ast->query)) + +(deftest test-tranduce-children + (is (= (transduce-query + (comp (filter (comp #{:a :c} :key)) + (map #(assoc % :params {:n 42}))) + [:a :b :c :d]) + '[(:a {:n 42}) (:c {:n 42})]))) + +(deftest test-merge-queries + (is (= (eql/merge-queries nil nil) + [])) + + (is (= (eql/merge-queries [:a] nil) + [:a])) + + (is (= (eql/merge-queries [] []) + [])) + + (is (= (eql/merge-queries [:a] []) + [:a])) + + (is (= (eql/merge-queries [:a] [:a]) + [:a])) + + (is (= (eql/merge-queries [:a] [:b]) + [:a :b])) + + (is (= (eql/merge-queries [:a] [:b :c :d]) + [:a :b :c :d])) + + (is (= (eql/merge-queries [[:u/id 1]] [[:u/id 2]]) + [[:u/id 1] [:u/id 2]])) + + (is (= (eql/merge-queries [{:user [:name]}] [{:user [:email]}]) + [{:user [:name :email]}])) + + (is (= (eql/merge-queries [:a] [{:a [:x]}]) + [{:a [:x]}])) + + (is (= (eql/merge-queries [{:a [:x]}] [:a]) + [{:a [:x]}])) + + (testing "don't merge queries with different params" + (is (= (eql/merge-queries ['({:user [:name]} {:login "u1"})] + ['({:user [:email]} {:login "u2"})]) + nil))) + + (testing "don't merge queries with different params" + (is (= (eql/merge-queries ['(:user {:login "u1"})] + ['(:user {:login "u2"})]) + nil))) + + (testing "merge when params are same" + (is (= (eql/merge-queries ['({:user [:name]} {:login "u1"})] + ['({:user [:email]} {:login "u1"})]) + ['({:user [:name :email]} {:login "u1"})]))) + + (testing "calls can't be merged when same name occurs" + (is (= (eql/merge-queries ['(hello {:login "u1"})] + ['(hello {:bla "2"})]) + nil))) + + (testing "even when parameters are the same" + (is (= (eql/merge-queries ['(hello {:login "u1"})] + ['(hello {:login "u1"})]) + nil)))) + +(deftest test-update-child + (is (= (eql/update-child {:children [{:dispatch-key :id :key :id :type :prop} + {:dispatch-key :parent :key :parent :query 3 :type :join}] + :type :root} + :parent update :query dec) + {:children [{:dispatch-key :id :key :id :type :prop} + {:dispatch-key :parent :key :parent :query 2 :type :join}] + :type :root}))) + +(deftest update-recursive-depth-test + (is (= (eql/update-recursive-depth + {:children [{:dispatch-key :id :key :id :type :prop} + {:dispatch-key :parent :key :parent :query 3 :type :join}] + :type :root} + :parent dec) + {:children [{:dispatch-key :id :key :id :type :prop} + {:dispatch-key :parent :key :parent :query 2 :type :join}] + :type :root}))) + +(deftest test-mask-query + (is (= (eql/mask-query [] []) + [])) + (is (= (eql/mask-query [:foo :bar] []) + [])) + (is (= (eql/mask-query [:foo :bar] [:foo]) + [:foo])) + (is (= (eql/mask-query [:bar :foo] [:foo]) + [:foo])) + (is (= (eql/mask-query [:foo {:bar [:inside]}] [:foo]) + [:foo])) + (is (= (eql/mask-query ['(:foo {:bla "meh"}) :bar] [:foo]) + ['(:foo {:bla "meh"})])) + (is (= (eql/mask-query [:foo {:bar [:inside :more]}] [:foo :bar]) + [:foo {:bar [:inside :more]}])) + (is (= (eql/mask-query [:foo {:bar [:inside :more]}] [:foo {:bar [:inside]}]) + [:foo {:bar [:inside]}]))) + +(deftest test-normalize-query-variables + (testing "blank query" + (is (= (eql/normalize-query-variables []) + []))) + + (testing "simple query" + (is (= (eql/normalize-query-variables [:a :b :c]) + [:a :b :c]))) + + (testing "normalize ident values" + (is (= (eql/normalize-query-variables [[:foo "bar"]]) + [[:foo ::eql/var]]))) + + (testing "normalize params" + (is (= (eql/normalize-query-variables ['(:foo {:x 1 :y 2})]) + ['(:foo {:x ::eql/var :y ::eql/var})]))) + + (testing "all together" + (is (= (eql/normalize-query-variables '[:a :b {[:join "val"] [{(:c {:page 10}) [:d]}]}]) + '[:a :b + {[:join ::eql/var] + [({:c [:d]} + {:page ::eql/var})]}])))) + +(deftest test-query-id + (is (= (eql/query-id '[:a :b {[:join "val"] [{(:c {:page 10}) [:d]}]}]) + -61421281))) + + +(deftest shallow-conversion + (testing "requesting shallow conversion will only convert the first layer of a query" + (let [ast (eql/query->shallow-ast [:x + {:y [{:z [:a]}]} + {[:table 1] [:z {:other [:m :n]}]} + {:ujoin {:u1 [:x] :u2 [:y]}}])] + (is (= {:type :root, + :children [{:type :prop, :dispatch-key :x, :key :x} + ;; BB-TEST-PATCH: bb returns {} for some meta calls that clojure doesn't + {:type :join, :dispatch-key :y, :key :y, :query [{:z [:a]}] :meta {}} + {:type :join, :dispatch-key :table, :key [:table 1], :query [:z {:other [:m :n]}] :meta {}} + {:type :join, :dispatch-key :ujoin, :key :ujoin, :query {:u1 [:x], :u2 [:y]} :meta {}}]} + ast))))) + + +(deftest merge-asts-as-reduce-function + (testing + "init - when called with arity zero, it returns an empty ast" + (is (= {:type :root + :children []} + (transduce (map identity) + eql/merge-asts + [])))) + (testing + "completion - when called with arity one, it should return its argument" + (is (= {:children [{:dispatch-key :a + :key :a + :type :prop}] + :type :root} + (transduce (map identity) + eql/merge-asts + [(eql/query->ast [:a])])))) + (testing + "step - the old arity 2. Should compose both nodes into a new node" + (is (= {:children [{:dispatch-key :a + :key :a + :type :prop} + {:dispatch-key :b + :key :b + :type :prop}] + :type :root} + (transduce (map identity) + eql/merge-asts + [(eql/query->ast [:a]) + (eql/query->ast [:b])]))))) diff --git a/test-resources/lib_tests/exoscale/lingo/test/core_test.cljc b/test-resources/lib_tests/exoscale/lingo/test/core_test.cljc new file mode 100644 index 00000000..d6fa482c --- /dev/null +++ b/test-resources/lib_tests/exoscale/lingo/test/core_test.cljc @@ -0,0 +1,385 @@ +(ns exoscale.lingo.test.core-test + (:require [clojure.test :refer [are deftest is]] + [exoscale.lingo :as l] + [exoscale.lingo.impl :as impl] + [exoscale.lingo.highlight :as u] + [clojure.spec.alpha :as s])) + +(defn f2? [_] false) +(defn f3? [_] false) + +(l/set-spec-error! `exoscale.lingo.test.core-test/f2? "yolo") +(l/set-spec-error! `f3? "should match Something") + +(-> (s/def ::thing #(string? %)) + (l/set-spec-error! "should be a string with bla bla bla")) + +(s/def ::things (s/coll-of ::thing)) + +(s/def :foo/name string?) + +(s/def :foo/names (s/coll-of :foo/name)) + +(s/def :foo/person (s/keys :req-un [:foo/names])) + +(s/def :foo/age int?) +(s/def :foo/agent (s/keys :req-un [:foo/person :foo/age])) + +(s/def :foo/agent2 (s/keys :req-un [:foo/person :foo/age])) + +(def ^:dynamic *opts* {:highlight? false + :group-missing-keys? false + :group-or-problems? false + :header? false}) + +(deftest test-outputs + (are [spec val output] (= (l/explain-str spec val *opts*) + output) + + ::thing + 1 + "1 is an invalid :exoscale.lingo.test.core-test/thing - should be a string with bla bla bla\n" + + (s/coll-of ::thing) + [1] + "1 in `[0]` is an invalid :exoscale.lingo.test.core-test/thing - should be a string with bla bla bla\n" + + ::things + [1] + "1 in `[0]` is an invalid :exoscale.lingo.test.core-test/thing - should be a string with bla bla bla\n" + + ;; test traversing + (s/def ::things2 ::things) + [1] + "1 in `[0]` is an invalid :exoscale.lingo.test.core-test/thing - should be a string with bla bla bla\n" + + ::things + 1 + "1 is an invalid :exoscale.lingo.test.core-test/things - should be a Collection\n" + + (s/and string? #(> (count %) 3)) + "" + "\"\" is invalid - should contain more than 3 elements\n" + + (s/def ::cnt #(> (count %) 3)) + "" + "\"\" is an invalid :exoscale.lingo.test.core-test/cnt - should contain more than 3 elements\n" + + ;; test the original unchanged msg + (s/and string? #(pos? (count %))) + "" + "\"\" is invalid - (pos? (count %))\n" + + ;; with a custom pred matcher + (do + (l/set-pred-error! #{'(pos? (count %))} (constantly "should be non blank")) + (s/and string? #(pos? (count %)))) + "" + "\"\" is invalid - should be non blank\n" + + #{:a :b :c} + "b" + "\"b\" is invalid - should be one of :a, :b, :c\n" + + ;; (s/and string? #(xss/string-of* % {:blank? false :min-length 3 :max-length 10})) + ;; "" + ;; "\"\" is invalid - should be a String non blank, at least 3 characters in length, at most 10 characters in length\n" + (s/def :exoscale.lingo/c1 (s/map-of int? int? :count 3)) + {"a" "b"} + "{\"a\" \"b\"} is an invalid :exoscale.lingo/c1 - should contain exactly 3 elements\n" + + (s/and any? #(= 1 (count %))) + [] + "[] is invalid - should contain exactly 1 element\n" + + (s/and any? #(= (count %) 1)) + [] + "[] is invalid - should contain exactly 1 element\n" + + (s/and any? #(= 42 (count %))) + [] + "[] is invalid - should contain exactly 42 elements\n" + + (s/and any? #(= (count %) 42)) + [] + "[] is invalid - should contain exactly 42 elements\n" + + (s/and any? #(>= (count %) 42)) + [] + "[] is invalid - should contain at least 42 elements\n" + + (s/and any? #(<= (count %) 1)) + [1 1] + "[1 1] is invalid - should contain at most 1 element\n" + + (s/and any? #(<= % 1)) + 10 + "10 is invalid - should be at most 1\n" + + (s/and any? #(< % 1)) + 10 + "10 is invalid - should be less than 1\n" + + (s/and any? #(>= % 1)) + 0 + "0 is invalid - should be at least 1\n" + + (s/and any? #(> % 1)) + 0 + "0 is invalid - should be greater than 1\n" + + (s/and any? #(= % "yolo")) + 0 + "0 is invalid - should be equal to yolo\n" + + (s/and any? #(= "yolo" %)) + 0 + "0 is invalid - should be equal to yolo\n" + + (s/int-in 0 10) + -1 + "-1 is invalid - should be an Integer between 0 10\n" + + (s/and number? #(<= 0 % 10)) + -1 + "-1 is invalid - should be an Integer between 0 10\n" + + (s/double-in :min 0 :max 10) + (double 11) + "11.0 is invalid - should be at most 10\n" + + (s/coll-of any? :min-count 3) + [1] + "[1] is invalid - should contain at least 3 elements\n" + + (s/coll-of any? :max-count 3) + [1 1 1 1] + "[1 1 1 1] is invalid - should contain between 0 3 elements\n" + + (s/coll-of any? :max-count 3 :min-count 1) + [1 1 1 1] + "[1 1 1 1] is invalid - should contain between 1 3 elements\n" + + (s/coll-of any? :count 3) + [1 1 1 1] + "[1 1 1 1] is invalid - should contain exactly 3 elements\n" + + (s/coll-of any? :count 1) + [1 1 1 1] + "[1 1 1 1] is invalid - should contain exactly 1 element\n" + + (s/coll-of any? :kind set?) + [1] + "[1] is invalid - should be a Set\n" + + (s/map-of any? any? :count 1) + {:a 1 :b 2} + "{:a 1, :b 2} is invalid - should contain exactly 1 element\n" + + neg-int? + [1] + "[1] is invalid - should be a Negative Integer\n" + + (s/def :foo/agent (s/keys :req-un [:foo/person :foo/age])) + {:age 10} + "{:age 10} is an invalid :foo/agent - missing key :person\n" + + (s/def :foo/agent (s/keys :req [:foo/person :foo/age])) + {:foo/age 10} + "#:foo{:age 10} is an invalid :foo/agent - missing key :foo/person\n" + + (do + (alter-var-root #'*opts* assoc :hide-keyword-namespaces? true) + (s/def :foo/agent (s/keys :req [:foo/person :foo/age]))) + {:foo/age 10} + "#:foo{:age 10} is an invalid :foo/agent - missing key :person\n" + + (do + (alter-var-root #'*opts* dissoc :hide-keyword-namespaces?) + (s/def :foo/agent (s/keys :req-un [:foo/person :foo/age]))) + {:age 10 :person {:names [1]}} + "1 in `person.names[0]` is an invalid :foo/name - should be a String\n" + + (-> (s/def :foo/agent2 (s/keys :req-un [:foo/person :foo/age])) + ;; (xs/with-meta! {:exoscale.lingo/name "Agent"}) + ) + {:age ""} + "\"\" in `age` is an invalid :foo/age - should be an Integer\n{:age \"\"} is an invalid :foo/agent2 - missing key :person\n" + + (s/def :foo/animal #{:a :b :c}) + 1 + "1 is an invalid :foo/animal - should be one of :a, :b, :c\n" + + :foo/person + {:names [1 :yolo]} + "1 in `names[0]` is an invalid :foo/name - should be a String\n:yolo in `names[1]` is an invalid :foo/name - should be a String\n" + + nil? + 1 + "1 is invalid - should be nil\n" + + (s/nilable string?) + 1 + "1 is invalid - should be a String\n1 is invalid - should be nil\n" + + ;; BB-TEST-PATCH: bb returns sci details instead of string + #_f2? + #_1 + #_"1 is invalid - yolo\n" + + ;; BB-TEST-PATCH: bb returns sci details instead of string + #_f3? + #_1 + #_"1 is invalid - should match Something\n")) + +(deftest focus-test + (let [_ '_] + (is (= [_ _ _] (u/focus [3 2 1] nil))) + (is (= _ (u/focus 1 nil))) + (is (= 1 (u/focus 1 []))) + + (is (= [_ _ 1] (u/focus [3 2 1] [2]))) + (is (= [3 _ _] (u/focus [3 2 1] [0]))) + + (is (= {:a 1} (u/focus {:a 1} [:a]))) + (is (= {:a _} (u/focus {:a 1} [:b]))) + (is (= {:a _ :c 1} (u/focus {:a {:b 1} :c 1} [:c]))) + + (is (= {:a {:b [_ {:c {:d #{:b :a}, :e _}}]}} + (u/focus {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + [:a :b 1 :c :d] + {:descend-mismatching-nodes? true}))) + + (is (= {:a {:b [1 {:c {:d #{_}, :e _}}]}} + (u/focus {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + [:a :b 0] + {:descend-mismatching-nodes? true}))) + + (is (= {:a {:b [_ {:c {:d #{_}, :e _}}]}} + (u/focus {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + nil + {:descend-mismatching-nodes? true}))) + + (is (= {:a {:b [1 _]}} + (u/focus {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + [:a :b 0]))) + + (is (= {:a {:b [_ {:c {:d #{:b :a} :e _}}]}} + (u/focus {:a {:b [1 {:c {:d #{:b :a} :e {:f 1}}}]}} + [:a :b 1 :c :d]))))) + +(deftest highlight-test + (are [input path output] + (= (u/highlight input path {:focus? true}) + output) + + [3 2 1] {:in [2] :val 1} "[_ _ 1]\n ^" + + [3 2 1] {:in [0] :val 3} "[3 _ _]\n ^" + + {:a 1} {:in [:a] :val 1} "{:a 1}\n ^" + + {:a {:b 1} :c 1} {:in [:c] :val 1} "{:a _, :c 1}\n ^" + + {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + {:in [:a :b 1 :c :d] :val #{:a :b}} + "{:a {:b [_ {:c {:d #{:b :a}, :e _}}]}}\n ^^^^^^^^" + + {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + {:in [:a :b 0] :val 1} + "{:a {:b [1 _]}}\n ^" + + {:a {:b [1 {:c {:d #{:a :b} :e :foo}}]}} + {:in [:a :b 0] :val 1} + "{:a {:b [1 _]}}\n ^" + + {:a {:b [1 {:c {:d #{:b :a} :e {:f 1}}}]}} + {:in [:a :b 1 :c :d] :val #{:b :a}} + "{:a {:b [_ {:c {:d #{:b :a}, :e _}}]}}\n ^^^^^^^^" + + ;; single line hl + {:a {:bar 255555 :c 3 :d 4 :e 5}} + {:in [:a :bar] :val 255555} + "{:a {:bar 255555, :c _, :d _, :e _}}\n ^^^^^^" + + ;; ;; multiline hl output + {:aaaaaaaaaaaaa + {:bbbbbbbbbbbbbbbbbdddddddddddddddddddddddddddddddddddddd 2 :c 33333 :d 4 :e 5}} + {:in [:aaaaaaaaaaaaa :c] :val 33333} + "{:aaaaaaaaaaaaa\n {:bbbbbbbbbbbbbbbbbdddddddddddddddddddddddddddddddddddddd _,\n :c 33333,\n ^^^^^\n :d _,\n :e _}}") + (is (= ["[1]\n ^ should be a string with bla bla bla"] + (->> (l/explain-data ::things [1]) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/highlight))))) + +(deftest test-group-map-keys + (is (= "missing keys :age, :person" + (-> (l/explain-data :foo/agent2 {} {:group-missing-keys? true}) + :clojure.spec.alpha/problems + first + :exoscale.lingo.explain/message))) + + (is (= #{"missing keys :age, :person" + "missing keys :names"} + (->> (l/explain-data (s/tuple :foo/agent2 :foo/person) + [{} {}] + {:group-missing-keys? true}) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/message) + set)))) + +(deftest test-group-or-keys + (s/def ::test-group-or-keys (s/nilable string?)) + (s/def ::test-group-or-keys2 (s/or :str string? :int int?)) + (is (= #{"should be a String OR should be nil"} + (->> (l/explain-data ::test-group-or-keys + 1 + {:group-or-problems? true + :group-missing-keys? true}) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/message) + set))) + (is (= #{"should be a String OR should be an Integer"} + (->> (l/explain-data ::test-group-or-keys2 + :kw + {:group-or-problems? true + :group-missing-keys? true}) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/message) + set))) + + (is (= #{"should be a String OR should be nil"} + (->> (l/explain-data (s/coll-of (s/or :_ ::test-group-or-keys + :_ string?)) + ["" 1] + {:group-or-problems? true + :group-missing-keys? true}) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/message) + set)) + "ensure there is no duplication of messages in the final pb string") + + (s/def ::test-group-or-keys3 int?) + (is (= #{"should be a String OR should be nil" + "should be a String OR should be an Integer" + "should be an Integer"} + (->> (l/explain-data (s/keys :req-un [::test-group-or-keys]) + {:test-group-or-keys 1 + ::test-group-or-keys2 :boom + ::test-group-or-keys3 ""} + {:group-or-problems? true + :group-missing-keys? true}) + :clojure.spec.alpha/problems + (map :exoscale.lingo.explain/message) + set)) + "grouping does not alter the other problems")) + +(deftest fix-map-path-test + (is (= [] (impl/fix-map-path [] []))) + (is (= [] (impl/fix-map-path {} []))) + (is (= [:a] (impl/fix-map-path {:a 1} [:a 1]))) + (is (= [:a :b :c] (impl/fix-map-path {:a {:b {:c 1}}} [:a 1 :b 1 :c 1]))) + (is (= [:a :b :c 0] (impl/fix-map-path {:a {:b {:c [1]}}} + [:a 1 :b 1 :c 1 0]))) + (is (= [:a :b :c 1 :d] + (impl/fix-map-path {:a {:b {:c [{} {:d 1}]}}} [:a 1 :b 1 :c 1 1 :d 1])))) diff --git a/test-resources/lib_tests/integrant/core_test.cljc b/test-resources/lib_tests/integrant/core_test.cljc index f8cae2ad..065bb5a5 100644 --- a/test-resources/lib_tests/integrant/core_test.cljc +++ b/test-resources/lib_tests/integrant/core_test.cljc @@ -98,6 +98,7 @@ "{:foo/a #test/var clojure.core/+}") {:foo/a #'+})))) +;; BB-TEST-PATCH: No *loaded-libs* in bb #?(:bb :TODO :clj (defn- remove-lib [lib] (remove-ns lib) @@ -105,6 +106,7 @@ (derive :integrant.test-child/foo :integrant.test/foo) +;; BB-TEST-PATCH: No *loaded-libs* in bb #?(:bb :TODO :clj (deftest load-namespaces-test diff --git a/test-resources/lib_tests/meta_merge/core_test.cljc b/test-resources/lib_tests/meta_merge/core_test.cljc new file mode 100644 index 00000000..a737e362 --- /dev/null +++ b/test-resources/lib_tests/meta_merge/core_test.cljc @@ -0,0 +1,57 @@ +(ns meta-merge.core-test + (:require #?(:clj [clojure.test :refer :all] + :cljs [cljs.test :refer-macros [deftest is testing]]) + [meta-merge.core :refer [meta-merge]])) + +(deftest test-meta-merge + (testing "simple merge" + (is (= (meta-merge {:a 1 :b 2} {:b 3 :c 4}) + {:a 1 :b 3 :c 4}))) + + (testing "inner map merge" + (is (= (meta-merge {:a {:b 1 :c 2}} {:a {:c 3}}) + {:a {:b 1 :c 3}}))) + + (testing "inner set merge" + (is (= (meta-merge {:a #{:b :c}} {:a #{:c :d}}) + {:a #{:b :c :d}}))) + + (testing "inner vector merge" + (is (= (meta-merge {:a [:b :c]} {:a [:d]}) + {:a [:b :c :d]}))) + + (testing "meta replace" + (is (= (meta-merge {:a [:b :c]} {:a ^:replace [:d]}) + {:a [:d]}))) + + (testing "meta displace" + (is (= (meta-merge {:a [:b :c]} {:a ^:displace [:d]}) + {:a [:b :c]}))) + + (testing "meta prepend" + (is (= (meta-merge {:a [:b :c]} {:a ^:prepend [:d]}) + {:a [:d :b :c]}))) + + (testing "deep inner merge" + (is (= (meta-merge {:a {:b {:c [:d]}}} {:a {:b {:c [:e] :f :g}}}) + {:a {:b {:c [:d :e] :f :g}}}))) + + (testing "collection type remains the same" + (is (map? (meta-merge {:a :b} {:c :d}))) + (is (vector? (meta-merge [:a :b] [:c]))) + (is (set? (meta-merge #{:a :b} #{:c}))) + (is (list? (meta-merge '(:a :b) '(:c))))) + + (testing "nil displace" + (is (= (meta-merge {:b :c} {:a ^:displace [:d]}) + {:a [:d] :b :c}))) + + (testing "varargs" + (is (= (meta-merge) + {})) + (is (= (meta-merge {:a :b}) + {:a :b})) + (is (= (meta-merge {:a :b :x 1} {:a :c :y 2} {:a :d}) + {:a :d :x 1 :y 2})) + (is (= (meta-merge {:a :b :x 1} {:a :c :y 2} {:a :d} {:y 4 :z 3}) + {:a :d :x 1 :y 4 :z 3})))) diff --git a/test-resources/lib_tests/meta_merge/test_runner.cljs b/test-resources/lib_tests/meta_merge/test_runner.cljs new file mode 100644 index 00000000..134b8e9b --- /dev/null +++ b/test-resources/lib_tests/meta_merge/test_runner.cljs @@ -0,0 +1,5 @@ +(ns meta-merge.test-runner + (:require [doo.runner :refer-macros [doo-tests]] + [meta-merge.core-test])) + +(doo-tests 'meta-merge.core-test) diff --git a/test-resources/lib_tests/pyramid/core_test.cljc b/test-resources/lib_tests/pyramid/core_test.cljc new file mode 100644 index 00000000..361f75b9 --- /dev/null +++ b/test-resources/lib_tests/pyramid/core_test.cljc @@ -0,0 +1,574 @@ +(ns pyramid.core-test + (:require + [pyramid.core :as p] + [pyramid.ident :as ident] + [clojure.test :as t])) + + +(t/deftest normalization + (t/is (= {:person/id {0 {:person/id 0}}} + (p/db [{:person/id 0}])) + "a single entity") + (t/is (= {:person/id {0 {:person/id 0 + :person/name "asdf"} + 1 {:person/id 1 + :person/name "jkl"}}} + (p/db [{:person/id 0 + :person/name "asdf"} + {:person/id 1 + :person/name "jkl"}])) + "multiple entities with attributes") + (t/is (= {:person/id {0 {:person/id 0 + :person/name "asdf"} + 1 {:person/id 1 + :person/name "jkl"}} + :people [[:person/id 0] + [:person/id 1]]} + (p/db [{:people [{:person/id 0 + :person/name "asdf"} + {:person/id 1 + :person/name "jkl"}]}])) + "nested under a key") + (t/is (= {:person/id {0 {:person/id 0 + :some-data {1 "hello" + 3 "world"}}}} + (p/db [{:person/id 0 + :some-data {1 "hello" + 3 "world"}}])) + "Map with numbers as keys") + (t/is (= {:a/id {1 {:a/id 1 + :b [{:c [:d/id 1]}]}} + :d/id {1 {:d/id 1 + :d/txt "a"}}} + (p/db [{:a/id 1 + :b [{:c {:d/id 1 + :d/txt "a"}}]}])) + "Collections of non-entities still get normalized") + (t/is (= {:person/id + {123 + {:person/id 123, + :person/name "Will", + :contact {:phone "000-000-0001"}, + :best-friend [:person/id 456], + :friends + [[:person/id 9001] + [:person/id 456] + [:person/id 789] + [:person/id 1000]]}, + 456 + {:person/id 456, + :person/name "Jose", + :account/email "asdf@jkl", + :best-friend [:person/id 123]}, + 9001 #:person{:id 9001, :name "Georgia"}, + 789 #:person{:id 789, :name "Frank"}, + 1000 #:person{:id 1000, :name "Robert"}}} + (p/db [{:person/id 123 + :person/name "Will" + :contact {:phone "000-000-0001"} + :best-friend + {:person/id 456 + :person/name "Jose" + :account/email "asdf@jkl"} + :friends + [{:person/id 9001 + :person/name "Georgia"} + {:person/id 456 + :person/name "Jose"} + {:person/id 789 + :person/name "Frank"} + {:person/id 1000 + :person/name "Robert"}]} + {:person/id 456 + :best-friend {:person/id 123}}])) + "refs")) + + +(t/deftest non-entities + (t/is (= {:foo ["bar"]} (p/db [{:foo ["bar"]}]))) + (t/is (= {:person/id {0 {:person/id 0 + :foo ["bar"]}}} + (p/db [{:person/id 0 + :foo ["bar"]}])))) + + +(t/deftest custom-schema + (t/is (= {:color {"red" {:color "red" :hex "#ff0000"}}} + (p/db [{:color "red" :hex "#ff0000"}] + (ident/by-keys :color))) + "ident/by-keys") + (t/is (= {:color {"red" {:color "red" :hex "#ff0000"}}} + (p/db [^{:db/ident :color} + {:color "red" :hex "#ff0000"}])) + "local schema") + (t/testing "complex schema" + (let [db (p/db [{:type "person" + :id "1234" + :purchases [{:type "item" + :id "1234"}]} + {:type "item" + :id "5678"} + {:type "foo"} + {:id "bar"}] + (fn [entity] + (let [{:keys [type id]} entity] + (when (and (some? type) (some? id)) + [(keyword type "id") id]))))] + (t/is (= {:person/id + {"1234" {:type "person", :id "1234", :purchases [[:item/id "1234"]]}}, + :item/id + {"1234" {:type "item", :id "1234"}, "5678" {:type "item", :id "5678"}}, + :type "foo", + :id "bar"} + db) + "correctly identifies entities") + (t/is (= {[:person/id "1234"] + {:type "person", :id "1234", :purchases [{:type "item", :id "1234"}]}} + (p/pull db [{[:person/id "1234"] [:type :id {:purchases [:type :id]}]}])) + "pull")))) + + +(t/deftest add + (t/is (= {:person/id {0 {:person/id 0}}} + (p/add {} {:person/id 0}))) + (t/is (= {:person/id {0 {:person/id 0 :person/name "Gill"} + 1 {:person/id 1}}} + (p/add + {} + {:person/id 0} + {:person/id 1} + {:person/id 0 :person/name "Gill"})))) + + +(t/deftest add-report + (t/is (= {:db {:person/id {0 {:person/id 0}}} + :entities #{[:person/id 0]}} + (p/add-report {} {:person/id 0}))) + (t/is (= {:db {:person/id {0 {:person/id 0 + :person/name "Gill" + :best-friend [:person/id 1]} + 1 {:person/id 1 + :person/name "Uma"}} + :me [:person/id 0]} + :entities #{[:person/id 0] + [:person/id 1]}} + (p/add-report {} {:me {:person/id 0 + :person/name "Gill" + :best-friend {:person/id 1 + :person/name "Uma"}}}))) + #_(t/is (= {:db {:person/id {0 {:person/id 0 :person/name "Gill"} + 1 {:person/id 1}}} + :entities #{{:person/id 0 :person/name "Gill"} + {:person/id 1}}} + (p/add-report + {} + {:person/id 0} + {:person/id 1} + {:person/id 0 :person/name "Gill"})))) + + +(def data + {:people/all [{:person/id 0 + :person/name "Alice" + :person/age 25 + :best-friend {:person/id 1} + :person/favorites + {:favorite/ice-cream "vanilla"}} + {:person/id 1 + :person/name "Bob" + :person/age 23}]}) + + +(def db + (p/db [data])) + + +(t/deftest pull + (t/is (= #:people{:all [{:person/id 0} {:person/id 1}]} + (p/pull db [:people/all])) + "simple key") + (t/is (= {:people/all [{:person/name "Alice" + :person/id 0} + {:person/name "Bob" + :person/id 1}]} + (p/pull db [{:people/all [:person/name :person/id]}])) + "basic join + prop") + (t/is (= #:people{:all [{:person/name "Alice" + :person/id 0 + :best-friend #:person{:name "Bob", :id 1 :age 23}} + #:person{:name "Bob", :id 1}]} + (p/pull db [#:people{:all [:person/name :person/id :best-friend]}])) + "join + prop + join ref lookup") + (t/is (= #:people{:all [{:person/name "Alice" + :person/id 0 + :best-friend #:person{:name "Bob"}} + #:person{:name "Bob", :id 1}]} + (p/pull db [#:people{:all [:person/name + :person/id + {:best-friend [:person/name]}]}])) + "join + prop, ref as prop resolver") + (t/is (= {[:person/id 1] #:person{:id 1, :name "Bob", :age 23}} + (p/pull db [[:person/id 1]])) + "ident acts as ref lookup") + (t/is (= {[:person/id 0] {:person/id 0 + :person/name "Alice" + :person/age 25 + :best-friend {:person/id 1} + :person/favorites #:favorite{:ice-cream "vanilla"}}} + (p/pull db [[:person/id 0]])) + "ident does not resolve nested refs") + (t/is (= {[:person/id 0] #:person{:id 0 + :name "Alice" + :favorites #:favorite{:ice-cream "vanilla"}}} + (p/pull db [{[:person/id 0] [:person/id + :person/name + :person/favorites]}])) + "join on ident") + (t/is (= {:people/all [{:person/name "Alice" + :person/id 0 + :best-friend #:person{:name "Bob", :id 1 :age 23}} + #:person{:name "Bob", :id 1}] + [:person/id 1] #:person{:age 23}} + (p/pull db [{:people/all [:person/name :person/id :best-friend]} + {[:person/id 1] [:person/age]}])) + "multiple joins") + + (t/testing "includes params" + (t/is (= #:people{:all [#:person{:name "Bob", :id 1}]} + (p/pull (-> db + (p/add {'(:people/all {:with "params"}) [[:person/id 1]]})) + '[{(:people/all {:with "params"}) + [:person/name :person/id]}]))) + (t/is (= '{:person/foo {:person/id 1 + :person/name "Bob"}} + (p/pull (-> db + (p/add {'(:person/foo {:person/id 2}) + {:person/id 1}})) + '[{(:person/foo {:person/id 2}) + [:person/name :person/id]}])) + "params that include an entity-looking thing should not be normalized") + (t/is (= {} + (p/pull db '[([:person/id 1] {:with "params"})]))) + (t/is (= {} + (p/pull db '[{(:people/all {:with "params"}) + [:person/name :person/id]}])))) + + (t/testing "union" + (let [data {:chat/entries + [{:message/id 0 + :message/text "foo" + :chat.entry/timestamp "1234"} + {:message/id 1 + :message/text "bar" + :chat.entry/timestamp "1235"} + {:audio/id 0 + :audio/url "audio://asdf.jkl" + :audio/duration 1234 + :chat.entry/timestamp "4567"} + {:photo/id 0 + :photo/url "photo://asdf_10x10.jkl" + :photo/height 10 + :photo/width 10 + :chat.entry/timestamp "7890"}]} + db1 (p/db [data]) + query [{:chat/entries + {:message/id + [:message/id :message/text :chat.entry/timestamp] + + :audio/id + [:audio/id :audio/url :audio/duration :chat.entry/timestamp] + + :photo/id + [:photo/id :photo/url :photo/width :photo/height :chat.entry/timestamp] + + :asdf/jkl [:asdf/jkl]}}]] + (t/is (= #:chat{:entries [{:message/id 0 + :message/text "foo" + :chat.entry/timestamp "1234"} + {:message/id 1 + :message/text "bar" + :chat.entry/timestamp "1235"} + {:audio/id 0 + :audio/url "audio://asdf.jkl" + :audio/duration 1234 + :chat.entry/timestamp "4567"} + {:photo/id 0 + :photo/url "photo://asdf_10x10.jkl" + :photo/width 10 + :photo/height 10 + :chat.entry/timestamp "7890"}]} + (p/pull db1 query))))) + + (t/testing "not found" + (t/is (= {} (p/pull {} [:foo]))) + (t/is (= {} (p/pull {} [:foo :bar :baz]))) + (t/is (= {} (p/pull {} [:foo {:bar [:asdf]} :baz]))) + + (t/is (= {:foo "bar"} + (p/pull {:foo "bar"} [:foo {:bar [:asdf]} :baz]))) + (t/is (= {:bar {:asdf 123}} + (p/pull + {:bar {:asdf 123}} + [:foo {:bar [:asdf :jkl]} :baz]))) + (t/is (= {:bar {}} + (p/pull + (p/db [{:bar {:bar/id 0}} + {:bar/id 0 + :qwerty 1234}]) + [:foo {:bar [:asdf :jkl]} :baz]))) + (t/is (= {:bar {:asdf "jkl"}} + (p/pull + (p/db [{:bar {:bar/id 0}} + {:bar/id 0 + :asdf "jkl"}]) + [:foo {:bar [:asdf :jkl]} :baz]))) + (t/is (= {:bar {}} + (p/pull + (p/db [{:bar {:bar/id 0}} + {:bar/id 1 + :asdf "jkl"}]) + [:foo {:bar [:asdf :jkl]} :baz]))) + + (t/is (= {:foo [{:bar/id 1 + :bar/name "asdf"} + {:baz/id 1 + :baz/name "jkl"}]} + (p/pull + (p/db [{:foo [{:bar/id 1 + :bar/name "asdf"} + {:baz/id 1 + :baz/name "jkl"}]}]) + [{:foo {:bar/id [:bar/id :bar/name] + :baz/id [:baz/id :baz/name]}}]))) + + (t/is (= {:foo [{:bar/id 1 + :bar/name "asdf"} + {:bar/id 2} + {:baz/id 1 + :baz/name "jkl"}]} + (p/pull + (p/db [{:foo [{:bar/id 1 + :bar/name "asdf"} + {:bar/id 2} + {:baz/id 1 + :baz/name "jkl"}]}]) + [{:foo {:bar/id [:bar/id :bar/name] + :baz/id [:baz/id :baz/name]}}])))) + + (t/testing "bounded recursion" + (let [data {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders + [{:entry/id "asdf" + :entry/folders + [{:entry/id "qwerty"}]} + {:entry/id "jkl" + :entry/folders + [{:entry/id "uiop"}]}]}]}} + db (p/db [data])] + (t/is (= {:entries + {:entry/id "foo" + :entry/folders + []}} + (p/pull db '[{:entries [:entry/id + {:entry/folders 0}]}]))) + (t/is (= {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders []}]}} + (p/pull db '[{:entries [:entry/id + {:entry/folders 1}]}]))) + (t/is (= {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders + [{:entry/id "asdf" + :entry/folders []} + {:entry/id "jkl" + :entry/folders []}]}]}} + (p/pull db '[{:entries [:entry/id + {:entry/folders 2}]}]))) + (t/is (= {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders + [{:entry/id "asdf" + :entry/folders + [{:entry/id "qwerty"}]} + {:entry/id "jkl" + :entry/folders + [{:entry/id "uiop"}]}]}]}} + (p/pull db '[{:entries [:entry/id + {:entry/folders 3}]}]))) + (t/is (= {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders + [{:entry/id "asdf" + :entry/folders + [{:entry/id "qwerty"}]} + {:entry/id "jkl" + :entry/folders + [{:entry/id "uiop"}]}]}]}} + (p/pull db '[{:entries [:entry/id + {:entry/folders 10}]}]))))) + + (t/testing "infinite recursion" + (let [data {:entries + {:entry/id "foo" + :entry/folders + [{:entry/id "bar"} + {:entry/id "baz" + :entry/folders + [{:entry/id "asdf" + :entry/folders + [{:entry/id "qwerty"}]} + {:entry/id "jkl" + :entry/folders + [{:entry/id "uiop"}]}]}]}} + db (p/db [data])] + (t/is (= data + (p/pull db '[{:entries [:entry/id + {:entry/folders ...}]}]))))) + + (t/testing "query metadata" + (t/is (-> db + (p/pull ^:foo []) + (meta) + (:foo)) + "root") + (t/is (-> db + (p/pull [^:foo {[:person/id 0] [:person/name]}]) + (get [:person/id 0]) + (meta) + (:foo)) + "join") + (let [data {:chat/entries + [{:message/id 0 + :message/text "foo" + :chat.entry/timestamp "1234"} + {:message/id 1 + :message/text "bar" + :chat.entry/timestamp "1235"} + {:audio/id 0 + :audio/url "audio://asdf.jkl" + :audio/duration 1234 + :chat.entry/timestamp "4567"} + {:photo/id 0 + :photo/url "photo://asdf_10x10.jkl" + :photo/height 10 + :photo/width 10 + :chat.entry/timestamp "7890"}]} + db1 (p/db [data]) + query ^:foo [^:bar + {:chat/entries + {:message/id + [:message/id :message/text :chat.entry/timestamp] + + :audio/id + [:audio/id :audio/url :audio/duration :chat.entry/timestamp] + + :photo/id + [:photo/id :photo/url :photo/width :photo/height :chat.entry/timestamp] + + :asdf/jkl [:asdf/jkl]}}] + result (p/pull db1 query)] + (t/is (= #:chat{:entries [{:message/id 0 + :message/text "foo" + :chat.entry/timestamp "1234"} + {:message/id 1 + :message/text "bar" + :chat.entry/timestamp "1235"} + {:audio/id 0 + :audio/url "audio://asdf.jkl" + :audio/duration 1234 + :chat.entry/timestamp "4567"} + {:photo/id 0 + :photo/url "photo://asdf_10x10.jkl" + :photo/width 10 + :photo/height 10 + :chat.entry/timestamp "7890"}]} + result)) + (t/is (-> result meta :foo)) + (t/is (every? #(:bar (meta %)) (get result :chat/entries))))) + (t/testing "dangling entities" + (t/is (= {[:id 0] {:friends [{:id 1} {:id 2}]}} + (p/pull + {:id {0 {:id 0 :name "asdf" :friends [[:id 1] [:id 2]]} + 1 {:id 1 :name "jkl"}}} + [{[:id 0] [:friends]}])) + "dangling entity shows up in queries that do not select any props") + ;; BB-TEST-PATCH: NullPointerException: Cannot invoke "clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)" + #_(t/is (= {[:id 0] {:friends [{:id 1, :name "jkl"} {:id 2}]}} + (p/pull + {:id {0 {:id 0 :name "asdf" :friends [[:id 1] [:id 2]]} + 1 {:id 1 :name "jkl"}}} + [{[:id 0] [{:friends [:id :name]}]}])) + "dangling entity shows up in queries that include ID") + ;; BB-TEST-PATCH: NullPointerException: Cannot invoke "clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)" + #_(t/is (= {[:id 0] {:friends [{:name "jkl"}]}} + (p/pull + {:id {0 {:id 0 :name "asdf" :friends [[:id 1] [:id 2]]} + 1 {:id 1 :name "jkl"}}} + [{[:id 0] [{:friends [:name]}]}])) + "dangling entity does not show up in queries that do not include ID"))) + + +(t/deftest pull-report + (t/is (= {:data {:people/all [{:person/name "Alice"} + {:person/name "Bob"}]} + :entities #{[:person/id 0] [:person/id 1]}} + (p/pull-report db [{:people/all [:person/name]}])) + "basic join + prop") + (t/is (= {:data #:people{:all [{:person/name "Alice" + :best-friend #:person{:name "Bob", :id 1 :age 23}} + #:person{:name "Bob"}]} + :entities #{[:person/id 0] [:person/id 1]}} + (p/pull-report db [#:people{:all [:person/name :best-friend]}])) + "join + prop + join ref lookup") + (t/is (= {:data {[:person/id 1] #:person{:id 1, :name "Bob", :age 23}} + :entities #{[:person/id 1]}} + (p/pull-report db [[:person/id 1]])) + "ident acts as ref lookup") + (t/is (= {:data {[:person/id 0] {:person/id 0 + :person/name "Alice" + :person/age 25 + :best-friend {:person/id 1} + :person/favorites #:favorite{:ice-cream "vanilla"}}} + :entities #{[:person/id 0]}} + (p/pull-report db [[:person/id 0]])) + "ident does not resolve nested refs")) + + +(t/deftest delete + (t/is (= {:people/all [[:person/id 0]] + :person/id {0 {:person/id 0 + :person/name "Alice" + :person/age 25 + :person/favorites #:favorite{:ice-cream "vanilla"}}}} + (p/delete db [:person/id 1])))) + + +(t/deftest data->query + (t/is (= [:a] + (p/data->query {:a 42}))) + (t/is (= [{:a [:b]}] + (p/data->query {:a {:b 42}}))) + (t/is (= [{:a [:b :c]}] + (p/data->query {:a [{:b 42} {:c :d}]}))) + (t/is (= [{[:a 42] [:b]}] + (p/data->query {[:a 42] {:b 33}})))) + +(comment + (t/run-tests)) diff --git a/test-resources/lib_tests/pyramid/pull_test.cljc b/test-resources/lib_tests/pyramid/pull_test.cljc new file mode 100644 index 00000000..138647b6 --- /dev/null +++ b/test-resources/lib_tests/pyramid/pull_test.cljc @@ -0,0 +1,23 @@ +(ns pyramid.pull-test + (:require + [clojure.test :as t] + [pyramid.pull :as p])) + + +(def entities + (for [i (range 1000)] + [:id i])) + + +(t/deftest many-entities + (t/is (= (set entities) + (:entities + (p/pull-report + {:id (into + {} + (map #(vector + (second %) + (hash-map (first %) (second %)))) + entities) + :all (vec entities)} + [{:all [:id]}]))))) diff --git a/test-resources/lib_tests/pyramid/query_test.cljc b/test-resources/lib_tests/pyramid/query_test.cljc new file mode 100644 index 00000000..7f1e7e81 --- /dev/null +++ b/test-resources/lib_tests/pyramid/query_test.cljc @@ -0,0 +1,112 @@ +(ns pyramid.query-test + (:require + [pyramid.query :as p.q :refer [q]] + [clojure.test :as t])) + + +(def db {:person/id {"123" {:person/id "123" + :person/name "foo" + :person/best-friend [:person/id "789"] + :person/friends [[:person/id "456"] + [:person/id "789"]]} + "456" {:person/id "456" + :person/name "bar" + :person/friends [[:person/id "123"] + [:person/id "789"]]} + "789" {:person/id "789" + :person/name "baz" + :person/friends [[:person/id "123"] + [:person/id "456"]]} + "1011" {:person/id "1011"}} + :person {:bar "baz"} + :asdf "jkl"}) + + +(t/deftest joins + (t/is (= '(["123"] ["456"] ["789"] ["1011"]) + (q '[:find ?id + :where + [?e :person/id ?id]] + db))) + + (t/is (= '(["123" "foo"] ["456" "bar"] ["789" "baz"]) + (q '[:find ?id ?name + :where + [?e :person/id ?id] + [?e :person/name ?name]] + db))) + + (t/is (= '(["123" "foo" [:person/id "789"]]) + (q '[:find ?id ?name ?friend + :where + [?e :person/id ?id] + [?e :person/name ?name] + [?e :person/best-friend ?friend]] + db))) + + (t/is (= '(["foo" "baz"]) + (q '[:find ?name ?friend-name + :where + [?e :person/name ?name] + [?e :person/best-friend ?friend] + [?friend :person/name ?friend-name]] + db))) + + (t/is (= '() + (q '[:find ?id + :where + [?e :person/name "asdf"] + [?e :person/id ?id]] + db)) + "not found") + + (t/is (= '(["123" "foo"]) + (q '[:find ?id ?name + :in $ ?name + :where + [?e :person/name ?name] + [?e :person/id ?id]] + db + "foo")) + "join on :in") + + (t/is (= '(["foo" "bar"] + ["foo" "baz"] + ["bar" "foo"] + ["bar" "baz"] + ["baz" "foo"] + ["baz" "bar"]) + (q '[:find ?name ?friend-name + :where + [?e :person/name ?name] + [?e :person/friends ^:many ?friend] + [?friend :person/name ?friend-name]] + db)) + "multiple cardinality value") + + (t/is (= '(["123" "foo"] + ["456" "bar"]) + (q '[:find ?id ?name + :in $ ^:many ?name + :where + [?e :person/name ?name] + [?e :person/id ?id]] + db + ["foo" "bar"])) + "multi cardinality join on :in") + + (t/is (= '(["foo" "foo"] + ["foo" "bar"] + ["foo" "baz"] + ["bar" "foo"] + ["bar" "bar"] + ["bar" "baz"] + ["baz" "foo"] + ["baz" "bar"] + ["baz" "baz"]) + (q '[:find ?name1 ?name2 + :where + [?e1 :person/name ?name1] + [?e2 :person/name ?name2]] + db)) + "cross product")) diff --git a/test-resources/lib_tests/swirrl/dogstatsd_test.clj b/test-resources/lib_tests/swirrl/dogstatsd_test.clj new file mode 100644 index 00000000..6ee88cdf --- /dev/null +++ b/test-resources/lib_tests/swirrl/dogstatsd_test.clj @@ -0,0 +1,22 @@ +(ns swirrl.dogstatsd-test + (:require [swirrl.dogstatsd :as sut] + [swirrl.dogstatsd.specs] + [clojure.test :refer [deftest is testing]] + [clojure.spec.test.alpha :as st])) + +(st/instrument) + +(deftest basic-invocation-tests + (testing "Basic metric procedure calls run without error" + (let [client (sut/configure {:endpoint "localhost:8111"})] + + (sut/increment! client ::increment) + (sut/increment! client ::increment 10) + (sut/decrement! client ::decrement) + + (sut/histogram! client ::histogram 10) + (sut/distribution! client ::distribution 10) + (sut/set! client ::set "a-value") + (sut/event! client "event title" "some text here" {}) + + (is true))))