Compare commits

...

4 commits

Author SHA1 Message Date
Tim Zöller
b2610ee291 Set proper group ID for Maven
This will make it possible to publish the artifact under the existing
Testcontainers Maven group
2024-04-14 16:57:17 +02:00
Tim Zöller
7d2ff63148 Added env variable for reusable containers (#72)
The flag is needed for reusable containes to work
2024-04-14 14:21:54 +02:00
Tim Zöller
0e2d483715 Added :reuse flag (#72)
Included a new flag for the container reuse feature
2024-04-14 14:07:31 +02:00
Tim Zöller
3809bbb28c Refactoring to use new namespaces
This matches the default testcontainers naming patterns and gets rid of
the hyphen
2024-04-14 12:52:23 +02:00
9 changed files with 78 additions and 36 deletions

View file

@ -6,6 +6,9 @@ on:
pull_request:
branches: [ main ]
env:
TESTCONTAINERS_REUSE_ENABLE: true
permissions:
contents: read

View file

@ -1,4 +1,4 @@
# clj-test-containers
# testcontainers-clj
[![Clojars Project](http://clojars.org/clj-test-containers/latest-version.svg)](http://clojars.org/clj-test-containers)
@ -11,17 +11,13 @@ This library is a lightweight wrapper around the [Testcontainers Java library](h
This library does not provide tools to include testcontainers in your testing lifecycle. As there are many different
test tools with different approaches to testing in the clojure world, handling the lifecycle is up to you.
## Integration with test runners
There is an [experimental kaocha plugin](https://github.com/lambdaschmiede/kaocha-testcontainers-plugin) you can try out
## Usage
The library provides a set of functions to interact with the testcontainers. A simple example, how to create a container
with a Docker label, could look like this:
```clojure
(require '[clj-test-containers.core :as tc])
(require '[testcontainers-clj.core :as tc])
(def container (-> (tc/create {:image-name "postgres:12.1"
:exposed-ports [5432]
@ -41,7 +37,7 @@ If you'd rather create a container from a Dockerfile in your project, it could l
```clojure
(require '[clj-test-containers.core :as tc])
(require '[testcontainers-clj.core :as tc])
(def container (-> (tc/create-from-docker-file {:env-vars {"FOO" "bar"}
:exposed-ports [80]
@ -52,7 +48,7 @@ If you'd rather create a container from a Dockerfile in your project, it could l
If you prefer to use prebuilt containers from the Testcontainers project, you can do it like this
```clojure
(require '[clj-test-containers.core :as tc])
(require '[testcontainers-clj.core :as tc])
(:import [org.testcontainers.containers PostgreSQLContainer])
(def container (-> (tc/init {:container (PostgreSQLContainer. "postgres:12.2")
@ -72,19 +68,21 @@ Creates a testcontainers instance from a given Docker label and returns them
| ------------- | :------------- |:----------------------------------------------------------------------------------------------------|
| `: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 |
| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? |
| `:env-vars` | Map | A map with environment variables |
| `:command` | Vector with strings | The start command of the container |
| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) |
| `:network-aliases` | Map | A list of alias names for the container on the network |
| `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for |
| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} |
| `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for |
| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} |
#### 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 |
| `:reuse` | Boolean | Is this container reusable? |
| `:env-vars` | Map | Value of the same input parameter |
| `:host` | String | The host for the Docker Container |
| `:network` | Map | The network configuration of the Container, if provided |
@ -103,6 +101,20 @@ Creates a testcontainers instance from a given Docker label and returns them
"while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"]})
```
#### Example with reuse
```clojure
(create {:image-name "alpine:3.2"
:exposed-ports [80]
:reuse true
:env-vars {"MAGIC_NUMBER" "42"}
:network (create-network)
:network-aliases ["api-server"]
:command ["/bin/sh"
"-c"
"while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"]})
```
#### Example using wait-for and healthcheck:
```clojure
@ -123,17 +135,18 @@ Initializes a given Testcontainer, which was e.g. provided by a library
#### Config parameters:
| Key | Type | Description |
| ------------- | :------------- |:----------------------------------------------------------------------------------------------------|
| `:container` | `org.testcontainers.containers.GenericContainer`, 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 | The start command of the container |
| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) |
| `:network-aliases` | Map | A list of alias names for the container on the network |
| Key | Type | Description |
|--------------------|:------------------------------------------------------------|:---------------------------------------------------------------------------------------------------|
| `:container` | `org.testcontainers.containers.GenericContainer`, 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 |
| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? |
| `:env-vars` | Map | A map with environment variables |
| `:command` | Vector with strings | The start command of the container |
| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) |
| `:network-aliases` | Map | A list of alias names for the container on the network |
| `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for |
| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} |
| | | |
| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} |
| | | |
#### Result:
@ -141,6 +154,7 @@ Initializes a given Testcontainer, which was e.g. provided by a library
| ------------- | :------------- |:------------------------------------------------------------------------------------------|
| `: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 |
| `:reuse` | Boolean | Is this container reusable? |
| `:env-vars` | Map | Value of the same input parameter |
| `:host` | String | The host for the Docker Container |
| `:network` | Map | The network configuration of the Container, if provided |
@ -181,6 +195,7 @@ Creates a testcontainer from a Dockerfile
| ------------- | :------------- | :----- |
| `:docker-file` | String, mandatory | String containing a path to a Dockerfile |
| `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port |
| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? |
| `:env-vars` | Map | A map with environment variables |
| `:command` | Vector with strings | The start command of the container |
| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) |
@ -195,6 +210,7 @@ Creates a testcontainer from a Dockerfile
| ------------- | :------------- | :----- |
| `: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 |
| `:reuse` | Boolean | Is this container reusable? |
| `:env-vars` | Map | Value of the same input parameter |
| `:host` | String | The host for the Docker Container |
| `:network` | Map | The network configuration of the Container, if provided |
@ -231,6 +247,7 @@ Starts the Testcontainer, which was defined by `create`
| ------------- | :------------- | :----- |
| `: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 |
| `:reuse` | Boolean | Is this container reusable? |
| `:env-vars` | Map | Value of the same input parameter |
| `:host` | String | The host for the Docker Container |
| `:id` | String | The ID of the started docker container |

View file

@ -15,7 +15,7 @@ The functions accept and return a map structure, which enables us to operate the
consistent way. The example shown with Java Interop above would look like this, when using the wrapped functions:
```clojure
(require '[clj-test-containers.core :as tc])
(require '[testcontainers-clj.core :as tc])
(deftest db-integration-test
(testing "A simple PostgreSQL integration test"

View file

@ -1,4 +1,4 @@
(defproject testcontainers-clj "unspecified"
(defproject org.testcontainers/testcontainers-clj "unspecified"
:description "A lightweight, official wrapper around the Testcontainers Java library"
:url "https://github.com/testcontainers/testcontainers-clj"

View file

@ -1,6 +1,6 @@
(ns clj-test-containers.core
(ns testcontainers-clj.core
(:require
[clj-test-containers.spec.core :as cs]
[testcontainers-clj.spec.core :as cs]
[clojure.spec.alpha :as s]
[clojure.string])
(:import
@ -183,6 +183,7 @@
"Sets the properties for a testcontainer instance"
[{:keys [^GenericContainer container
exposed-ports
reuse
env-vars
command
network
@ -194,6 +195,9 @@
(doseq [[k v] env-vars]
(.addEnv container k v))
(when reuse
(.withReuse container true))
(when command
(.setCommand container ^"[Ljava.lang.String;" (into-array String command)))
@ -425,5 +429,6 @@
;; REPL Helpers
(comment
(start! (create {:image-name "postgres:12.1"}))
(perform-cleanup!))
(start! (create {:image-name "postgres:12.1" :reuse true}))
(perform-cleanup!)
)

View file

@ -1,4 +1,4 @@
(ns clj-test-containers.spec.container
(ns testcontainers-clj.spec.container
(:require
[clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen])
@ -14,6 +14,9 @@
(s/def ::exposed-ports
(s/coll-of (s/int-in 1 65535)))
(s/def ::reuse
boolean?)
(s/def ::env-vars
(s/map-of string? string?))

View file

@ -1,7 +1,7 @@
(ns clj-test-containers.spec.core
(ns testcontainers-clj.spec.core
(:require
[clj-test-containers.spec.container :as csc]
[clj-test-containers.spec.network :as csn]
[testcontainers-clj.spec.container :as csc]
[testcontainers-clj.spec.network :as csn]
[clojure.spec.alpha :as s]))
(s/def ::wait-for
@ -25,13 +25,15 @@
::csc/exposed-ports
::csc/env-vars
::csc/host]
:opt-un [::network
:opt-un [::csc/reuse
::network
::wait-for
::log-to]))
(s/def ::init-options
(s/keys :req-un [::csc/container]
:opt-un [::csc/exposed-ports
::csc/reuse
::csc/env-vars
::csc/command
::network

View file

@ -1,4 +1,4 @@
(ns clj-test-containers.spec.network
(ns testcontainers-clj.spec.network
(:require
[clojure.spec.alpha :as s])
(:import

View file

@ -1,6 +1,6 @@
(ns clj-test-containers.core-test
(ns testcontainers-clj.core-test
(:require
[clj-test-containers.core :as sut]
[testcontainers-clj.core :as sut]
[clojure.string :refer [includes?]]
[clojure.test :refer [deftest is testing]])
(:import
@ -115,7 +115,19 @@
result (sut/execute-command! initialized-container ["whoami"])
_stopped-container (sut/stop! container)]
(is (= 0 (:exit-code result)))
(is (= "root\n" (:stdout result))))))
(is (= "root\n" (:stdout result)))))
(testing "Reusing a container with the :reuse flag"
(let [container-1 (sut/init {:container (PostgreSQLContainer. "postgres:15.3")
:exposed-ports [5432]
:reuse true})
container-2 (sut/init {:container (PostgreSQLContainer. "postgres:15.3")
:exposed-ports [5432]
:reuse true})
initialized-container-1 (sut/start! container-1)
initialized-container-2 (sut/start! container-2)]
(is (= (.getContainerId (:container initialized-container-1))
(.getContainerId (:container initialized-container-2)))))))
(deftest execute-command-in-container