The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/ee/packages/federation-matrix/tests/scripts/run-integration-tests.sh

376 lines
12 KiB

#!/bin/bash
# Federation Integration Test Runner
# This script builds Rocket.Chat locally and starts the federation services,
# then waits for Rocket.Chat to be ready before running the end-to-end tests.
set -e # Exit on any error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Script moved under tests/scripts; package root is two levels up from script dir
PACKAGE_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
DOCKER_COMPOSE_FILE="$PACKAGE_ROOT/docker-compose.test.yml"
MAX_WAIT_TIME=240 # 4 minutes
CHECK_INTERVAL=5 # Check every 5 seconds
# Build configuration
# Use a temporary directory outside the repo to avoid symlink traversal issues during Meteor build
BUILD_DIR="$(mktemp -d "${FEDERATION_TEST_TMPDIR:-/tmp}/rc-federation-build-XXXXXX")"
ROCKETCHAT_ROOT="$(cd "$PACKAGE_ROOT/../../.." && pwd)" # Go up to project root
# Parse command line arguments
KEEP_RUNNING=false
INCLUDE_ELEMENT=false
USE_PREBUILT_IMAGE=false
PREBUILT_IMAGE=""
INTERRUPTED=false
NO_TEST=false
CI=false
LOGS=false
START_CONTAINERS=true
while [[ $# -gt 0 ]]; do
case $1 in
--start-containers-only)
NO_TEST=true
KEEP_RUNNING=true
shift
;;
--ci)
CI=true
KEEP_RUNNING=true
START_CONTAINERS=false
shift
;;
--logs)
LOGS=true
NO_TEST=true
START_CONTAINERS=false
shift
;;
--keep-running)
KEEP_RUNNING=true
shift
;;
--element)
INCLUDE_ELEMENT=true
shift
;;
--no-test)
NO_TEST=true
shift
;;
--image)
USE_PREBUILT_IMAGE=true
# If no IMAGE value is provided (or next token is another flag), default to latest
if [[ -z "${2:-}" || "$2" == -* ]]; then
PREBUILT_IMAGE="rocketchat/rocket.chat:latest"
shift 1
else
PREBUILT_IMAGE="$2"
shift 2
fi
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --start-containers-only Start containers and skip running tests"
echo " --ci Run tests in CI mode (keep containers running, no cleanup)"
echo " --logs Show logs of rc1 and hs1 containers"
echo " --keep-running Keep Docker containers running after tests complete"
echo " --element Include Element web client in the test environment"
echo " --no-test Start containers and skip running tests"
echo " --image [IMAGE] Use a pre-built Docker image instead of building locally"
echo " --help, -h Show this help message"
echo ""
echo "By default, builds Rocket.Chat locally and runs the 'test' profile"
echo "Use --image to test against a pre-built image (e.g., --image rocketchat/rocket.chat:latest)"
echo "If --image is provided without a value, defaults to rocketchat/rocket.chat:latest"
echo "Use --element to run all services including Element web client"
echo "Use --no-test to start containers and skip running tests"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Logging functions
log_info() {
echo -e "${BLUE} [$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_success() {
echo -e "${GREEN}✅ [$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_warning() {
echo -e "${YELLOW} [$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_error() {
echo -e "${RED}❌ [$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
docker_logs() {
echo ""
echo "ROCKET.CHAT (rc1) LOGS:"
echo "----------------------------------------"
docker compose -f "$DOCKER_COMPOSE_FILE" --profile "$COMPOSE_PROFILE" logs rc1
echo ""
echo "SYNAPSE (hs1) LOGS:"
echo "----------------------------------------"
docker compose -f "$DOCKER_COMPOSE_FILE" --profile "$COMPOSE_PROFILE" logs hs1
echo ""
echo "=========================================="
}
# Cleanup function
cleanup() {
if [ "$CI" = true ]; then
# Exit with the test result code
if [ -n "${TEST_EXIT_CODE:-}" ]; then
exit $TEST_EXIT_CODE
fi
fi
# Show container logs if tests failed
if [ -n "${TEST_EXIT_CODE:-}" ] && [ "$TEST_EXIT_CODE" -ne 0 ]; then
echo ""
echo "=========================================="
echo "CONTAINER LOGS (Test Failed)"
echo "=========================================="
docker_logs
fi
if [ "$KEEP_RUNNING" = true ]; then
log_info "Keeping Docker containers running (--keep-running flag set)"
log_info "Services are available at:"
log_info " - Rocket.Chat: https://rc1"
log_info " - Synapse: https://hs1"
log_info " - MongoDB: localhost:27017"
if [ "$INCLUDE_ELEMENT" = true ]; then
log_info " - Element: https://element"
fi
log_info "To stop containers manually, run: docker compose -f \"$DOCKER_COMPOSE_FILE\" --profile \"$COMPOSE_PROFILE\" down -v"
else
log_info "Cleaning up services..."
docker compose -f "$DOCKER_COMPOSE_FILE" --profile "$COMPOSE_PROFILE" down -v 2>/dev/null || true
log_success "Cleanup completed"
fi
# Remove temporary build directory if it exists
if [ -n "${BUILD_DIR:-}" ] && [ -d "$BUILD_DIR" ]; then
rm -rf "$BUILD_DIR" || true
fi
# Exit with the test result code
if [ -n "${TEST_EXIT_CODE:-}" ]; then
exit $TEST_EXIT_CODE
fi
}
# Setup signal handlers for cleanup
trap cleanup EXIT TERM
# Handle interrupt signal (Ctrl+C) immediately
trap 'INTERRUPTED=true; log_info "Received interrupt signal (Ctrl+C), stopping..."; cleanup; exit 130' INT
# Check if docker-compose.test.yml exists
if [ ! -f "$DOCKER_COMPOSE_FILE" ]; then
log_error "docker-compose.test.yml not found at $DOCKER_COMPOSE_FILE"
exit 1
fi
if [ "$INCLUDE_ELEMENT" = true ]; then
COMPOSE_PROFILE="element"
else
COMPOSE_PROFILE="test"
fi
if [ "$START_CONTAINERS" = true ]; then
# Build Rocket.Chat locally if not using pre-built image
if [ "$USE_PREBUILT_IMAGE" = false ]; then
log_info "🚀 Building Rocket.Chat locally..."
log_info "====================================="
# Clean up any existing build
log_info "Cleaning up previous build..."
rm -rf "$BUILD_DIR"
# Build the project
log_info "Building packages from project root..."
cd "$ROCKETCHAT_ROOT"
yarn build
# Build the Meteor bundle (must be run from the meteor directory)
log_info "Building Meteor bundle..."
cd "$ROCKETCHAT_ROOT/apps/meteor"
METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build --server-only --directory "$BUILD_DIR"
log_success "Build completed!"
else
log_info "🚀 Using pre-built image: $PREBUILT_IMAGE"
log_info "====================================="
fi
log_info "🚀 Starting Federation Integration Tests"
log_info "====================================="
BUILD_PARAM=""
# Set environment variables for Docker Compose
if [ "$USE_PREBUILT_IMAGE" = true ]; then
export ROCKETCHAT_IMAGE="$PREBUILT_IMAGE"
log_info "Using pre-built image: $PREBUILT_IMAGE"
else
export ROCKETCHAT_BUILD_CONTEXT="$BUILD_DIR"
export ROCKETCHAT_DOCKERFILE="$ROCKETCHAT_ROOT/apps/meteor/.docker/Dockerfile.alpine"
BUILD_PARAM="--build"
log_info "Building from local context: $BUILD_DIR"
fi
# Start services
if [ "$INCLUDE_ELEMENT" = true ]; then
log_info "Starting all federation services including Element web client..."
else
log_info "Starting federation services (test profile only)..."
fi
docker compose -f "$DOCKER_COMPOSE_FILE" --profile "$COMPOSE_PROFILE" up -d $BUILD_PARAM
# Wait for rc1 container to be running
log_info "Waiting for rc1 container to start..."
timeout=60
while [ $timeout -gt 0 ] && [ "$INTERRUPTED" = false ]; do
if docker compose -f "$DOCKER_COMPOSE_FILE" ps rc1 --filter "status=running" | grep -q "rc1"; then
log_success "rc1 container is running"
break
fi
sleep 2
timeout=$((timeout - 2))
done
if [ "$INTERRUPTED" = true ]; then
log_info "Container startup interrupted by user"
exit 130
fi
if [ $timeout -le 0 ]; then
log_error "rc1 container failed to start within 60 seconds"
exit 1
fi
# Wait for both Rocket.Chat and Synapse to be ready
log_info "Waiting for Rocket.Chat and Synapse servers to be ready..."
# Function to wait for a service to be ready
wait_for_service() {
local url=$1
local name=$2
local host=$3
local elapsed=0
local ca_cert="${CA_CERT:-$PACKAGE_ROOT/docker-compose/traefik/certs/ca/rootCA.crt}"
# Derive host/port from URL when not explicitly provided
local host_with_port="${url#*://}"
host_with_port="${host_with_port%%/*}"
if [ -z "$host" ]; then
host="${host_with_port%%:*}"
fi
local port
if [[ "$host_with_port" == *:* ]]; then
port="${host_with_port##*:}"
else
if [[ "$url" == https://* ]]; then
port=443
else
port=80
fi
fi
log_info "Checking $name at $url (host $host -> 127.0.0.1:$port)..."
while [ $elapsed -lt $MAX_WAIT_TIME ] && [ "$INTERRUPTED" = false ]; do
# Capture curl output and error for debugging
curl_output=$(curl -fsS --cacert "$ca_cert" --resolve "${host}:${port}:127.0.0.1" "$url" 2>&1)
curl_exit_code=$?
if [ $curl_exit_code -eq 0 ]; then
log_success "$name is ready!"
return 0
fi
log_info "$name not ready yet, waiting... (${elapsed}s/${MAX_WAIT_TIME}s)"
log_info "Curl error: $curl_output"
sleep $CHECK_INTERVAL
elapsed=$((elapsed + CHECK_INTERVAL))
done
if [ "$INTERRUPTED" = true ]; then
log_info "Service check interrupted by user"
return 1
fi
log_error "$name failed to become ready within ${MAX_WAIT_TIME} seconds"
return 1
}
# Wait for Rocket.Chat
if ! wait_for_service "https://rc1/api/info" "Rocket.Chat" "rc1"; then
log_error "Last 50 lines of rc1 logs:"
docker compose -f "$DOCKER_COMPOSE_FILE" logs --tail 50 rc1
exit 1
fi
# Wait for Synapse
if ! wait_for_service "https://hs1/_matrix/client/versions" "Synapse" "hs1"; then
log_error "Last 50 lines of hs1 logs:"
docker compose -f "$DOCKER_COMPOSE_FILE" logs --tail 50 hs1
exit 1
fi
fi
# Run the end-to-end tests
if [ "$NO_TEST" = false ]; then
log_info "Running end-to-end tests..."
cd "$PACKAGE_ROOT"
set +e
IS_EE=true NODE_EXTRA_CA_CERTS=$(pwd)/docker-compose/traefik/certs/ca/rootCA.crt yarn test:federation
TEST_EXIT_CODE=$?
set -e
elif [ "$LOGS" = true ]; then
docker_logs
exit 0
else
log_info "No-test mode: skipping test execution"
log_info "Services are ready and running. You can now:"
log_info " - Access Rocket.Chat at: https://rc1"
log_info " - Access Synapse at: https://hs1"
log_info " - Access MongoDB at: localhost:27017"
if [ "$INCLUDE_ELEMENT" = true ]; then
log_info " - Access Element at: https://element"
fi
log_info ""
log_info "To run tests manually, execute: yarn testend-to-end"
log_info "To stop containers, use: docker compose -f $DOCKER_COMPOSE_FILE down"
TEST_EXIT_CODE=0
fi