#!/usr/bin/env bash # ============================================================================== # Smart Test Runner for Laravel & CakePHP # Tự động nhận diện Framework (Laravel / CakePHP) và Môi trường thực thi: # Laravel Sail -> Docker Compose -> Local PHP CLI / bin/cake / phpunit # ============================================================================== set -e # Màu sắc thông báo GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' CYAN='\033[0;36m' NC='\033[0m' # No Color echo -e "${YELLOW}🔍 Đang phát hiện Framework và môi trường chạy test...${NC}" # Hàm chạy test run_test() { local runner="$1" shift echo -e "${GREEN}🚀 Chạy test bằng: ${runner} $*${NC}" $runner "$@" } # 1. Xác định Framework: CakePHP hay Laravel FRAMEWORK="generic" if [ -f "bin/cake" ] || ( [ -f "composer.json" ] && grep -q '"cakephp/cakephp"' composer.json ); then FRAMEWORK="cakephp" echo -e "${CYAN}✓ Phát hiện Framework: CakePHP${NC}" elif [ -f "artisan" ] || ( [ -f "composer.json" ] && grep -q '"laravel/framework"' composer.json ); then FRAMEWORK="laravel" echo -e "${CYAN}✓ Phát hiện Framework: Laravel${NC}" else echo -e "${BLUE}ℹ Framework: PHP Generic (PHPUnit)${NC}" fi # 2. Xử lý môi trường CakePHP if [ "$FRAMEWORK" = "cakephp" ]; then # A. Docker Compose if [ -f "docker-compose.yml" ] || [ -f "compose.yaml" ]; then if docker info >/dev/null 2>&1; then SERVICES=("app" "web" "php" "php-fpm" "backend" "workspace") for svc in "${SERVICES[@]}"; do if docker compose ps --services --filter "status=running" 2>/dev/null | grep -q "^${svc}$"; then echo -e "${GREEN}✓ Phát hiện Docker Compose với service: '${svc}'${NC}" if docker compose exec -T ${svc} test -f "bin/cake" 2>/dev/null; then run_test "docker compose exec -T ${svc} bin/cake test app" "$@" exit 0 else run_test "docker compose exec -T ${svc} vendor/bin/phpunit" "$@" exit 0 fi fi done fi fi # B. Local CakePHP CLI if [ -f "bin/cake" ] && command -v php >/dev/null 2>&1; then if ! php -m | grep -q -i intl; then echo -e "${YELLOW}⚠️ Cảnh báo: PHP CLI hiện tại chưa bật extension 'intl'. CakePHP yêu cầu extension này để chạy!${NC}" fi if ! php -m | grep -q -i sqlite; then echo -e "${YELLOW}⚠️ Cảnh báo: PHP CLI hiện tại chưa bật extension 'pdo_sqlite'. Test dùng In-memory SQLite có thể bị lỗi!${NC}" fi echo -e "${GREEN}✓ Sử dụng CakePHP CLI cục bộ (bin/cake)${NC}" run_test "bin/cake test app" "$@" exit 0 fi # C. Local PHPUnit fallback if [ -f "vendor/bin/phpunit" ] && command -v php >/dev/null 2>&1; then echo -e "${GREEN}✓ Sử dụng vendor/bin/phpunit cục bộ${NC}" run_test "./vendor/bin/phpunit" "$@" exit 0 fi fi # 3. Xử lý môi trường Laravel if [ "$FRAMEWORK" = "laravel" ]; then # A. Laravel Sail if [ -f "./vendor/bin/sail" ] && docker info >/dev/null 2>&1; then if docker ps --format '{{.Names}}' | grep -q "laravel.test"; then echo -e "${GREEN}✓ Phát hiện Laravel Sail đang hoạt động.${NC}" run_test "./vendor/bin/sail test" "$@" exit 0 fi fi # B. Docker Compose if [ -f "docker-compose.yml" ] || [ -f "compose.yaml" ]; then if docker info >/dev/null 2>&1; then SERVICES=("app" "php" "php-fpm" "backend" "workspace" "web") for svc in "${SERVICES[@]}"; do if docker compose ps --services --filter "status=running" 2>/dev/null | grep -q "^${svc}$"; then echo -e "${GREEN}✓ Phát hiện Docker Compose với service: '${svc}'${NC}" run_test "docker compose exec -T ${svc} php artisan test" "$@" exit 0 fi done fi fi # C. Fallback về PHP CLI cục bộ if command -v php >/dev/null 2>&1; then echo -e "${YELLOW}ℹ Không tìm thấy container Docker đang chạy. Chuyển về PHP CLI máy local.${NC}" if ! php -m | grep -q -i sqlite; then echo -e "${RED}⚠️ Cảnh báo: PHP CLI hiện tại chưa bật extension pdo_sqlite. Unit test dùng In-memory SQLite có thể bị lỗi!${NC}" fi run_test "php artisan test" "$@" exit 0 fi fi # 4. Fallback chung cho mọi dự án PHP if [ -f "vendor/bin/phpunit" ]; then echo -e "${YELLOW}ℹ Chạy qua vendor/bin/phpunit cục bộ...${NC}" run_test "./vendor/bin/phpunit" "$@" exit 0 fi echo -e "${RED}❌ Không tìm thấy môi trường phù hợp (Docker Compose, Sail, bin/cake, hoặc PHP CLI) để chạy test!${NC}" exit 1