#!/bin/bash # Test script for PISCAL Docker image # Verifies build, executable presence, and basic functionality set -e # Exit on any error IMAGE_NAME="piscal-server" IMAGE_TAG="test" TEST_CONTAINER="piscal-test-$$" TEST_DIR=$(mktemp -d) SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Cleanup function cleanup() { echo -e "\n${YELLOW}Cleaning up...${NC}" docker rm -f "$TEST_CONTAINER" 2>/dev/null || true rm -rf "$TEST_DIR" 2>/dev/null || true } trap cleanup EXIT # Test counter TESTS_PASSED=0 TESTS_FAILED=0 # Test function run_test() { local test_name="$1" echo -e "\n${YELLOW}Testing: $test_name${NC}" if eval "$2"; then echo -e "${GREEN}✓ PASS: $test_name${NC}" ((TESTS_PASSED++)) return 0 else echo -e "${RED}✗ FAIL: $test_name${NC}" ((TESTS_FAILED++)) return 1 fi } echo "==========================================" echo "PISCAL Docker Image Test Suite" echo "==========================================" # Test 1: Build verification run_test "Docker image builds successfully" " docker build -t ${IMAGE_NAME}:${IMAGE_TAG} . > /dev/null 2>&1 " # Test 2: Executable check run_test "PISCAL executable exists at /srv/piscal" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -f /srv/piscal " run_test "PISCAL executable is executable" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -x /srv/piscal " run_test "PISCAL executable can be executed (version check)" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} /srv/piscal --help > /dev/null 2>&1 || \ docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} /srv/piscal 2>&1 | head -1 > /dev/null " # Test 3: Manager scripts check run_test "piscal_manager.sh exists and is executable" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -x /srv/piscal_manager.sh " run_test "piscal_launcher.sh exists and is executable" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -x /srv/piscal_launcher.sh " run_test "piscal_launcher.cfg exists" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -f /srv/piscal_launcher.cfg " run_test "subdir_year.sh exists and is executable" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -x /srv/subdir_year.sh " # Test 4: SSH configuration run_test "SSH server is installed" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} which sshd > /dev/null " run_test "launcher user exists" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} id launcher > /dev/null " run_test "launcher user is in sudo group" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} groups launcher | grep -q sudo " run_test "/run/sshd directory exists" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} test -d /run/sshd " # Test 5: Functional test with piscal_manager.sh - test all status states run_test "piscal_manager.sh: Status 'not started' for new directory" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} bash -c ' mkdir -p /srv/test_status/input cd /srv status=\$(bash piscal_manager.sh -d test_status 2>&1) echo \"\$status\" | grep -q \"not started\" ' " run_test "piscal_manager.sh: Error handling - missing directory" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} bash -c ' cd /srv status=\$(bash piscal_manager.sh -d nonexistent_dir 2>&1) echo \"\$status\" | grep -q \"not found\" ' " run_test "piscal_manager.sh: Error handling - missing input directory" " docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} bash -c ' mkdir -p /srv/test_noinput cd /srv status=\$(bash piscal_manager.sh -d test_noinput 2>&1) echo \"\$status\" | grep -q \"input directory.*not found\" ' " run_test "piscal_manager.sh: Launch job returns 'started' status" " # Create test directory structure mkdir -p \"${TEST_DIR}/input\" cp \"${SCRIPT_DIR}/sample_data/LeafInput-valid.csv\" \"${TEST_DIR}/input/\" # Start container with test data mounted docker run -d --name \"${TEST_CONTAINER}\" \ -v \"${TEST_DIR}:/test\" \ ${IMAGE_NAME}:${IMAGE_TAG} \ tail -f /dev/null > /dev/null 2>&1 # Wait for container to be ready sleep 2 # Create working directory structure inside container (relative to /srv) docker exec \"${TEST_CONTAINER}\" bash -c ' mkdir -p /srv/test_work/input cp /test/input/*.csv /srv/test_work/input/ 2>/dev/null || true ' # Launch job using piscal_manager.sh result=\$(docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_work -p C3_photosynthesis_leafweb -s -t 2>&1 ') # Check for 'started' status echo \"\$result\" | grep -q \"started\" # Clean up test container docker rm -f \"${TEST_CONTAINER}\" > /dev/null 2>&1 || true " run_test "piscal_manager.sh: Status 'running' while job is active" " # Create test directory structure mkdir -p \"${TEST_DIR}/input\" cp \"${SCRIPT_DIR}/sample_data/LeafInput-valid.csv\" \"${TEST_DIR}/input/\" # Start container with test data mounted docker run -d --name \"${TEST_CONTAINER}\" \ -v \"${TEST_DIR}:/test\" \ ${IMAGE_NAME}:${IMAGE_TAG} \ tail -f /dev/null > /dev/null 2>&1 # Wait for container to be ready sleep 2 # Create working directory structure inside container (relative to /srv) docker exec \"${TEST_CONTAINER}\" bash -c ' mkdir -p /srv/test_running/input cp /test/input/*.csv /srv/test_running/input/ 2>/dev/null || true ' # Launch job in background docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_running -p C3_photosynthesis_leafweb -s -t > /dev/null 2>&1 ' || true # Wait a moment for job to start sleep 1 # Check status (should be 'running' or 'complete' depending on speed) status=\$(docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_running 2>&1 | head -1 ') # Status should be either 'running' or 'complete' echo \"\$status\" | grep -qE \"^(running|complete)\$\" # Clean up test container docker rm -f \"${TEST_CONTAINER}\" > /dev/null 2>&1 || true " run_test "piscal_manager.sh: Cannot launch if already running" " # Create test directory structure mkdir -p \"${TEST_DIR}/input\" cp \"${SCRIPT_DIR}/sample_data/LeafInput-valid.csv\" \"${TEST_DIR}/input/\" # Start container with test data mounted docker run -d --name \"${TEST_CONTAINER}\" \ -v \"${TEST_DIR}:/test\" \ ${IMAGE_NAME}:${IMAGE_TAG} \ tail -f /dev/null > /dev/null 2>&1 # Wait for container to be ready sleep 2 # Create working directory structure inside container (relative to /srv) docker exec \"${TEST_CONTAINER}\" bash -c ' mkdir -p /srv/test_already/input cp /test/input/*.csv /srv/test_already/input/ 2>/dev/null || true ' # Create a fake running process by creating PID file with current shell PID docker exec \"${TEST_CONTAINER}\" bash -c ' echo \$\$ > /srv/test_already/piscal.pid ' # Try to launch - should fail with 'still running' result=\$(docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_already -p C3_photosynthesis_leafweb -s -t 2>&1 ' || echo \"still running\") # Check for 'still running' status echo \"\$result\" | grep -q \"still running\" # Clean up test container docker rm -f \"${TEST_CONTAINER}\" > /dev/null 2>&1 || true " run_test "piscal_manager.sh: Status 'complete' with output verification" " # Create test directory structure mkdir -p \"${TEST_DIR}/input\" cp \"${SCRIPT_DIR}/sample_data/LeafInput-valid.csv\" \"${TEST_DIR}/input/\" # Start container with test data mounted docker run -d --name \"${TEST_CONTAINER}\" \ -v \"${TEST_DIR}:/test\" \ ${IMAGE_NAME}:${IMAGE_TAG} \ tail -f /dev/null > /dev/null 2>&1 # Wait for container to be ready sleep 2 # Create working directory structure inside container (relative to /srv) docker exec \"${TEST_CONTAINER}\" bash -c ' mkdir -p /srv/test_complete/input cp /test/input/*.csv /srv/test_complete/input/ 2>/dev/null || true ' # Launch job using piscal_manager.sh docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_complete -p C3_photosynthesis_leafweb -s -t > /dev/null 2>&1 ' || true # Wait for job to complete (with timeout) - use a script inside container docker exec \"${TEST_CONTAINER}\" bash -c ' max_wait=120 wait_time=0 status=\"\" while [ \$wait_time -lt \$max_wait ]; do cd /srv status=\$(bash piscal_manager.sh -d test_complete 2>&1 | head -1) if echo \"\$status\" | grep -q \"complete\"; then break fi if echo \"\$status\" | grep -q \"not started\"; then sleep 2 wait_time=\$((wait_time + 2)) continue fi if echo \"\$status\" | grep -q \"running\"; then sleep 5 wait_time=\$((wait_time + 5)) continue fi break done # Verify status is complete echo \"\$status\" | grep -q \"complete\" ' # Get full status output to verify output structure full_output=\$(docker exec \"${TEST_CONTAINER}\" bash -c ' cd /srv bash piscal_manager.sh -d test_complete 2>&1 ') # Verify output contains the expected markers echo \"\$full_output\" | grep -q \"#touser\" echo \"\$full_output\" | grep -q \"#clninput\" echo \"\$full_output\" | grep -q \"#nottouser\" # Verify output directories exist docker exec \"${TEST_CONTAINER}\" test -d /srv/test_complete/output/fitresult/touser docker exec \"${TEST_CONTAINER}\" test -d /srv/test_complete/output/clninput docker exec \"${TEST_CONTAINER}\" test -d /srv/test_complete/output/fitresult/nottouser # Verify output directories contain files (at least one file should exist) touser_files=\$(docker exec \"${TEST_CONTAINER}\" bash -c 'find /srv/test_complete/output/fitresult/touser -type f 2>/dev/null | wc -l') [ \"\$touser_files\" -gt 0 ] # Clean up test container docker rm -f \"${TEST_CONTAINER}\" > /dev/null 2>&1 || true " # Summary echo "" echo "==========================================" echo "Test Summary" echo "==========================================" echo -e "${GREEN}Passed: ${TESTS_PASSED}${NC}" if [ $TESTS_FAILED -gt 0 ]; then echo -e "${RED}Failed: ${TESTS_FAILED}${NC}" exit 1 else echo -e "${GREEN}Failed: ${TESTS_FAILED}${NC}" echo -e "\n${GREEN}All tests passed!${NC}" exit 0 fi