Configure clojure.spec, add a few specs (#23)

Creates specs for init, create, and init-network
This commit is contained in:
Rob Hanlon 2020-08-09 23:15:00 -07:00 committed by GitHub
parent 36f3820f4f
commit c0c08b2e26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 216 additions and 100 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ pom.xml.asc
.hgignore
.hg/
.DS_Store
.lsp

14
dev-src/user.clj Normal file
View file

@ -0,0 +1,14 @@
(ns user
(:require
[clojure.spec.alpha :as s]
[expound.alpha :as ea]
[kaocha.repl]))
(try
;; Attempt to set *explain-out*, assuming that we're inside of
;; a binding context...
(set! s/*explain-out* ea/printer)
(catch IllegalStateException _
;; ...if not, just alter the root binding.
(alter-var-root #'s/*explain-out* (constantly ea/printer))))

View file

@ -14,14 +14,18 @@
:plugins [[jainsahab/lein-githooks "1.0.0"]]
:profiles {:dev {:dependencies [[org.testcontainers/postgresql "1.14.3"]
[lambdaisland/kaocha-cloverage "1.0-45"]
:profiles {:dev {:dependencies [[expound "0.8.5"]
[lambdaisland/kaocha "1.0.641"]
[lambdaisland/kaocha-cloverage "1.0-45"]
[lambdaisland/kaocha-junit-xml "0.0.76"]
[mvxcvi/cljstyle "0.13.0" :exclusions [org.clojure/clojure]]]
[lambdaisland/kaocha-junit-xml "0.0.76"]
[mvxcvi/cljstyle "0.13.0" :exclusions [org.clojure/clojure]]
[org.clojure/test.check "1.1.0"]
[org.clojure/tools.namespace "1.0.0"]
[org.testcontainers/postgresql "1.14.3"]]
:githooks {:auto-install true
:ci-env-variable "CI"
:pre-commit ["script/pre-commit"]}}}
:pre-commit ["script/pre-commit"]}
:source-paths ["dev-src"]}}
:target-path "target/%s")

View file

@ -1,5 +1,7 @@
(ns clj-test-containers.core
(:require
[clj-test-containers.spec.container :as csc]
[clj-test-containers.spec.core :as cs]
[clojure.spec.alpha :as s])
(:import
(java.nio.file
@ -19,6 +21,10 @@
BindMode/READ_WRITE
BindMode/READ_ONLY))
(s/fdef init
:args (s/cat :init-options ::cs/init-options)
:ret ::cs/container)
(defn init
"Sets the properties for a testcontainer instance"
[{:keys [container exposed-ports env-vars command network network-aliases]}]
@ -42,6 +48,10 @@
:host (.getHost container)
:network network})
(s/fdef create
:args (s/cat :create-options ::cs/create-options)
:ret ::cs/container)
(defn create
"Creates a generic testcontainer and sets its properties"
[{:keys [image-name] :as options}]
@ -122,26 +132,27 @@
(dissoc :id)
(dissoc :mapped-ports)))
(defn- build-network
[{:keys [ipv6 driver]}]
(let [builder (Network/builder)]
(s/fdef init-network
:args (s/alt :nullary (s/cat)
:unary (s/cat :init-network-options
::cs/init-network-options))
:ret ::cs/network)
(when ipv6
(.enableIpv6 builder true))
(when driver
(.driver builder driver))
(let [network (.build builder)]
{:network network
:id (.getId network)
:name (.getName network)
:ipv6 (.getEnableIpv6 network)
:driver (.getDriver network)})))
(defn init-network
(defn ^:no-gen init-network
"Creates a network. The optional map accepts config values for enabling ipv6 and setting the driver"
([]
(build-network {}))
([options]
(build-network options)))
(init-network {}))
([{:keys [ipv6 driver]}]
(let [builder (Network/builder)]
(when ipv6
(.enableIpv6 builder true))
(when driver
(.driver builder driver))
(let [network (.build builder)]
{:network network
:id (.getId network)
:name (.getName network)
:ipv6 (.getEnableIpv6 network)
:driver (.getDriver network)}))))

View file

@ -0,0 +1,27 @@
(ns clj-test-containers.spec.container
(:require
[clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen])
(:import
(org.testcontainers.containers
GenericContainer)))
(s/def ::container
(s/with-gen #(instance? GenericContainer %)
#(gen/fmap (fn [image-name] (GenericContainer. image-name))
(gen/string-alphanumeric))))
(s/def ::exposed-ports
(s/coll-of (s/int-in 1 65535)))
(s/def ::env-vars
(s/map-of string? string?))
(s/def ::command
(s/coll-of string?))
(s/def ::network-aliases
(s/coll-of string?))
(s/def ::image-name
string?)

View file

@ -0,0 +1,39 @@
(ns clj-test-containers.spec.core
(:require
[clj-test-containers.spec.container :as csc]
[clj-test-containers.spec.network :as csn]
[clojure.spec.alpha :as s]))
(s/def ::network
(s/nilable (s/keys :req-un [::csn/network
::csn/id
::csn/name
::csn/ipv6
::csn/driver])))
(s/def ::container
(s/keys :req-un [::csc/container
::csc/exposed-ports
::csc/env-vars
::csc/host]
:opt-un [::network]))
(s/def ::init-options
(s/keys :req-un [::csc/container]
:opt-un [::csc/exposed-ports
::csc/env-vars
::csc/command
::network
::csc/network-aliases]))
(s/def ::create-options
(s/keys :req-un [::csc/image-name]
:opt-un [::csc/exposed-ports
::csc/env-vars
::csc/command
::network
::csc/network-aliases]))
(s/def ::init-network-options
(s/keys :opt-un [::csn/ipv6
::csn/driver]))

View file

@ -0,0 +1,22 @@
(ns clj-test-containers.spec.network
(:require
[clojure.spec.alpha :as s])
(:import
(org.testcontainers.containers
Network)))
(s/def ::id
string?)
(s/def ::ipv6
(s/nilable boolean?))
(s/def ::driver
(s/nilable string?))
(s/def ::name
string?)
(s/def ::network
(s/with-gen #(instance? Network %)
#(s/gen #{(Network/newNetwork)})))

View file

@ -1,19 +1,18 @@
(ns clj-test-containers.core-test
(:require
[clj-test-containers.core :refer :all]
[clojure.test :refer :all])
[clj-test-containers.core :as sut]
[clojure.test :refer [deftest is testing]])
(:import
(org.testcontainers.containers
PostgreSQLContainer)))
(deftest create-test
(testing "Testing basic testcontainer generic image initialisation"
(let [container (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
initialized-container (start! container)
stopped-container (stop! container)]
(let [container (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
initialized-container (sut/start! container)
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 5432)))
@ -21,10 +20,10 @@
(is (nil? (:mapped-ports stopped-container)))))
(testing "Testing basic testcontainer image creation from docker file"
(let [container (create-from-docker-file {:exposed-ports [80]
:docker-file "test/resources/Dockerfile"})
initialized-container (start! container)
stopped-container (stop! container)]
(let [container (sut/create-from-docker-file {:exposed-ports [80]
:docker-file "test/resources/Dockerfile"})
initialized-container (sut/start! container)
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 80)))
@ -33,37 +32,37 @@
(testing "Executing a command in the running Docker container with a custom container"
(let [container (init {:container (PostgreSQLContainer. "postgres:12.2")})
initialized-container (start! container)
result (execute-command! initialized-container ["whoami"])
stopped-container (stop! container)]
(let [container (sut/init {:container (PostgreSQLContainer. "postgres:12.2")})
initialized-container (sut/start! container)
result (sut/execute-command! initialized-container ["whoami"])
_stopped-container (sut/stop! container)]
(is (= 0 (:exit-code result)))
(is (= "root\n" (:stdout result))))))
(deftest execute-command-in-container
(testing "Executing a command in the running Docker container"
(let [container (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
initialized-container (start! container)
result (execute-command! initialized-container ["whoami"])
stopped-container (stop! container)]
(let [container (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
initialized-container (sut/start! container)
result (sut/execute-command! initialized-container ["whoami"])
_stopped-container (sut/stop! container)]
(is (= 0 (:exit-code result)))
(is (= "root\n" (:stdout result))))))
(deftest init-volume-test
(testing "Testing mapping of a classpath resource"
(let [container (-> (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(map-classpath-resource! {:resource-path "test.sql"
:container-path "/opt/test.sql"
:mode :read-only}))
initialized-container (start! container)
file-check (execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (stop! container)]
(let [container (-> (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(sut/map-classpath-resource! {:resource-path "test.sql"
:container-path "/opt/test.sql"
:mode :read-only}))
initialized-container (sut/start! container)
file-check (sut/execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 5432)))
@ -72,15 +71,15 @@
(is (nil? (:mapped-ports stopped-container)))))
(testing "Testing mapping of a filesystem-binding"
(let [container (-> (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(bind-filesystem! {:host-path "."
:container-path "/opt"
:mode :read-only}))
initialized-container (start! container)
file-check (execute-command! initialized-container ["tail" "/opt/README.md"])
stopped-container (stop! container)]
(let [container (-> (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(sut/bind-filesystem! {:host-path "."
:container-path "/opt"
:mode :read-only}))
initialized-container (sut/start! container)
file-check (sut/execute-command! initialized-container ["tail" "/opt/README.md"])
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 5432)))
@ -89,15 +88,15 @@
(is (nil? (:mapped-ports stopped-container)))))
(testing "Copying a file from the host into the container"
(let [container (-> (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(copy-file-to-container! {:path "test.sql"
:container-path "/opt/test.sql"
:type :host-path}))
initialized-container (start! container)
file-check (execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (stop! container)]
(let [container (-> (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(sut/copy-file-to-container! {:path "test.sql"
:container-path "/opt/test.sql"
:type :host-path}))
initialized-container (sut/start! container)
file-check (sut/execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 5432)))
@ -106,15 +105,15 @@
(is (nil? (:mapped-ports stopped-container)))))
(testing "Copying a file from the classpath into the container"
(let [container (-> (create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(copy-file-to-container! {:path "test.sql"
:container-path "/opt/test.sql"
:type :classpath-resource}))
initialized-container (start! container)
file-check (execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (stop! container)]
(let [container (-> (sut/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" "pw"}})
(sut/copy-file-to-container! {:path "test.sql"
:container-path "/opt/test.sql"
:type :classpath-resource}))
initialized-container (sut/start! container)
file-check (sut/execute-command! initialized-container ["tail" "/opt/test.sql"])
stopped-container (sut/stop! container)]
(is (some? (:id initialized-container)))
(is (some? (:mapped-ports initialized-container)))
(is (some? (get (:mapped-ports initialized-container) 5432)))
@ -123,23 +122,21 @@
(is (nil? (:mapped-ports stopped-container))))))
(deftest networking-test
(testing "Putting two containers into the same network and check their communication"
(let [network (init-network)
server-container (create {:image-name "alpine:3.5"
:network network
:network-aliases ["foo"]
:command ["/bin/sh"
"-c"
"while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"]})
client-container (create {:image-name "alpine:3.5"
:network network
:command ["top"]})
started-server (start! server-container)
started-client (start! client-container)
response (execute-command! started-client ["wget", "-O", "-", "http://foo:8080"])
stopped-server (stop! started-server)
stopped-client (stop! started-client)]
(let [network (sut/init-network)
server-container (sut/create {:image-name "alpine:3.5"
:network network
:network-aliases ["foo"]
:command ["/bin/sh"
"-c"
"while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"]})
client-container (sut/create {:image-name "alpine:3.5"
:network network
:command ["top"]})
started-server (sut/start! server-container)
started-client (sut/start! client-container)
response (sut/execute-command! started-client ["wget", "-O", "-", "http://foo:8080"])
_stopped-server (sut/stop! started-server)
_stopped-client (sut/stop! started-client)]
(is (= 0 (:exit-code response)))
(is (= "yay" (:stdout response))))))

View file

@ -2,4 +2,5 @@
{:plugins [:kaocha.plugin/junit-xml
:kaocha.plugin/cloverage
:kaocha.plugin.alpha/spec-test-check]
:reporter [kaocha.report/documentation]
:kaocha.plugin.junit-xml/target-file "target/junit.xml"}