test-neo4j-integration.sh raw

   1  #!/bin/bash
   2  # Neo4j Integration Test Runner
   3  #
   4  # This script runs the Neo4j integration tests by:
   5  # 1. Checking if Docker/Docker Compose are available
   6  # 2. Starting a Neo4j container
   7  # 3. Running the integration tests
   8  # 4. Stopping the container
   9  #
  10  # Usage:
  11  #   ./scripts/test-neo4j-integration.sh
  12  #
  13  # Environment variables:
  14  #   SKIP_DOCKER_INSTALL=1  - Skip Docker installation check
  15  #   KEEP_CONTAINER=1       - Don't stop container after tests
  16  #   NEO4J_TEST_REQUIRED=1  - Fail if Docker/Neo4j not available (for local testing)
  17  #
  18  # Exit codes:
  19  #   0 - Tests passed OR Docker/Neo4j not available (soft fail for CI)
  20  #   1 - Tests failed (only when Neo4j is available)
  21  #   2 - Tests required but Docker/Neo4j not available (when NEO4J_TEST_REQUIRED=1)
  22  
  23  set -e
  24  
  25  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  26  PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
  27  COMPOSE_FILE="$PROJECT_ROOT/pkg/neo4j/docker-compose.yaml"
  28  CONTAINER_NAME="neo4j-test"
  29  
  30  # Colors for output
  31  RED='\033[0;31m'
  32  GREEN='\033[0;32m'
  33  YELLOW='\033[1;33m'
  34  BLUE='\033[0;34m'
  35  NC='\033[0m' # No Color
  36  
  37  log_info() {
  38      echo -e "${GREEN}[INFO]${NC} $1"
  39  }
  40  
  41  log_warn() {
  42      echo -e "${YELLOW}[WARN]${NC} $1"
  43  }
  44  
  45  log_error() {
  46      echo -e "${RED}[ERROR]${NC} $1"
  47  }
  48  
  49  log_skip() {
  50      echo -e "${BLUE}[SKIP]${NC} $1"
  51  }
  52  
  53  # Soft fail - exit 0 for CI compatibility unless NEO4J_TEST_REQUIRED is set
  54  soft_fail() {
  55      local message="$1"
  56      if [ "$NEO4J_TEST_REQUIRED" = "1" ]; then
  57          log_error "$message"
  58          log_error "NEO4J_TEST_REQUIRED=1 is set, failing"
  59          exit 2
  60      else
  61          log_skip "$message"
  62          log_skip "Neo4j integration tests skipped (set NEO4J_TEST_REQUIRED=1 to require)"
  63          exit 0
  64      fi
  65  }
  66  
  67  # Check if Docker is installed and running
  68  check_docker() {
  69      if ! command -v docker &> /dev/null; then
  70          soft_fail "Docker is not installed"
  71          return 1
  72      fi
  73  
  74      if ! docker info &> /dev/null 2>&1; then
  75          soft_fail "Docker daemon is not running or permission denied"
  76          return 1
  77      fi
  78  
  79      log_info "Docker is available"
  80      return 0
  81  }
  82  
  83  # Check if Docker Compose is installed
  84  check_docker_compose() {
  85      # Try docker compose (v2) first, then docker-compose (v1)
  86      if docker compose version &> /dev/null 2>&1; then
  87          COMPOSE_CMD="docker compose"
  88          log_info "Using Docker Compose v2"
  89          return 0
  90      elif command -v docker-compose &> /dev/null; then
  91          COMPOSE_CMD="docker-compose"
  92          log_info "Using Docker Compose v1"
  93          return 0
  94      else
  95          soft_fail "Docker Compose is not installed"
  96          return 1
  97      fi
  98  }
  99  
 100  # Start Neo4j container
 101  start_neo4j() {
 102      log_info "Starting Neo4j container..."
 103  
 104      cd "$PROJECT_ROOT"
 105  
 106      # Try to start container, soft fail if it doesn't work
 107      if ! $COMPOSE_CMD -f "$COMPOSE_FILE" up -d 2>&1; then
 108          soft_fail "Failed to start Neo4j container"
 109          return 1
 110      fi
 111  
 112      log_info "Waiting for Neo4j to become healthy..."
 113  
 114      # Wait for container to be healthy (up to 2 minutes)
 115      local timeout=120
 116      local elapsed=0
 117  
 118      while [ $elapsed -lt $timeout ]; do
 119          local health=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "not_found")
 120  
 121          if [ "$health" = "healthy" ]; then
 122              log_info "Neo4j is healthy and ready"
 123              return 0
 124          elif [ "$health" = "not_found" ]; then
 125              log_warn "Container $CONTAINER_NAME not found, retrying..."
 126          fi
 127  
 128          echo -n "."
 129          sleep 2
 130          elapsed=$((elapsed + 2))
 131      done
 132  
 133      echo ""
 134      log_warn "Neo4j failed to become healthy within $timeout seconds"
 135      log_info "Container logs:"
 136      docker logs "$CONTAINER_NAME" --tail 20 2>/dev/null || true
 137  
 138      # Clean up failed container
 139      $COMPOSE_CMD -f "$COMPOSE_FILE" down -v 2>/dev/null || true
 140  
 141      soft_fail "Neo4j container failed to start properly"
 142      return 1
 143  }
 144  
 145  # Stop Neo4j container
 146  stop_neo4j() {
 147      if [ "$KEEP_CONTAINER" = "1" ]; then
 148          log_info "KEEP_CONTAINER=1, leaving Neo4j running"
 149          return 0
 150      fi
 151  
 152      log_info "Stopping Neo4j container..."
 153      cd "$PROJECT_ROOT"
 154      $COMPOSE_CMD -f "$COMPOSE_FILE" down -v 2>/dev/null || true
 155  }
 156  
 157  # Run integration tests
 158  run_tests() {
 159      log_info "Running Neo4j integration tests..."
 160  
 161      cd "$PROJECT_ROOT"
 162  
 163      # Set environment variables for tests
 164      # Note: Tests use ORLY_NEO4J_* prefix (consistent with app config)
 165      export ORLY_NEO4J_URI="bolt://localhost:7687"
 166      export ORLY_NEO4J_USER="neo4j"
 167      export ORLY_NEO4J_PASSWORD="testpassword"
 168      # Also set NEO4J_TEST_URI for testmain_test.go compatibility
 169      export NEO4J_TEST_URI="bolt://localhost:7687"
 170  
 171      # Run tests with integration tag
 172      if go test -tags=integration ./pkg/neo4j/... -v -timeout 5m; then
 173          log_info "All integration tests passed!"
 174          return 0
 175      else
 176          log_error "Some integration tests failed"
 177          return 1
 178      fi
 179  }
 180  
 181  # Main execution
 182  main() {
 183      log_info "Neo4j Integration Test Runner"
 184      log_info "=============================="
 185  
 186      if [ "$NEO4J_TEST_REQUIRED" = "1" ]; then
 187          log_info "NEO4J_TEST_REQUIRED=1 - tests will fail if Neo4j unavailable"
 188      else
 189          log_info "NEO4J_TEST_REQUIRED not set - tests will skip if Neo4j unavailable"
 190      fi
 191  
 192      # Check prerequisites (these will soft_fail if not available)
 193      check_docker || exit $?
 194      check_docker_compose || exit $?
 195  
 196      # Check if compose file exists
 197      if [ ! -f "$COMPOSE_FILE" ]; then
 198          soft_fail "Docker Compose file not found: $COMPOSE_FILE"
 199      fi
 200  
 201      # Track if we need to stop the container
 202      local need_cleanup=0
 203  
 204      # Check if container is already running
 205      if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"; then
 206          log_info "Neo4j container is already running"
 207      else
 208          start_neo4j || exit $?
 209          need_cleanup=1
 210      fi
 211  
 212      # Run tests
 213      local test_result=0
 214      run_tests || test_result=1
 215  
 216      # Cleanup
 217      if [ $need_cleanup -eq 1 ]; then
 218          stop_neo4j
 219      fi
 220  
 221      if [ $test_result -eq 0 ]; then
 222          log_info "Integration tests completed successfully"
 223      else
 224          log_error "Integration tests failed"
 225      fi
 226  
 227      exit $test_result
 228  }
 229  
 230  # Handle cleanup on script exit
 231  cleanup() {
 232      if [ "$KEEP_CONTAINER" != "1" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"; then
 233          log_warn "Cleaning up after interrupt..."
 234          stop_neo4j
 235      fi
 236  }
 237  
 238  trap cleanup EXIT INT TERM
 239  
 240  main "$@"
 241