diff --git a/README.md b/README.md index 99e2a78..8a20215 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,211 @@ This library does not provide tools to include testcontainers in your testing li ## Usage + +The library provides a set of functions to interact with the testcontainers. A simple exampe could look like this: + ```clojure (require '[clj-test-containers.core :as tc]) -(def container (tc/create {:image-name "postgres:12.1" - :exposed-ports [5432] - :env-vars {"POSTGRES_PASSWORD" "verysecret"}})) +(def container (-> (tc/create {:image-name "postgres:12.1" + :exposed-ports [5432] + :env-vars {"POSTGRES_PASSWORD" "verysecret"}}) + (tc/configure-volume! {:file-system-bind {:host-path "/tmp" + :container-path "/opt" + :mode :read-only}}))) -(tc/start postgres) +(do-database-testing (:host container) + (get (:mapped-ports container) 5432)) -(tc/stop postgres) +(tc/stop! container) ``` +## Functions and Properties + +### create +Creates a testcontainers instance and returns them + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| `:image-name` | String, mandatory | The name and label of an image, e.g. `postgres:12.2` | +| `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port | +| `:env-vars` | Map | A map with environment variables| +| `:command` | Vector with strings | Environment Variables to be set in the container| + +#### Result: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| `:container` | `org.testcontainers.containers.Container` | The Testcontainers instance, accessible for everything this library doesn't provide (yet) | +| `:exposed-ports` | Vector with ints | Value of the same input parameter | +| `:env-vars` | Map | Value of the same input parameter| +| `:host` | String | The host for the Docker Container| + +#### Example: + +```clojure +(create {:image-name "alpine:3.2" + :exposed-ports [80] + :env-vars {"MAGIC_NUMBER" "42" + :command ["/bin/sh" + "-c" + "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"]}) +``` + +### start! +Starts the Testcontainer, which was defined by `create` + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | + +#### Result: +The `container-config` + +#### Example: + +```clojure +(def container (create {:image-name "alpine:3.2" + :exposed-ports [80] + :env-vars {"MAGIC_NUMBER" "42"}) + +(start! container) +``` + +### stop! +Stops the Testcontainer, which was defined by `create` + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | + +#### Result: +The `container-config` + +#### Example: + +```clojure +(def container (create {:image-name "alpine:3.2" + :exposed-ports [80] + :env-vars {"MAGIC_NUMBER" "42"}) + +(start! container) +(stop! container) +``` + +### map-classpath-resource! +Maps a resource from your classpath into the containers file system + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | +| Second parameter: | | | +| `:resource-path` | String, mandatory | Path of your classpath resource | +| `:container-path` | String, mandatory | Path, to which the resource should be mapped | +| `:mode` | Keyword, mandatory | `:read-only` or `:read-write` | + + +#### Result: +The `container-config` + +#### Example: + +```clojure +(map-classpath-resource! container {:resource-path "test.sql" + :container-path "/opt/test.sql" + :mode :read-only}) +``` + +### bind-filesystem! +Binds a path from your local filesystem into the Docker container as a volume + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | +| Second parameter: | | | +| `:host-path` | String , mandatory | Path on your local filesystem | +| `:container-path` | String, mandatory | Path, to which the resource should be mapped | +| `:mode` | Keyword, mandatory | `:read-only` or `:read-write` | + + +#### Result: +The `container-config` + +#### Example: + +```clojure +(bind-filesystem! container {:host-path "." + :container-path "/opt" + :mode :read-only}) +``` + +### copy-file-to-container! +Copies a file from your filesystem or classpath into the running container + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | +| Second parameter: | | | +| `:path` | String, mandatory | Path to a classpath resource *or* file on your filesystem | +| `:host-path` | String, mandatory | Path, to which the file should be copied | +| `:type` | Keyword, mandatory | `:classpath-resource` or `:host-path` | + + +#### Result: +The `container-config` + +#### Example: + +```clojure +(copy-file-to-container! container {:path "test.sql" + :container-path "/opt/test.sql" + :type :host-path}) +``` + +### execute-command! +Executes a command in the running container, and returns the result + +#### Config parameters: + +| Key | Type | Description | +| ------------- |:------------- | :-----| +| First parameter: | | | +| `container-config`| Map, mandatory | Return value of the `create` function | +| Second parameter: | | | +| `command` | Vector with Strings, mandatory | A vector containing the command and its parameters | + + + +#### Result: +| Key | Type | Description | +| ------------- |:------------- | :-----| +| `:exit-code` | int | Exit code of the executed command | +| `:stdout` | String | Content of stdout | +| `:stdin` | String | Content of stdin | + +#### Example: + +```clojure +(execute-command! container ["tail" "/opt/test.sql"]) +``` + + ## License diff --git a/project.clj b/project.clj index e3766ab..0fad771 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject clj-test-containers "0.1.0-SNAPSHOT" +(defproject clj-test-containers "0.1.0" :description "A lightweight, unofficial wrapper around the Testcontainers Java library" :url "https://github.com/javahippie/clj-test-containers" :license {:name "Eclipse Public License" diff --git a/src/clj_test_containers/core.clj b/src/clj_test_containers/core.clj index a679254..d142fdf 100644 --- a/src/clj_test_containers/core.clj +++ b/src/clj_test_containers/core.clj @@ -1,4 +1,4 @@ -5(ns clj-test-containers.core) +(ns clj-test-containers.core) (defn- resolve-bind-mode [bind-mode] (if (= :read-write bind-mode) @@ -29,27 +29,29 @@ :host (.getHost container)})) -(defn configure-volume! [container-config - {classpath-resource-mapping :classpath-resource-mapping - file-system-bind :file-system-bind}] +(defn map-classpath-resource! + "Maps a resource in the classpath to the given container path. Should be called before starting the container!" + [container-config + {resource-path :resource-path + container-path :container-path + mode :mode}] + + (assoc container-config :container (.withClasspathResourceMapping (:container container-config) + resource-path + container-path + (resolve-bind-mode mode)))) - (when-let [{resource-path :resource-path - container-path :container-path - mode :mode} classpath-resource-mapping] - (.withClasspathResourceMapping (:container container-config) - resource-path - container-path - (resolve-bind-mode mode))) +(defn bind-filesystem! + "Binds a source from the filesystem to the given container path. Should be called before starting the container!" + [container-config + {host-path :host-path + container-path :container-path + mode :mode}] - - (when-let [{host-path :host-path - container-path :container-path - mode :mode} file-system-bind] - (.withFileSystemBind (:container container-config) - host-path - container-path)) - - container-config) + (assoc container-config :container (.withFileSystemBind (:container container-config) + host-path + container-path + (resolve-bind-mode mode)))) (defn copy-file-to-container! "Copies a file into the running container" @@ -67,7 +69,8 @@ mountable-file container-path)))) -(defn execute-command +(defn execute-command! + "Executes a command in the container, and returns the result" [container-conf command] (let [container (:container container-conf) result (.execInContainer container diff --git a/test/clj_test_containers/core_test.clj b/test/clj_test_containers/core_test.clj index cfd65d7..9e9de05 100644 --- a/test/clj_test_containers/core_test.clj +++ b/test/clj_test_containers/core_test.clj @@ -22,22 +22,22 @@ :exposed-ports [5432] :env-vars {"POSTGRES_PASSWORD" "pw"}}) initialized-container (start! container) - result (execute-command initialized-container ["whoami"]) + result (execute-command! initialized-container ["whoami"]) stopped-container (stop! container)] (is (= 0 (:exit-code result))) (is (= "root\n" (:stdout result)))))) -(deftest init-with-volume-test +(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"}}) - (configure-volume! {:classpath-resource-mapping {:resource-path "test.sql" - :container-path "/opt/test.sql" - :mode :read-only}})) + (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"]) + file-check (execute-command! initialized-container ["tail" "/opt/test.sql"]) stopped-container (stop! container)] (is (some? (:id initialized-container))) (is (some? (:mapped-ports initialized-container))) @@ -50,11 +50,11 @@ (let [container (-> (create {:image-name "postgres:12.2" :exposed-ports [5432] :env-vars {"POSTGRES_PASSWORD" "pw"}}) - (configure-volume! {:file-system-bind {:host-path "." - :container-path "/opt" - :mode :read-only}})) + (bind-filesystem! {:host-path "." + :container-path "/opt" + :mode :read-only})) initialized-container (start! container) - file-check (execute-command initialized-container ["tail" "/opt/README.md"]) + file-check (execute-command! initialized-container ["tail" "/opt/README.md"]) stopped-container (stop! container)] (is (some? (:id initialized-container))) (is (some? (:mapped-ports initialized-container))) @@ -71,7 +71,7 @@ :container-path "/opt/test.sql" :type :host-path})) initialized-container (start! container) - file-check (execute-command initialized-container ["tail" "/opt/test.sql"]) + file-check (execute-command! initialized-container ["tail" "/opt/test.sql"]) stopped-container (stop! container)] (is (some? (:id initialized-container))) (is (some? (:mapped-ports initialized-container))) @@ -88,7 +88,7 @@ :container-path "/opt/test.sql" :type :classpath-resource})) initialized-container (start! container) - file-check (execute-command initialized-container ["tail" "/opt/test.sql"]) + file-check (execute-command! initialized-container ["tail" "/opt/test.sql"]) stopped-container (stop! container)] (is (some? (:id initialized-container))) (is (some? (:mapped-ports initialized-container)))