ef6141cf68
Fixes test failures when running outside Docker container by detecting
environment and skipping container-dependent tests gracefully.
Changes:
- Add container availability detection to conftest.py
- New check_container_available() helper with 2s timeout
- New container_available session fixture
- Update cwa_api_client to skip if no container on port
- Update Docker container startup tests
- test_container_stays_running now skips gracefully
- Wrapped connection attempts in try/except
- Fix smoke tests for local/CI environments
- test_required_directories_exist: Skip if not in container
- test_cwa_db_can_be_imported: Support both container and workspace paths
- test_lock_* tests: Skip when /config/processed_books missing
- Standardize port configuration
- Change default test port from 8083 to 8085 (avoid conflicts)
- Fix port mapping: use {test_port}:8083 (container always uses 8083)
- Add CWA_TEST_PORT=8083 env var to CI workflow
- Remove CWA_PORT_OVERRIDE (not needed)
Result:
- Local dev (no container): 111 passed, 21 skipped
- CI (with container): All tests run normally
- Clean skip messages instead of cryptic connection errors
461 lines
15 KiB
Bash
Executable File
461 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
||
# Calibre-Web Automated - Interactive Test Runner
|
||
# Copyright (C) 2024-2025 Calibre-Web Automated contributors
|
||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
||
set -e
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
MAGENTA='\033[0;35m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m' # No Color
|
||
BOLD='\033[1m'
|
||
|
||
# Script directory
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
cd "$SCRIPT_DIR"
|
||
|
||
# Activate venv if it exists
|
||
if [ -d ".venv" ]; then
|
||
source .venv/bin/activate
|
||
fi
|
||
|
||
# Test configuration
|
||
# Default to 8085 to avoid conflicts with production CWA on 8083
|
||
TEST_PORT="${CWA_TEST_PORT:-8085}"
|
||
|
||
# Determine pytest command (python3 -m pytest works in more environments)
|
||
if command -v pytest &> /dev/null; then
|
||
PYTEST="pytest"
|
||
else
|
||
PYTEST="python3 -m pytest"
|
||
fi
|
||
|
||
# Check dependencies
|
||
check_dependencies() {
|
||
local missing_deps=0
|
||
|
||
# Check Docker
|
||
if ! command -v docker &> /dev/null; then
|
||
print_warning "Docker not found - integration tests will fail"
|
||
missing_deps=1
|
||
elif ! docker info &> /dev/null; then
|
||
print_warning "Docker daemon not running - integration tests will fail"
|
||
missing_deps=1
|
||
fi
|
||
|
||
# Check Python venv
|
||
if [ ! -d ".venv" ]; then
|
||
print_warning "Virtual environment not found"
|
||
echo " Run: python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt"
|
||
missing_deps=1
|
||
fi
|
||
|
||
# Check pytest
|
||
if ! command -v pytest &> /dev/null && ! python3 -c "import pytest" &> /dev/null; then
|
||
print_warning "pytest not installed"
|
||
echo " Run: pip install pytest pytest-timeout pytest-flask pytest-mock faker testcontainers"
|
||
missing_deps=1
|
||
fi
|
||
|
||
if [ $missing_deps -eq 1 ]; then
|
||
echo ""
|
||
read -p "Continue anyway? [y/N]: " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
exit 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Print header
|
||
print_header() {
|
||
clear
|
||
echo -e "${BOLD}${CYAN}"
|
||
echo "╔════════════════════════════════════════════════════════════╗"
|
||
echo "║ ║"
|
||
echo "║ Calibre-Web Automated - Test Suite Runner ║"
|
||
echo "║ ║"
|
||
echo "╚════════════════════════════════════════════════════════════╝"
|
||
echo "Setup Guide:"
|
||
echo "https://github.com/crocodilestick/Calibre-Web-Automated/wiki/Testing-Quick-Setup"
|
||
echo -e "${NC}"
|
||
}
|
||
|
||
# Print section header
|
||
print_section() {
|
||
echo -e "\n${BOLD}${BLUE}▶ $1${NC}"
|
||
}
|
||
|
||
# Print success message
|
||
print_success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
# Print error message
|
||
print_error() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
}
|
||
|
||
# Print warning message
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠${NC} $1"
|
||
}
|
||
|
||
# Print info message
|
||
print_info() {
|
||
echo -e "${CYAN}ℹ${NC} $1"
|
||
}
|
||
|
||
# Check if running inside Docker
|
||
check_environment() {
|
||
if [ -f /.dockerenv ]; then
|
||
ENVIRONMENT="docker"
|
||
print_info "Running inside Docker container (Dev Container detected)"
|
||
DEFAULT_MODE="dind"
|
||
else
|
||
ENVIRONMENT="host"
|
||
print_info "Running on host machine"
|
||
DEFAULT_MODE="bind"
|
||
fi
|
||
}
|
||
|
||
# Show main menu
|
||
show_menu() {
|
||
print_header
|
||
check_environment
|
||
check_dependencies
|
||
|
||
echo ""
|
||
echo -e "${BOLD}Select Test Mode:${NC}"
|
||
echo ""
|
||
echo -e " ${BOLD}1)${NC} Integration Tests (Bind Mount Mode)"
|
||
echo " └─ Standard mode - uses temporary directories"
|
||
echo " └─ Best for: Local development, CI/CD"
|
||
echo ""
|
||
echo -e " ${BOLD}2)${NC} Integration Tests (Docker Volume Mode)"
|
||
echo " └─ DinD compatible - uses Docker volumes"
|
||
echo " └─ Best for: Dev containers, Docker-in-Docker"
|
||
echo ""
|
||
echo -e " ${BOLD}3)${NC} Docker Startup Tests"
|
||
echo " └─ Tests container initialization and health"
|
||
echo ""
|
||
echo -e " ${BOLD}4)${NC} All Tests (Full Suite)"
|
||
echo " └─ Run everything available"
|
||
echo ""
|
||
echo -e " ${BOLD}5)${NC} Quick Test (Single Integration Test)"
|
||
echo " └─ Fast verification - runs one test"
|
||
echo ""
|
||
echo -e " ${BOLD}6)${NC} Custom Test Selection"
|
||
echo " └─ Choose specific test file or pattern"
|
||
echo ""
|
||
echo -e " ${BOLD}7)${NC} Show Test Info & Status"
|
||
echo ""
|
||
echo -e " ${BOLD}q)${NC} Quit"
|
||
echo ""
|
||
echo -ne "${BOLD}Enter your choice [1-7, q]:${NC} "
|
||
}
|
||
|
||
# Run integration tests in bind mount mode
|
||
run_integration_bind() {
|
||
print_header
|
||
print_section "Running Integration Tests (Bind Mount Mode)"
|
||
echo ""
|
||
|
||
print_info "Starting test container with bind mounts..."
|
||
print_info "This will take ~3-4 minutes"
|
||
echo ""
|
||
|
||
# Check if pytest is available
|
||
if ! command -v pytest &> /dev/null; then
|
||
print_error "pytest not found! Installing..."
|
||
pip install -q pytest pytest-timeout pytest-flask pytest-mock faker
|
||
fi
|
||
|
||
# Run tests
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
if pytest tests/integration/test_ingest_pipeline.py -v --tb=short; then
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_success "All integration tests passed!"
|
||
else
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_error "Some tests failed. Check output above."
|
||
fi
|
||
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Run integration tests in Docker volume mode
|
||
run_integration_dind() {
|
||
print_header
|
||
print_section "Running Integration Tests (Docker Volume Mode)"
|
||
echo ""
|
||
|
||
print_info "Starting test container with Docker volumes..."
|
||
print_info "This will take ~3-4 minutes"
|
||
print_warning "Note: One test (cwa_db_tracks_import) will be skipped"
|
||
echo ""
|
||
|
||
# Check if pytest is available
|
||
if ! command -v pytest &> /dev/null; then
|
||
print_error "pytest not found! Installing..."
|
||
pip install -q pytest pytest-timeout pytest-flask pytest-mock faker
|
||
fi
|
||
|
||
# Run tests with volume mode enabled
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
if USE_DOCKER_VOLUMES=true pytest tests/integration/test_ingest_pipeline.py -v --tb=short; then
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_success "All runnable tests passed! (19/20, 1 skipped)"
|
||
else
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_error "Some tests failed. Check output above."
|
||
fi
|
||
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Run Docker startup tests
|
||
run_docker_tests() {
|
||
print_header
|
||
print_section "Running Docker Startup Tests"
|
||
echo ""
|
||
|
||
print_info "Testing container initialization..."
|
||
echo ""
|
||
|
||
if [ ! -f tests/docker/test_container_startup.py ]; then
|
||
print_error "Docker tests not found at tests/docker/test_container_startup.py"
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
return
|
||
fi
|
||
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
if pytest tests/docker/ -v --tb=short; then
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_success "Docker tests passed!"
|
||
else
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
print_error "Some Docker tests failed."
|
||
fi
|
||
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Run all tests
|
||
run_all_tests() {
|
||
print_header
|
||
print_section "Running Full Test Suite"
|
||
echo ""
|
||
|
||
print_info "This will run all available tests"
|
||
print_warning "Estimated time: 5-7 minutes"
|
||
echo ""
|
||
echo -ne "Continue? [y/N]: "
|
||
read -r confirm
|
||
|
||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||
return
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
# Determine mode based on environment
|
||
if [ "$DEFAULT_MODE" = "dind" ]; then
|
||
print_info "Using Docker Volume mode (DinD environment detected)"
|
||
USE_DOCKER_VOLUMES=true $PYTEST tests/ -v --tb=short || true
|
||
else
|
||
print_info "Using Bind Mount mode"
|
||
$PYTEST tests/ -v --tb=short || true
|
||
fi
|
||
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Run quick test
|
||
run_quick_test() {
|
||
print_header
|
||
print_section "Quick Test - Single Integration Test"
|
||
echo ""
|
||
|
||
print_info "Running: test_ingest_epub_already_target_format"
|
||
print_info "This verifies basic ingest functionality (~30 seconds)"
|
||
echo ""
|
||
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
if [ "$DEFAULT_MODE" = "dind" ]; then
|
||
USE_DOCKER_VOLUMES=true $PYTEST tests/integration/test_ingest_pipeline.py::TestBookIngestInContainer::test_ingest_epub_already_target_format -v
|
||
else
|
||
$PYTEST tests/integration/test_ingest_pipeline.py::TestBookIngestInContainer::test_ingest_epub_already_target_format -v
|
||
fi
|
||
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Custom test selection
|
||
run_custom_test() {
|
||
print_header
|
||
print_section "Custom Test Selection"
|
||
echo ""
|
||
|
||
echo "Available test files:"
|
||
echo " 1) tests/integration/test_ingest_pipeline.py (20 tests)"
|
||
echo " 2) tests/docker/test_container_startup.py (9 tests)"
|
||
echo ""
|
||
echo "Or enter a custom pytest pattern (e.g., tests/integration/ -k metadata)"
|
||
echo ""
|
||
echo -ne "Enter choice [1-2 or custom pattern]: "
|
||
read -r choice
|
||
|
||
case $choice in
|
||
1)
|
||
TEST_PATH="tests/integration/test_ingest_pipeline.py"
|
||
;;
|
||
2)
|
||
TEST_PATH="tests/docker/test_container_startup.py"
|
||
;;
|
||
*)
|
||
TEST_PATH="$choice"
|
||
;;
|
||
esac
|
||
|
||
echo ""
|
||
print_info "Running: $TEST_PATH"
|
||
echo ""
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
|
||
if [ "$DEFAULT_MODE" = "dind" ]; then
|
||
USE_DOCKER_VOLUMES=true $PYTEST $TEST_PATH -v
|
||
else
|
||
$PYTEST $TEST_PATH -v
|
||
fi
|
||
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Show test information
|
||
show_info() {
|
||
print_header
|
||
print_section "Test Suite Information"
|
||
echo ""
|
||
|
||
print_info "Test Environment: $ENVIRONMENT"
|
||
print_info "Default Mode: $DEFAULT_MODE"
|
||
echo ""
|
||
|
||
echo -e "${BOLD}Available Tests:${NC}"
|
||
echo ""
|
||
|
||
# Count tests
|
||
echo "📊 Integration Tests:"
|
||
$PYTEST tests/integration/ --collect-only -q 2>/dev/null | tail -1 || echo " (pytest needed to count)"
|
||
echo ""
|
||
|
||
if [ -d tests/docker ]; then
|
||
echo "🐳 Docker Tests:"
|
||
$PYTEST tests/docker/ --collect-only -q 2>/dev/null | tail -1 || echo " (pytest needed to count)"
|
||
echo ""
|
||
fi
|
||
|
||
echo -e "${BOLD}Test Modes:${NC}"
|
||
echo ""
|
||
echo -e "• ${BOLD}Bind Mount Mode${NC} (Default on host)"
|
||
echo " └─ Uses temporary directories"
|
||
echo " └─ 20/20 integration tests pass"
|
||
echo " └─ Faster cleanup"
|
||
echo ""
|
||
echo -e "• ${BOLD}Docker Volume Mode${NC} (Default in dev containers)"
|
||
echo " └─ Uses Docker volumes via docker cp"
|
||
echo " └─ 19/20 integration tests pass (1 skipped)"
|
||
echo " └─ Required for Docker-in-Docker"
|
||
echo ""
|
||
|
||
echo -e "${BOLD}Documentation:${NC}"
|
||
echo " • tests/DOCKER_VOLUMES.md - Volume mode details"
|
||
echo " • HYBRID_DOCKER_IMPLEMENTATION.md - Implementation notes"
|
||
echo " • DIND_MODE_COMPLETE.md - Completion summary"
|
||
echo ""
|
||
|
||
read -p "Press Enter to continue..."
|
||
}
|
||
|
||
# Main loop
|
||
main() {
|
||
# Check for required dependencies
|
||
if ! command -v docker &> /dev/null; then
|
||
print_header
|
||
print_error "Docker is required but not found!"
|
||
echo ""
|
||
echo "Please install Docker and try again."
|
||
exit 1
|
||
fi
|
||
|
||
while true; do
|
||
show_menu
|
||
read -r choice
|
||
|
||
case $choice in
|
||
1)
|
||
run_integration_bind
|
||
;;
|
||
2)
|
||
run_integration_dind
|
||
;;
|
||
3)
|
||
run_docker_tests
|
||
;;
|
||
4)
|
||
run_all_tests
|
||
;;
|
||
5)
|
||
run_quick_test
|
||
;;
|
||
6)
|
||
run_custom_test
|
||
;;
|
||
7)
|
||
show_info
|
||
;;
|
||
q|Q)
|
||
print_header
|
||
print_success "Goodbye!"
|
||
echo ""
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_header
|
||
print_error "Invalid choice. Please try again."
|
||
sleep 2
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# Run main function
|
||
main
|