Files
crocodilestick ef6141cf68 test: Make test suite container-aware with smart skipping
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
2025-10-24 17:34:39 +02:00

461 lines
15 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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