Files
dokku-docker-compose/docs/TESTING.md

13 KiB

Testing Strategy for Dokku Docker Compose Plugin

Test Types

1. Plugin Integration Tests

Purpose: Test integration with various Dokku plugins Location: tests/integration/plugins/ What to test:

  • Plugin detection and selection
  • Configuration mapping from docker-compose to plugin
  • Service linking with plugins
  • Fallback behavior when plugins are not available
  • Version-specific plugin features

2. Unit Tests

Purpose: Test individual functions in isolation Tools: bats-core (Bash Automated Testing System) Location: tests/unit/ What to test:

  • Small, pure functions
  • Input validation
  • Output formatting
  • Error conditions

Example test file structure:

tests/unit/
  ├── test_helpers.bats
  ├── test_validation.bats
  └── test_parser.bats

3. Integration Tests

Purpose: Test interactions between components Tools: bats-core, Docker-in-Docker (DinD) Location: tests/integration/ What to test:

  • Component interactions
  • File system operations
  • Command execution
  • Docker API interactions

4. End-to-End (E2E) Tests

Purpose: Test the plugin in a real Dokku environment Tools:

  • Dokku Test Suite
  • Docker Compose test environments Location: tests/e2e/ What to test:
  • Full plugin commands
  • Real Dokku app creation
  • Network and volume handling
  • Multi-service scenarios

Test Environment

Plugin-Specific Test Setup

For testing with different Dokku plugins, we'll use a matrix approach:

# .github/workflows/test-plugins.yml
name: Test with Dokku Plugins

on: [push, pull_request]

jobs:
  test-plugins:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        plugin: [postgres, redis, memcached, mysql, mariadb, mongo]
    steps:
      - uses: actions/checkout@v3
      - name: Set up Dokku
        run: |
          wget https://raw.githubusercontent.com/dokku/dokku/v0.30.0/bootstrap.sh
          sudo DOKKU_TAG=v0.30.0 bash bootstrap.sh
      - name: Install ${{ matrix.plugin }} plugin
        run: |
          sudo dokku plugin:install https://github.com/dokku/dokku-${{ matrix.plugin }}.git ${{ matrix.plugin }}
      - name: Run plugin tests
        run: |
          PLUGIN=${{ matrix.plugin }} bats tests/integration/plugins/

Prerequisites

  • Docker
  • Docker Compose
  • bats-core (brew install bats-core on macOS)
  • yq (brew install yq)
  • jq (brew install jq)

Setup

  1. Clone the repository
  2. Run make test-deps to install test dependencies
  3. Run make test to execute all tests

Test Structure

Test Data

Sample Docker Compose files for testing:

testdata/
  ├── simple/
  │   ├── docker-compose.yml
  │   └── expected/
  ├── multi-service/
  │   ├── docker-compose.yml
  │   └── expected/
  └── edge-cases/
      ├── invalid-yaml.yml
      └── unsupported-version.yml

Test Helpers

Create reusable test utilities in tests/test_helper.bash:

#!/usr/bin/env bash

setup() {
    load 'test_helper/bats-support/load'
    load 'test_helper/bats-assert/load'
    
    # Set up test environment
    TEST_DIR="$(mktemp -d)"
    cd "$TEST_DIR" || exit 1
}

teardown() {
    # Clean up test environment
    rm -rf "$TEST_DIR"
}

# Helper functions...

Writing Plugin Integration Tests

Example Plugin Test

@test "import with postgres plugin" {
    # Setup
    local test_dir="$(mktemp -d)"
    cd "$test_dir" || exit 1
    
    # Create test compose file
    cat > docker-compose.yml <<EOL
version: '3.8'
services:
  app:
    image: nginx:alpine
    environment:
      - DB_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
EOL

    # Run import
    run dokku docker-compose:import docker-compose.yml
    assert_success
    
    # Verify postgres plugin was used
    run dokku postgres:list
    assert_success
    assert_output --partial "app-db"
    
    # Verify linking
    run dokku postgres:linked app
    assert_success
    assert_output --partial "app-db"
    
    # Cleanup
    cd - >/dev/null
    rm -rf "$test_dir"
}

Testing Plugin Installation Instructions

@test "show installation instructions when plugin is missing" {
    # Setup
    local test_dir="$(mktemp -d)"
    cd "$test_dir" || exit 1
    
    # Create test compose file
    cat > docker-compose.yml <<EOL
version: '3.8'
services:
  db:
    image: postgres:13
EOL

    # Mock dokku command to simulate missing plugin
    dokku() {
        if [[ "$1" == "plugin:list" ]]; then
            echo "  00_dokku-standard    0.30.0 enabled    dokku core standard plugin"
            return 0
        fi
        return 1
    }
    export -f dokku
    
    # Run import and capture output
    run dokku docker-compose:import docker-compose.yml
    
    # Assert
    assert_failure
    assert_output --partial "Required plugin 'postgres' is not installed"
    assert_output --regexp "To install.*dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres"
    assert_output --partial "Use --skip-plugin=postgres to continue without this plugin"
    
    # Cleanup
    cd - >/dev/null
    rm -rf "$test_dir"
}

@test "continue with --skip-plugin flag" {
    # Setup
    local test_dir="$(mktemp -d)"
    cd "$test_dir" || exit 1
    
    # Create test compose file
    cat > docker-compose.yml <<EOL
version: '3.8'
services:
  app:
    image: nginx:alpine
    depends_on:
      - db
  db:
    image: postgres:13
EOL

    # Mock dokku command to simulate missing plugin
    dokku() {
        if [[ "$1" == "plugin:list" ]]; then
            echo "  00_dokku-standard    0.30.0 enabled    dokku core standard plugin"
            return 0
        fi
        # Allow other dokku commands to pass through
        command dokku "$@"
    }
    export -f dokku
    
    # Run import with skip plugin flag
    run dokku docker-compose:import --skip-plugin=postgres docker-compose.yml
    
    # Assert
    assert_success
    assert_output --partial "Skipping postgres plugin as requested"
    assert_output --partial "Using container for service: db"
    
    # Cleanup
    cd - >/dev/null
    rm -rf "$test_dir"
}

### Testing Fallback Behavior
```bash
@test "fallback to container when plugin not available" {
    # Setup
    local test_dir="$(mktemp -d)"
    cd "$test_dir" || exit 1
    
    # Create test compose file
    cat > docker-compose.yml <<EOL
version: '3.8'
services:
  app:
    image: nginx:alpine
    depends_on:
      - db
  db:
    image: postgres:13
EOL

    # Uninstall postgres plugin if installed
    if dokku plugin:installed postgres; then
        skip "Postgres plugin is installed"
    fi
    
    # Run import
    run dokku docker-compose:import docker-compose.yml
    assert_success
    
    # Verify container was created instead of using plugin
    run docker ps --format '{{.Names}}' | grep app-db
    assert_success
    
    # Cleanup
    cd - >/dev/null
    rm -rf "$test_dir"
}

Writing Tests

Example Unit Tests for Plugin Detection

@test "get_plugin_installation_command returns correct command" {
    # Test with postgres
    run get_plugin_installation_command postgres
    assert_success
    assert_output "dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres"
    
    # Test with redis
    run get_plugin_installation_command redis
    assert_success
    assert_output "dokku plugin:install https://github.com/dokku/dokku-redis.git redis"
}

@test "check_plugin_installed detects missing plugin" {
    # Mock dokku command for missing plugin
    dokku() {
        if [[ "$1" == "plugin:list" ]]; then
            echo "  00_dokku-standard    0.30.0 enabled    dokku core standard plugin"
            return 0
        fi
        return 1
    }
    export -f dokku
    
    run check_plugin_installed postgres
    assert_failure
    assert_output --partial "postgres plugin is not installed"
}

@test "generate_skip_plugins_option generates correct flags" {
    # Test single plugin
    run generate_skip_plugins_option "postgres"
    assert_success
    assert_output "--skip-plugin=postgres"
    
    # Test multiple plugins
    run generate_skip_plugins_option "postgres redis"
    assert_success
    assert_output "--skip-plugin=postgres --skip-plugin=redis"
}

Example Plugin Unit Test

@test "detect_postgres_plugin" {
    # Mock dokku command
    dokku() {
        if [[ "$1" == "plugin:list" ]]; then
            echo "  00_dokku-standard    0.30.0 enabled    dokku core standard plugin"
            echo "  postgres            1.15.0 enabled    dokku postgres service plugin"
            return 0
        fi
        return 1
    }
    export -f dokku

    # Run test
    run detect_plugin 'postgres:13'
    
    # Assert
    assert_success
    assert_output "postgres"
}

@test "map_postgres_config" {
    # Setup test data
    local service_config=$(cat <<EOL
{
    "image": "postgres:13",
    "environment": {
        "POSTGRES_USER": "user",
        "POSTGRES_PASSWORD": "pass",
        "POSTGRES_DB": "mydb"
    },
    "volumes": ["pgdata:/var/lib/postgresql/data"]
}
EOL
    )

    # Run test
    run map_plugin_config "postgres" "$service_config"
    
    # Assert
    assert_success
    assert_line "--config-opt POSTGRES_USER=user"
    assert_line "--config-opt POSTGRES_PASSWORD=pass"
    assert_line "--config-opt POSTGRES_DB=mydb"
}

Example Unit Test

#!/usr/bin/env bats

load 'test_helper'

@test "validate_compose_file with valid file" {
    # Setup
    cat > docker-compose.yml <<EOL
version: '3.8'
services:
  web:
    image: nginx:alpine
EOL
    
    # Execute
    run validate_compose_file "docker-compose.yml"
    
    # Assert
    assert_success
    assert_output --partial "Valid compose file"
}

Example Integration Test

@test "import creates dokku apps from compose file" {
    # Setup test compose file
    # ...
    
    # Execute import
    run dokku docker-compose:import docker-compose.yml
    
    # Assert apps were created
    run dokku apps:list
    assert_success
    assert_output --partial "web"
    assert_output --partial "db"
}

Continuous Integration

GitHub Actions

Example workflow (.github/workflows/test.yml):

name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      docker:
        image: docker:dind
        options: >-
          --privileged
          --network=host
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker
        run: |
          docker --version
          docker-compose --version
      
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y bats jq
          sudo pip install yq
      
      - name: Run unit tests
        run: make test-unit
      
      - name: Run integration tests
        run: make test-integration
        
      - name: Run e2e tests
        run: make test-e2e

Test Coverage

Coverage Reporting

Use kcov for code coverage:

# Install kcov
brew install kcov

# Run tests with coverage
make coverage

Coverage Reports

  • HTML reports in coverage/
  • Publish to codecov.io or similar service

Best Practices

  1. Test Isolation: Each test should be independent
  2. Descriptive Names: Use clear, descriptive test names
  3. Minimal Fixtures: Keep test data minimal and focused
  4. Test Edge Cases: Include tests for error conditions
  5. Fast Feedback: Keep tests fast for quick iteration

Debugging Tests

Run a single test file:

bats tests/unit/test_parser.bats

Run with debug output:

bats --tap tests/

Performance Testing

For large compose files, add performance benchmarks:

@test "import handles large compose files quickly" {
    # Generate large compose file
    # ...
    
    # Time the import
    run time dokku docker-compose:import large-compose.yml
    
    # Assert it completes within time limit
    assert_success
    assert_output --regexp 'real\s+0:[0-4]\d\.[0-9]s'  # Less than 5 seconds
}

Mocking

For testing without real Dokku/Docker:

  1. Create mocks in tests/mocks/
  2. Override commands in test setup:
    dokku() {
        echo "[MOCK] dokku $*" >> "$TEST_MOCK_LOG"
        case "$1" in
            apps:exists)
                return 1  # App doesn't exist
                ;;
            --version)
                echo "0.30.0"
                ;;
            *)
                echo "Unexpected dokku command: $*"
                return 1
                ;;
        esac
    }
    export -f dokku
    

Test Maintenance

  1. Update tests when adding new features
  2. Review test failures carefully
  3. Keep test data up-to-date
  4. Document test cases in pull requests