From 7b9ab00118a12261def517ed9a67bd9e9e2a3fbd Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Tue, 11 Feb 2020 15:29:52 +0000 Subject: [PATCH 1/8] Add functions to generate the most commonly used commands --- src/jackdaw/test/commands.clj | 22 +++++++++++++++++++++- src/jackdaw/test/commands/base.clj | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index d493c461..ea5b6709 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -3,7 +3,8 @@ (:require [jackdaw.test.commands.base :as base] [jackdaw.test.commands.write :as write] - [jackdaw.test.commands.watch :as watch])) + [jackdaw.test.commands.watch :as watch]) + (:refer-clojure :exclude [do])) (def base-commands base/command-map) (def write-command write/command-map) @@ -37,3 +38,22 @@ [machine handler] (assoc machine :command-handler handler)) + +(defn do [do-fn] + `[:do ~do-fn]) + +(defn do! [do-fn] + `[:do! ~do-fn]) + +(defn write! + ([topic-id message] + `[:write! ~topic-id ~message]) + + ([topic-id message options] + `[:write! ~topic-id ~message ~options])) + +(defn watch + ([watch-query] + `[:watch ~watch-query]) + ([watch-query opts] + `[:watch ~watch-query ~opts])) diff --git a/src/jackdaw/test/commands/base.clj b/src/jackdaw/test/commands/base.clj index 1fbe22d7..18443b7a 100644 --- a/src/jackdaw/test/commands/base.clj +++ b/src/jackdaw/test/commands/base.clj @@ -1,6 +1,6 @@ (ns jackdaw.test.commands.base (:require - [clojure.pprint :as pprint])) + [clojure.pprint :as pprint])) (def command-map {:stop (constantly true) From 22802424195016bc3a1dea966a42999b6bb6b0b9 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 13:59:37 +0000 Subject: [PATCH 2/8] Update the test-machine integration tests to use the command api --- test/jackdaw/test_test.clj | 73 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/test/jackdaw/test_test.clj b/test/jackdaw/test_test.clj index 123ee9ff..6bae0c7d 100644 --- a/test/jackdaw/test_test.clj +++ b/test/jackdaw/test_test.clj @@ -3,6 +3,7 @@ [clojure.test :refer :all] [jackdaw.streams :as k] [jackdaw.test :as jd.test] + [jackdaw.test.commands :as cmd] [jackdaw.test.fixtures :as fix] [jackdaw.test.serde :as serde] [jackdaw.test.transports :as trns] @@ -115,10 +116,9 @@ (testing "write then watch" (fix/with-fixtures [(fix/topic-fixture kafka-config {"foo" foo-topic})] (with-open [t (jd.test/test-machine (kafka-transport))] - (let [write [:write! "foo" {:id "msg1" :payload "yolo"}] - watch [:watch (by-id "foo" "msg1") - {:info "failed to find foo with id=msg1"}] - + (let [write (cmd/write! "foo" {:id "msg1" :payload "yolo"}) + watch (cmd/watch (by-id "foo" "msg1") + {:info "failed to find foo with id=msg1"}) {:keys [results journal]} (jd.test/run-test t [write watch]) [write-result watch-result] results] @@ -139,13 +139,13 @@ (deftest test-reuse-machine (fix/with-fixtures [(fix/topic-fixture kafka-config {"foo" foo-topic})] (with-open [t (jd.test/test-machine (kafka-transport))] - (let [prog1 [[:write! "foo" {:id "msg2" :payload "yolo"}] - [:watch (by-id "foo" "msg2") - {:info "failed to find foo with id=msg2"}]] + (let [prog1 [(cmd/write! "foo" {:id "msg2" :payload "yolo"}) + (cmd/watch (by-id "foo" "msg2") + {:info "failed to find foo with id=msg2"})] - prog2 [[:write! "foo" {:id "msg3" :payload "you only live twice"}] - [:watch (by-id "foo" "msg3") - {:info "failed to find foo with id=msg3"}]]] + prog2 [(cmd/write! "foo" {:id "msg3" :payload "you only live twice"}) + (cmd/watch (by-id "foo" "msg3") + {:info "failed to find foo with id=msg3"})]] (testing "run test sequence and inspect results" (let [{:keys [results journal]} (jd.test/run-test t prog1)] @@ -207,14 +207,15 @@ :out test-out}}) (fn [machine] (jd.test/run-test machine - [[:write! :in {:id "1" :payload "foo"} {:key-fn :id}] - [:write! :in {:id "2" :payload "bar"} {:key-fn :id}] - [:watch (fn [journal] - (when (->> (get-in journal [:topics :out]) - (filter (fn [r] - (= (get-in r [:value :id]) "2"))) - (not-empty)) - true)) {:timeout 2000}]]))) + [(cmd/write! :in {:id "1" :payload "foo"} {:key-fn :id}) + (cmd/write! :in {:id "2" :payload "bar"} {:key-fn :id}) + (cmd/watch (fn [journal] + (when (->> (get-in journal [:topics :out]) + (filter (fn [r] + (= (get-in r [:value :id]) "2"))) + (not-empty)) + true)) + {:timeout 2000})]))) (catch Exception e (reset! error-raised e))) (is (not @error-raised)))) @@ -230,13 +231,13 @@ :out test-out}}) (fn [machine] (jd.test/run-test machine - [[:write! :in {:id "1" :payload "foo"} {:key-fn :id}] - [:write! :in {:id "2" :payload "bar"} {:key-fn :id}] - [:watch (fn [journal] - (->> (get-in journal [:topics :out]) - (filter (fn [r] - (= (:id r) "2"))) - (not-empty)))]]))) + [(cmd/write! :in {:id "1" :payload "foo"} {:key-fn :id}) + (cmd/write! :in {:id "2" :payload "bar"} {:key-fn :id}) + (cmd/watch (fn [journal] + (->> (get-in journal [:topics :out]) + (filter (fn [r] + (= (:id r) "2"))) + (not-empty))))]))) (catch Exception e (reset! error-raised e))) (is @error-raised))) @@ -252,13 +253,13 @@ :out test-out}}) (fn [machine] (jd.test/run-test machine - [[:write! :in {:id "1" :payload "foo"} {:key-fn bad-key-fn}] - [:write! :in {:id "2" :payload "bar"} {:key-fn :id}] - [:watch (fn [journal] - (->> (get-in journal [:topics :out]) - (filter (fn [r] - (= (:id r) "2"))) - (not-empty)))]]))) + [(cmd/write! :in {:id "1" :payload "foo"} {:key-fn bad-key-fn}) + (cmd/write! :in {:id "2" :payload "bar"} {:key-fn :id}) + (cmd/watch (fn [journal] + (->> (get-in journal [:topics :out]) + (filter (fn [r] + (= (:id r) "2"))) + (not-empty))))]))) (catch Exception e (reset! error-raised e))) (is @error-raised))) @@ -274,10 +275,10 @@ :out test-out}}) (fn [machine] (jd.test/run-test machine - [[:write! :in {:id "1" :payload "foo"} {:key-fn :id}] - [:write! :in {:id "2" :payload "bar"} {:key-fn :id}] - [:watch (fn [journal] - (bad-watch-fn journal))]]))) + [(cmd/write! :in {:id "1" :payload "foo"} {:key-fn :id}) + (cmd/write! :in {:id "2" :payload "bar"} {:key-fn :id}) + (cmd/watch (fn [journal] + (bad-watch-fn journal)))]))) (catch Exception e (reset! error-raised e))) (is @error-raised))) From 45de7a0cefa3af27ea0bdac45403a1f0ff1976d2 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 14:31:51 +0000 Subject: [PATCH 3/8] Add basic fspecs for the test commands --- src/jackdaw/test/commands.clj | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index ea5b6709..79b50f9c 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -1,6 +1,7 @@ (ns jackdaw.test.commands "" (:require + [clojure.spec.alpha :as s] [jackdaw.test.commands.base :as base] [jackdaw.test.commands.write :as write] [jackdaw.test.commands.watch :as watch]) @@ -39,12 +40,27 @@ (assoc machine :command-handler handler)) +;; Test Command API + +(s/def ::topic-id keyword?) +(s/def ::test-msg any?) +(s/def ::write-options map?) +(s/def ::watch-options map?) + (defn do [do-fn] `[:do ~do-fn]) +(s/fdef do + :args ifn? + :ret vector?) + (defn do! [do-fn] `[:do! ~do-fn]) +(s/fdef do! + :args ifn? + :ret vector?) + (defn write! ([topic-id message] `[:write! ~topic-id ~message]) @@ -52,8 +68,17 @@ ([topic-id message options] `[:write! ~topic-id ~message ~options])) +(s/fdef write! + :args (s/cat ::topic-id ::write-options) + :ret vector?) + (defn watch ([watch-query] `[:watch ~watch-query]) ([watch-query opts] `[:watch ~watch-query ~opts])) + +(s/fdef watch + :args (s/cat ::watch-query ifn? + ::opts ::watch-options) + :ret vector?) From 1414d15405a8a514e387dc88d1c92a1ab9ffab6c Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 14:38:15 +0000 Subject: [PATCH 4/8] The topic-id in write commands can also be a string --- src/jackdaw/test/commands.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index 79b50f9c..2e5a2f33 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -42,7 +42,8 @@ ;; Test Command API -(s/def ::topic-id keyword?) +(s/def ::topic-id (s/or ::keyword keyword? + ::string string?)) (s/def ::test-msg any?) (s/def ::write-options map?) (s/def ::watch-options map?) @@ -69,7 +70,9 @@ `[:write! ~topic-id ~message ~options])) (s/fdef write! - :args (s/cat ::topic-id ::write-options) + :args (s/cat :topic-id ::topic-id + :msg ::test-msg + :opts (s/? ::write-options)) :ret vector?) (defn watch @@ -79,6 +82,6 @@ `[:watch ~watch-query ~opts])) (s/fdef watch - :args (s/cat ::watch-query ifn? - ::opts ::watch-options) + :args (s/cat :watch ifn? + :opts (s/? ::watch-options)) :ret vector?) From cc5c3fef1dc9a05a9482fbdc5abe9f43c8b43dc1 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 14:54:40 +0000 Subject: [PATCH 5/8] Use full names in specs for better docs --- src/jackdaw/test/commands.clj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index 2e5a2f33..f43e7a5f 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -44,7 +44,7 @@ (s/def ::topic-id (s/or ::keyword keyword? ::string string?)) -(s/def ::test-msg any?) +(s/def ::test-message any?) (s/def ::write-options map?) (s/def ::watch-options map?) @@ -71,17 +71,17 @@ (s/fdef write! :args (s/cat :topic-id ::topic-id - :msg ::test-msg - :opts (s/? ::write-options)) + :message ::test-msg + :options (s/? ::write-options)) :ret vector?) (defn watch ([watch-query] `[:watch ~watch-query]) - ([watch-query opts] - `[:watch ~watch-query ~opts])) + ([watch-query options] + `[:watch ~watch-query ~options])) (s/fdef watch - :args (s/cat :watch ifn? - :opts (s/? ::watch-options)) + :args (s/cat :watch-query ifn? + :options (s/? ::watch-options)) :ret vector?) From a25fee06369f523db43b05c60c111bc295392be3 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 15:04:09 +0000 Subject: [PATCH 6/8] Update CHANGELOG --- CHANGELOG.md | 10 +++++++++- src/jackdaw/test/commands.clj | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b357dcd..8a793a6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog -## Fixed +## Unreleased + +### Added + +* Start formalizing test-machine commands with fspec'd functions + +## [0.7.2] - [2020-02-07] + +### Fixed * Fixed bug in Avro deserialisation, when handling a union of enum types diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index f43e7a5f..4ede842c 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -71,7 +71,7 @@ (s/fdef write! :args (s/cat :topic-id ::topic-id - :message ::test-msg + :message ::test-message :options (s/? ::write-options)) :ret vector?) From 31d360cb9cf46dd63d14169d3691c0776b4a4c44 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 15:11:46 +0000 Subject: [PATCH 7/8] Ignore test-results/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 187fd397..acafb3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /jackdaw-[a-z]*/checkouts /jackdaw-[a-z]*/target /target +/test-results /logs pom.xml pom.xml.asc From 8cbc24d89fef2219a9fc0d7dc94a04fceac4cbf0 Mon Sep 17 00:00:00 2001 From: Andy Chambers <andy.chambers@fundingcircle.com> Date: Wed, 12 Feb 2020 16:59:37 +0000 Subject: [PATCH 8/8] Add better docstrings for the command functions --- src/jackdaw/test/commands.clj | 49 ++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/jackdaw/test/commands.clj b/src/jackdaw/test/commands.clj index 4ede842c..8dfbf379 100644 --- a/src/jackdaw/test/commands.clj +++ b/src/jackdaw/test/commands.clj @@ -42,20 +42,30 @@ ;; Test Command API -(s/def ::topic-id (s/or ::keyword keyword? - ::string string?)) +(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?) -(defn do [do-fn] +(defn do + "Invoke the provided function, passing a snapshot of the test journal + + Use this to perform side-effects without representing their result in the journal" + [do-fn] `[:do ~do-fn]) (s/fdef do :args ifn? :ret vector?) -(defn do! [do-fn] +(defn do! + "Invoke the provided function, passing the journal `ref` + + Use this to perform side-effects when you want to represent the result in the journal + (e.g. insert test-data into an external database AND into the journal with the expectation + that it will eventually appear in kafka via some external system like kafka-connect)" + [do-fn] `[:do! ~do-fn]) (s/fdef do! @@ -63,6 +73,17 @@ :ret vector?) (defn write! + "Write a message to the topic identified in the topic-metadata by `topic-id` + + `:message` is typically a map to be serialized by the Serde configured in the topic-metadata + but it can be whatever the configued Serde is capable of serializing + `:options` is an optional map containing additional information describing how the test-message + should be created. The following properties are supported. + + `:key` An explicit key to associate with the test message + `:key-fn` A function to derive the key from the test message + `:partition` The partition to which the test message should be written + `:partition-fn` A function to derive the partition to which the test message should be written" ([topic-id message] `[:write! ~topic-id ~message]) @@ -76,12 +97,22 @@ :ret vector?) (defn watch - ([watch-query] - `[:watch ~watch-query]) - ([watch-query options] - `[:watch ~watch-query ~options])) + "Watch the test-journal until the `watch-fn` predicate returns true + + `:watch-fn` is a function that takes the journal and returns true once the journal + contains evidence of the test being complete + `:options` is an optional map containing additional information describing how the watch + function will be run. The following properties are supported. + + `:info` Diagnostic information to be included in the response when a watch fails + to observe the expected data in the journal + `:timeout` The number of milliseconds to wait before giving up" + ([watch-fn] + `[:watch ~watch-fn]) + ([watch-fn options] + `[:watch ~watch-fn ~options])) (s/fdef watch - :args (s/cat :watch-query ifn? + :args (s/cat :watch-fn ifn? :options (s/? ::watch-options)) :ret vector?)