Skip to content

Commit

Permalink
doc: start working on a new version of README
Browse files Browse the repository at this point in the history
  • Loading branch information
massix committed Dec 26, 2023
1 parent 3b0bf20 commit b09f78a
Showing 1 changed file with 146 additions and 66 deletions.
212 changes: 146 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,167 @@
# Purescript-TestContainers
# Testcontainers for PureScript

![Tests](https://github.com/massix/purescript-testcontainers/actions/workflows/purescript.yml/badge.svg)

## Description
This is my first attempt at creating a library for PureScript.
![Testcontainers Logo](https://testcontainers.com/images/testcontainers-logo.svg)

## Table of contents

- [Introduction](#introduction)
- [Quick example](#quick-example)
- [Features](#features)
- [Local Development](#local-development)
- [Containers](#containers)
- [Create container](#create-container)
- [Start a container](#start-a-container)
- [Stop a container](#stop-a-container)
- [Set a Wait Strategy](#set-a-wait-strategy)
- [Environment variables](#environment-variables)
- [Privileged Mode](#privileged-mode)
- [Capabilities](#capabilities)
- [Set User](#set-user)
- [Exec a command](#exec-a-command)
- [Use the withContainer helper](#use-the-withcontainer-helper)
- [Configure with a Monad](#configure-with-a-monad)
- [Network](#network)
- [Create a network](#create-a-network)
- [Attach a container to a network](#attach-a-container-to-a-network)
- [Docker Compose](#docker-compose)

## Introduction
[Testcontainers](https://testcontainers.com/) is an **opensource framework**
for providing throwaway, lightweight instances of databases, message brokers,
web browsers, or just about **anything that can run in a Docker container**.

No more need for mocks or complicated environment configurations.
**Define your test dependencies as code**, then simply run your tests and
containers will be created and then deleted.

With support for many languages and testing frameworks,
**all you need is Docker**.

## Quick example
A lot of examples are in the [tests folder](./test/Test/), but since we all
love to see some code from time to time, here is a very quick example on how to
launch an `alpine` container and execute the `ps` command in it, checking the
result:

It is still a work in progress, but some functionalities are there already, it might
cover some of the use-cases (creating a container, executing commands, opening ports)
when integration testing, for example creating a PostgreSQL container and connecting
to it.
```purescript
module Main where
The [test] folder contains some very basic examples.
import Prelude
import Data.Either (Either(..))
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Console as Console
import Test.TestContainers as TC
main :: Effect Unit
main = do
launchAff_ $ do
let alpineContainer = TC.setCommand [ "sleep", "infinity" ] $ TC.mkContainer "alpine"
eitherStarted <- TC.startContainer alpineContainer
case eitherStarted of
Left err -> Console.logShow err
Right started -> do
eitherExec <- TC.exec [ "ps" ] started
case eitherExec of
Left err -> Console.logShow err
Right { output, exitCode } -> do
Console.log $ "ps output: " <> output <> ", exitCode: " <> show exitCode
```

## Usage
In order to use this library you need to install `testcontainers` using node in your
environment:
This is to be considered as a _low-level_ library, hence it is not making use of
complex monads transformers or anything, it's up to the users to define their own
mtl stack if they need to.

$ npm install testcontainers
The library uses `Either String a` as a generic return value for almost all the
operations, where `a` is the success type, depending on the function called,
and `String` is to return the errors coming from the underlying FFI interface.
## Features
Not all the features of the original Testcontainers library have been
implemented yet, I plan to cover 100% of the functionalities but it will take
some time to develop everything.

After that, you need to install this library in your project, it can be done quite
easily if using `spago@next`, simply modify the workspace section of the `spago.yaml`
file and add this GitHub repository as a remote source.
For now, this is a list of the supported features, more details for each
feature are provided further down in the document.

If you're using older versions of spago, it might be more difficult and I honestly
do not know how to do it (PRs are welcome!).
- [X] Creation of containers
- [X] Basic handling (start/stop) of containers
- [X] Define wait strategies
- [X] Start in privileged mode
- [X] Start with capabilities (enable or disabled)
- [X] Handle users
- [X] Launch commands inside of containers
- [X] Create a network
- [X] Attach containers to a network
- [X] Use `docker-compose` definitions
- [X] Up and down of a compose environment
- [X] Get containers running in a compose environment
- [X] Start containers based on a profile
- [X] Rebuild containers automatically
- [X] Set environment variables from files

## Basic Examples
## Local Development
If you want to test the library locally, you just need to have the latest
versions of `spago` and `purs` installed.

### Start a PostgreSQL container and connect to it using
### Nix
For NixOS and nix users, a [flake.nix](./flake.nix) is provided, it uses the
`purescript-overlay` to install the latest versions of `spago` and `purs`. If
you use `direnv` you can simply `direnv allow .` to start a local development
shell.

```purescript
module TestPostgreSQL where
import Prelude
import Effect (Effect)
import Test.TestContainers as TC
import Test.TestContainers.Types as TC
import Yoga.Postgres as YP
## Containers

main :: Effect Unit
main = do
TC.withContainer mkPostgreSQLContainer $ \container -> do
liftEffect $ "PostgreSQL container started: " <> show container
{ host, port } <- liftEffect $ do
host <- TC.getHost container
port <- TC.getFirstMappedPort container
pure { host, port }
pool <- YG.mkPool $ mkConnection host port
launchAff_ $ do
withClient pool $ \c -> do
YG.execute_ (YG.Query "create table t (id int)") c
where
mkPostgreSQLContainer :: TC.TestContainer
mkPostgreSQLContainer =
TC.mkContainer "postgres:latest" $
TC.setExposedPorts [ 5432 ] <<<
-- PostgreSQL container will output this string twice while starting
-- The first one is before creating the database, the second one is the real one
TC.setWaitStrategy [ (TC.LogOutput "database system is ready to accept connections" 2) ] <<<
TC.setEnvironment
[ { key: "POSTGRES_DB", value: "test" }
, { key: "POSTGRES_USER", value: "test" }
, { key: "POSTGRES_PASSWORD", value: "test" }
]
mkConnection :: String -> Int -> YG.ConnectionInfo
mkConnection host port = YG.connectionInfoFromString $ "postgres://" <> host <> ":" <> show port <> "/test"
```
### Create container

### Start a container

## Contributions
### Stop a container

Contributions are welcome, the project is still fresh and there's a lot to do!
### Set a Wait Strategy

### Environment variables

## Features
### Privileged Mode

### Capabilities

### Set User

### Exec a command

### Use the `withContainer` helper

### Configure with a Monad

## Network

### Create a network

### Attach a container to a network

## Docker Compose

### Create an environment

### Up an environment

### Down an environment

### Use the `withCompose` helper

### Get a container from the environment

### Set Wait strategies for containers

### Use Profiles

### Automatically rebuild

### Use Environment variables

#### From files

- [x] Basic containers management (start, stop, port forwarding, ...)
- [x] Network management (create, attach, delete)
- [x] Docker compose integration
- [ ] Building of images
#### From Code

0 comments on commit b09f78a

Please sign in to comment.