yoga-test-docker
Docker Compose management for PureScript integration tests.
Overview
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.
Installation
Add to your test dependencies in spago.yaml:
test:
dependencies:
- yoga-test-docker
Usage
Basic Example
module 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)
Advanced: Relative Paths
If your docker-compose.test.yml is in the package directory:
-- From packages/my-package/test/Main.purs
Docker.startService "../docker-compose.test.yml" 30
Or specify the full relative path from workspace root:
Docker.startService "packages/my-package/docker-compose.test.yml" 30
API
startService
startService :: String -> Int -> Aff Unit
Start 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" 30
stopService
stopService :: String -> Aff Unit
Stop a Docker Compose service.
Parameters:
composeFile- Path to docker-compose.yml file
Example:
Docker.stopService "docker-compose.test.yml"
waitForHealthy
waitForHealthy :: String -> Int -> Aff Unit
Wait for a service to become healthy.
Parameters:
composeFile- Path to docker-compose.yml filemaxAttempts- Maximum number of 1-second attempts
Low-Level Functions
dockerComposeUp :: String -> Aff Unit
dockerComposeDown :: String -> Aff Unit
isServiceHealthy :: String -> Aff Boolean
Direct access to Docker Compose commands if you need custom logic.
Docker Compose File
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: 10
Guaranteed Cleanup
The bracket pattern ensures Docker services are stopped even if:
- Tests fail
- An exception occurs
- User presses Ctrl+C
This prevents orphaned Docker containers.
How It Works
- 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
Troubleshooting
Docker Not Found
Error: spawn docker ENOENT
Solution: Install Docker Desktop and ensure it's running.
Port Already in Use
Error: port is already allocated
Solution: Stop conflicting containers
docker compose -f docker-compose.test.yml down
Service Never Healthy
Error: Service failed to become healthy
Solution: Check logs
docker compose -f docker-compose.test.yml logs
Increase maxWaitSeconds for slower services (e.g., ScyllaDB needs 60s).
Best Practices
- 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)
License
MIT