Docker Compose management for PureScript integration tests.
This package provides utilities for managing Docker Compose services in integration test suites. It uses FFI to call docker compose commands and integrates seamlessly with PureScript's bracket pattern for guaranteed cleanup.
Add to your test dependencies in spago.yaml:
test:
dependencies:
- yoga-test-dockermodule Test.MyPackage.Main where
import Prelude
import Effect (Effect)
import Effect.Aff (launchAff_, bracket)
import Effect.Class (liftEffect)
import Effect.Console (log)
import Test.Spec.Runner (runSpec)
import Yoga.Test.Docker as Docker
main :: Effect Unit
main = launchAff_ do
liftEffect $ log "🧪 Starting tests with Docker\n"
bracket
-- Start Docker before tests
(do
liftEffect $ log "⏳ Starting Docker service..."
Docker.startService "docker-compose.test.yml" 30
liftEffect $ log "✅ Docker service ready!\n"
)
-- Stop Docker after tests (always runs!)
(\_ -> do
Docker.stopService "docker-compose.test.yml"
liftEffect $ log "✅ Cleanup complete\n"
)
-- Run tests
(\_ -> runSpec [ consoleReporter ] spec)If your docker-compose.test.yml is in the package directory:
-- From packages/my-package/test/Main.purs
Docker.startService "../docker-compose.test.yml" 30Or specify the full relative path from workspace root:
Docker.startService "packages/my-package/docker-compose.test.yml" 30startService :: String -> Int -> Aff UnitStart a Docker Compose service and wait for it to be healthy.
Parameters:
composeFile- Path to docker-compose.yml file (relative to workspace root)maxWaitSeconds- Maximum seconds to wait for service to become healthy
Example:
Docker.startService "docker-compose.test.yml" 30stopService :: String -> Aff UnitStop a Docker Compose service.
Parameters:
composeFile- Path to docker-compose.yml file
Example:
Docker.stopService "docker-compose.test.yml"waitForHealthy :: String -> Int -> Aff UnitWait for a service to become healthy.
Parameters:
composeFile- Path to docker-compose.yml filemaxAttempts- Maximum number of 1-second attempts
dockerComposeUp :: String -> Aff Unit
dockerComposeDown :: String -> Aff Unit
isServiceHealthy :: String -> Aff BooleanDirect access to Docker Compose commands if you need custom logic.
Your docker-compose.test.yml should include health checks:
version: '3.8'
services:
myservice-test:
image: redis:7-alpine
ports:
- "6380:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 2s
timeout: 3s
retries: 10The bracket pattern ensures Docker services are stopped even if:
- Tests fail
- An exception occurs
- User presses Ctrl+C
This prevents orphaned Docker containers.
- FFI Layer: JavaScript implementation uses Node's
child_process.spawnSync - Aff Integration: Functions return
Afffor composable async operations - Bracket Pattern: Guarantees cleanup via PureScript's resource management
- Health Checks: Polls
docker compose psuntil service reports healthy
Error: spawn docker ENOENT
Solution: Install Docker Desktop and ensure it's running.
Error: port is already allocated
Solution: Stop conflicting containers
docker compose -f docker-compose.test.yml downError: Service failed to become healthy
Solution: Check logs
docker compose -f docker-compose.test.yml logsIncrease maxWaitSeconds for slower services (e.g., ScyllaDB needs 60s).
- Use bracket: Always use
bracketfor guaranteed cleanup - Health checks: Define proper health checks in docker-compose.yml
- Unique ports: Use non-standard ports to avoid conflicts (e.g., 6380 instead of 6379)
- Test isolation: Each package should have its own docker-compose.test.yml
- Sufficient timeout: Allow enough time for services to start (30s for most, 60s for databases like ScyllaDB)
MIT