From 657fd9f854536b987accf8c689fc6671501bca89 Mon Sep 17 00:00:00 2001 From: Andy Chambers Date: Thu, 13 Feb 2020 14:41:39 +0000 Subject: [PATCH 1/4] Add a multi-spec for 'test-events' --- src/jackdaw/test.clj | 3 ++ src/jackdaw/test/commands.clj | 60 ++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/jackdaw/test.clj b/src/jackdaw/test.clj index 03121885..23835854 100644 --- a/src/jackdaw/test.clj +++ b/src/jackdaw/test.clj @@ -20,6 +20,7 @@ kafka cluster shared with other users." (:require [clojure.tools.logging :as log] + [clojure.spec.alpha :as s] [jackdaw.streams :as k] [jackdaw.test.commands :refer [with-handler command-handler]] [jackdaw.test.commands.base] @@ -143,6 +144,8 @@ commands to execute. Remember to use `with-open` on the test-machine to ensure that all resources are correcly torn down." [machine commands] + (s/assert (s/coll-of :jackdaw.test.commands/test-event) + commands) {:results (run-commands machine commands) :journal @(:journal machine)}) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index 8dfbf379..c2f03ca0 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -5,7 +5,7 @@ [jackdaw.test.commands.base :as base] [jackdaw.test.commands.write :as write] [jackdaw.test.commands.watch :as watch]) - (:refer-clojure :exclude [do])) + (:refer-clojure :exclude [do println pprint inspect])) (def base-commands base/command-map) (def write-command write/command-map) @@ -42,11 +42,14 @@ ;; Test Command API +(defmulti test-event first) + (s/def ::topic-id (s/or :keyword keyword? :string string?)) (s/def ::test-message any?) (s/def ::write-options map?) (s/def ::watch-options map?) +(s/def ::test-event (s/multi-spec test-event first)) (defn do "Invoke the provided function, passing a snapshot of the test journal @@ -57,7 +60,10 @@ (s/fdef do :args ifn? - :ret vector?) + :ret ::test-event) + +(defmethod test-event :do [_] (s/cat :op #{:do} + :do-fn ifn?)) (defn do! "Invoke the provided function, passing the journal `ref` @@ -68,9 +74,27 @@ [do-fn] `[:do! ~do-fn]) +(defmethod test-event :do! [_] (s/cat :op #{:do!} + :do-fn ifn?)) + +(s/fdef inspect + :args ifn? + :ret ::test-event) + +(defn inspect + "Invoke the provided function, passing the entire test-machine + + Can be useful while learning about how the test-machine works to inspect the state + of the test-machine." + [inspect-fn] + `[:do! ~inspect-fn]) + +(defmethod test-event :inspect [_] (s/cat :op #{:do!} + :inspect-fn ifn?)) + (s/fdef do! :args ifn? - :ret vector?) + :ret ::test-event) (defn write! "Write a message to the topic identified in the topic-metadata by `topic-id` @@ -94,7 +118,12 @@ :args (s/cat :topic-id ::topic-id :message ::test-message :options (s/? ::write-options)) - :ret vector?) + :ret ::test-event) + +(defmethod test-event :write! [_] (s/cat :op #{:write!} + :topic-id ::topic-id + :message ::test-message + :options (s/? ::write-options))) (defn watch "Watch the test-journal until the `watch-fn` predicate returns true @@ -115,4 +144,25 @@ (s/fdef watch :args (s/cat :watch-fn ifn? :options (s/? ::watch-options)) - :ret vector?) + :ret ::test-event) + +(defmethod test-event :watch [_] (s/cat :op #{:watch} + :watch-fn ifn? + :option (s/? ::watch-options))) + +;; Deprecated test events +;; +;; Keeping these around to ensure existing test-sequences continue to be valid +;; but `:stop` is a relic of when the implementation required an explicit stop +;; command and the others are all expressible as a simple `:do`. + +(defmethod test-event :stop [_] (s/cat :op #{:println})) + +(defmethod test-event :println [_] (s/cat :op #{:println} + :print-args (s/? any?))) + +(defmethod test-event :pprint [_] (s/cat :op #{:println} + :print-args (s/? any?))) + +(defmethod test-event :sleep [_] (s/cat :op #{:println} + :sleep-args int?)) From edc406ce4e54e65f8d949aa47053c776a6a4e39c Mon Sep 17 00:00:00 2001 From: Andy Chambers Date: Thu, 13 Feb 2020 14:48:17 +0000 Subject: [PATCH 2/4] Tighten option specs on the write/watch commands --- src/jackdaw/test/commands.clj | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index c2f03ca0..3811a95c 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -46,9 +46,15 @@ (s/def ::topic-id (s/or :keyword keyword? :string string?)) +(s/def ::timeout int?) +(s/def ::key any?) +(s/def ::key-fn ifn?) +(s/def ::partition int?) +(s/def ::partition-fn ifn?) +(s/def ::info string?) (s/def ::test-message any?) -(s/def ::write-options map?) -(s/def ::watch-options map?) +(s/def ::write-options (s/keys :req-un [::key ::key-fn ::partition ::partition-fn])) +(s/def ::watch-options (s/keys :req-un [::timeout ::info])) (s/def ::test-event (s/multi-spec test-event first)) (defn do From 448e1f601e61cd026d6ea12baa1ffab0bf18a0f2 Mon Sep 17 00:00:00 2001 From: Andy Chambers Date: Thu, 13 Feb 2020 15:19:37 +0000 Subject: [PATCH 3/4] Add tests to ensure test-command specs work correctly --- src/jackdaw/test.clj | 8 ++- src/jackdaw/test/commands.clj | 83 +++++++++++++++-------------- test/jackdaw/test/commands_test.clj | 42 +++++++++++++++ 3 files changed, 90 insertions(+), 43 deletions(-) diff --git a/src/jackdaw/test.clj b/src/jackdaw/test.clj index 23835854..329d1a6e 100644 --- a/src/jackdaw/test.clj +++ b/src/jackdaw/test.clj @@ -129,6 +129,8 @@ (recur results' rest-cmds)))))) +(s/def ::test-commands (s/coll-of :jackdaw.test.commands/test-event)) + (defn run-test "Runs a sequence of test commands against a test-machine and returns a response map. @@ -144,11 +146,13 @@ commands to execute. Remember to use `with-open` on the test-machine to ensure that all resources are correcly torn down." [machine commands] - (s/assert (s/coll-of :jackdaw.test.commands/test-event) - commands) + (s/assert ::test-commands commands) + {:results (run-commands machine commands) :journal @(:journal machine)}) +(s/check-asserts true) + (defn identity-transport "The identity transport simply injects input events directly into the journal. diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index 3811a95c..2cd4171f 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -53,10 +53,45 @@ (s/def ::partition-fn ifn?) (s/def ::info string?) (s/def ::test-message any?) -(s/def ::write-options (s/keys :req-un [::key ::key-fn ::partition ::partition-fn])) -(s/def ::watch-options (s/keys :req-un [::timeout ::info])) +(s/def ::write-options (s/keys :opt-un [::key ::key-fn ::partition ::partition-fn])) +(s/def ::watch-options (s/keys :opt-un [::timeout ::info])) (s/def ::test-event (s/multi-spec test-event first)) +(defmethod test-event :do [_] (s/cat :op #{:do} + :do-fn ifn?)) + +(defmethod test-event :do! [_] (s/cat :op #{:do!} + :do-fn ifn?)) + +(defmethod test-event :inspect [_] (s/cat :op #{:do!} + :inspect-fn ifn?)) + +(defmethod test-event :write! [_] (s/cat :op #{:write!} + :topic-id ::topic-id + :message ::test-message + :options (s/? ::write-options))) + +(defmethod test-event :watch [_] (s/cat :op #{:watch} + :watch-fn ifn? + :option (s/? ::watch-options))) + +;; Deprecated test events +;; +;; Keeping these around to ensure existing test-sequences continue to be valid +;; but `:stop` is a relic of when the implementation required an explicit stop +;; command and the others are all expressible as a simple `:do`. + +(defmethod test-event :stop [_] (s/cat :op #{:stop})) + +(defmethod test-event :println [_] (s/cat :op #{:println} + :print-args (s/? any?))) + +(defmethod test-event :pprint [_] (s/cat :op #{:pprint} + :print-args (s/? any?))) + +(defmethod test-event :sleep [_] (s/cat :op #{:sleep} + :sleep-args int?)) + (defn do "Invoke the provided function, passing a snapshot of the test journal @@ -65,12 +100,9 @@ `[:do ~do-fn]) (s/fdef do - :args ifn? + :args (s/cat :do-fn ifn?) :ret ::test-event) -(defmethod test-event :do [_] (s/cat :op #{:do} - :do-fn ifn?)) - (defn do! "Invoke the provided function, passing the journal `ref` @@ -80,11 +112,8 @@ [do-fn] `[:do! ~do-fn]) -(defmethod test-event :do! [_] (s/cat :op #{:do!} - :do-fn ifn?)) - -(s/fdef inspect - :args ifn? +(s/fdef do! + :args (s/cat :do-fn ifn?) :ret ::test-event) (defn inspect @@ -95,11 +124,8 @@ [inspect-fn] `[:do! ~inspect-fn]) -(defmethod test-event :inspect [_] (s/cat :op #{:do!} - :inspect-fn ifn?)) - -(s/fdef do! - :args ifn? +(s/fdef inspect + :args (s/cat :inspect-fn ifn?) :ret ::test-event) (defn write! @@ -126,11 +152,6 @@ :options (s/? ::write-options)) :ret ::test-event) -(defmethod test-event :write! [_] (s/cat :op #{:write!} - :topic-id ::topic-id - :message ::test-message - :options (s/? ::write-options))) - (defn watch "Watch the test-journal until the `watch-fn` predicate returns true @@ -152,23 +173,3 @@ :options (s/? ::watch-options)) :ret ::test-event) -(defmethod test-event :watch [_] (s/cat :op #{:watch} - :watch-fn ifn? - :option (s/? ::watch-options))) - -;; Deprecated test events -;; -;; Keeping these around to ensure existing test-sequences continue to be valid -;; but `:stop` is a relic of when the implementation required an explicit stop -;; command and the others are all expressible as a simple `:do`. - -(defmethod test-event :stop [_] (s/cat :op #{:println})) - -(defmethod test-event :println [_] (s/cat :op #{:println} - :print-args (s/? any?))) - -(defmethod test-event :pprint [_] (s/cat :op #{:println} - :print-args (s/? any?))) - -(defmethod test-event :sleep [_] (s/cat :op #{:println} - :sleep-args int?)) diff --git a/test/jackdaw/test/commands_test.clj b/test/jackdaw/test/commands_test.clj index 1ff29ffb..359ca402 100644 --- a/test/jackdaw/test/commands_test.clj +++ b/test/jackdaw/test/commands_test.clj @@ -1,5 +1,6 @@ (ns jackdaw.test.commands-test (:require + [clojure.spec.alpha :as s] [clojure.test :refer :all] [jackdaw.test.commands :as cmd])) @@ -16,3 +17,44 @@ (is (thrown-with-msg? clojure.lang.ExceptionInfo #"Unknown command: :not-found" (cmd/command-handler {} [:not-found]))))) + +(defn valid-command? + [cmd] + (s/valid? ::cmd/test-event cmd)) + +(deftest test-command-specs + (testing "base commands" + (is (not (valid-command? [:yolo]))) + (is (valid-command? (cmd/do #(println "yolo" %)))) + (is (valid-command? (cmd/do! #(println "yolo" %)))) + (is (valid-command? [:stop])) + (is (valid-command? [:println "yolo"])) + (is (valid-command? [:pprint {:foo "yolo"}])) + (is (valid-command? [:sleep 420]))) + + (testing "write commands" + (is (valid-command? (cmd/write! :foo {:id 1 :payload "yolo"}))) + + (is (valid-command? (cmd/write! :foo {:id 1 :payload "yolo"} + {:key 1 + :partition 1}))) + + (is (valid-command? (cmd/write! :foo {:id 1 :payload "yolo"} + {:key-fn :id + :partition-fn (constantly 1)}))) + + (is (not (valid-command? [:write! :foo {:id 1 :payload "yolo"} + {:key-fn "not a fn" + :partition-fn "not a fn"}])))) + + (testing "watch commands" + (is (valid-command? (cmd/watch #(= % :expected)))) + (is (valid-command? (cmd/watch #(= % :expected) + {:info "error hint"}))) + (is (valid-command? (cmd/watch #(= % :expected) + {:timeout 420}))) + + (is (not (valid-command? [:watch #(= % :expected) + {:timeout "not an int"}]))) + (is (not (valid-command? [:watch #(= % :expected) + {:info :not-a-string}]))))) From 5c1cc283402b9584304e494747e09b71c10ae072 Mon Sep 17 00:00:00 2001 From: Andy Chambers Date: Fri, 14 Feb 2020 09:45:16 +0000 Subject: [PATCH 4/4] Perform spec assert in executor --- src/jackdaw/test.clj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/jackdaw/test.clj b/src/jackdaw/test.clj index 329d1a6e..c38bf76c 100644 --- a/src/jackdaw/test.clj +++ b/src/jackdaw/test.clj @@ -65,6 +65,7 @@ ;; resources when the test machine is closed. (def +default-executor+ (-> (fn [machine cmd] + (s/assert :jackdaw.test.commands/test-event cmd) ((:command-handler machine) machine cmd)) with-status with-timing @@ -129,8 +130,6 @@ (recur results' rest-cmds)))))) -(s/def ::test-commands (s/coll-of :jackdaw.test.commands/test-event)) - (defn run-test "Runs a sequence of test commands against a test-machine and returns a response map. @@ -146,13 +145,9 @@ commands to execute. Remember to use `with-open` on the test-machine to ensure that all resources are correcly torn down." [machine commands] - (s/assert ::test-commands commands) - {:results (run-commands machine commands) :journal @(:journal machine)}) -(s/check-asserts true) - (defn identity-transport "The identity transport simply injects input events directly into the journal.