diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bf7870e..44940d1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,8 +18,8 @@ jobs: run: | find . -name "*.sh" -not -path "./vendor/*" -exec shellcheck {} + - unit-tests: - name: Unit Tests + php-tests: + name: PHP Tests runs-on: ubuntu-latest needs: shellcheck @@ -42,12 +42,27 @@ jobs: run: | chmod +x php/*.sh chmod +x php/laravel/*.sh 2>/dev/null || true - chmod +x shell/*.sh 2>/dev/null || true - chmod +x docker/*.sh 2>/dev/null || true - chmod +x simple/*.sh 2>/dev/null || true - chmod +x tests/run_all.sh chmod +x tests/lib/*.bash - find tests -name "*.sh" -exec chmod +x {} + + find tests/php -name "*.sh" -exec chmod +x {} + - - name: Run all tests - run: ./tests/run_all.sh + - name: Run PHP tests + run: ./tests/php/phpstan/run.sh + + python-tests: + name: Python Tests + runs-on: ubuntu-latest + needs: shellcheck + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Prepare test environment + run: | + chmod +x python/*.sh + chmod +x tests/lib/*.bash + find tests/python -name "*.sh" -exec chmod +x {} + + + - name: Run Python tests + run: | + for suite in tests/python/*/run.sh; do "$suite"; done diff --git a/python/check_flake8.sh b/python/check_flake8.sh new file mode 100755 index 0000000..7d63bd8 --- /dev/null +++ b/python/check_flake8.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# ---------------------------------------- +# Python Code Style Checker +# +# This script checks Python files for style +# issues using flake8. It runs directly on +# the host (no Docker required). +# +# Usage: +# ./check_style.sh file1.py file2.py ... +# ---------------------------------------- + +if [ $# -eq 0 ]; then + echo "No files to check" + exit 0 +fi + +PY_FILES=() +CHECKED_FILES=0 +HAS_ERRORS=0 + +for file in "$@"; do + # Skip non-Python files + if [[ ! "$file" =~ \.py$ ]]; then + continue + fi + + # Skip if file doesn't exist + if [ ! -f "$file" ]; then + continue + fi + + PY_FILES+=("$file") +done + +# Check if there are any Python files to check +if [ ${#PY_FILES[@]} -eq 0 ]; then + echo "No Python files to check" + exit 0 +fi + +# Run flake8 linter on each file +for file in "${PY_FILES[@]}"; do + CHECKED_FILES=$((CHECKED_FILES + 1)) + + OUTPUT=$(flake8 --max-line-length=120 "$file" 2>&1) + EXIT_CODE=$? + + if [ $EXIT_CODE -ne 0 ]; then + HAS_ERRORS=1 + echo "Style errors in: $file" + echo "$OUTPUT" + echo "" + fi +done + +# Final result +if [ $HAS_ERRORS -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Code style check failed!" + echo "Total files checked: $CHECKED_FILES" + echo "Fix the errors above before committing." + exit 1 +fi + +echo "Code style check passed! ($CHECKED_FILES files checked)" +exit 0 diff --git a/python/check_flake8_in_docker.sh b/python/check_flake8_in_docker.sh new file mode 100755 index 0000000..b54a232 --- /dev/null +++ b/python/check_flake8_in_docker.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# ---------------------------------------- +# Python Code Style Checker +# +# This script checks Python files for style +# issues using flake8. It runs directly on +# the host (no Docker required). +# +# Usage: +# ./check_style.sh file1.py file2.py ... +# ---------------------------------------- + +# ----------------------------- +# CONFIG +# ----------------------------- +CONTAINER_NAME="app_dev" + +# Path mapping: host path prefix -> container path prefix +HOST_APP_PATH="app/" +CONTAINER_APP_PATH="/app/" + +# ----------------------------- +# FUNCTIONS +# ----------------------------- +check_container_running() { + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "ERROR: Container '$CONTAINER_NAME' is not running" + echo "Start it with: docker-compose -f docker/dev/docker-compose.yml up -d" + exit 1 + fi +} + +# Convert host path to container path +to_container_path() { + local file="$1" + # Replace app/ with /app/ + echo "${file/#${HOST_APP_PATH}/${CONTAINER_APP_PATH}}" +} + +# ----------------------------- +# MAIN +# ----------------------------- +if [ $# -eq 0 ]; then + echo "No files to check" + exit 0 +fi + +check_container_running + +PY_FILES=() +CHECKED_FILES=0 +HAS_ERRORS=0 + +# Collect only .py files from app/ directory +for file in "$@"; do + # Skip non-Python files + if [[ ! "$file" =~ \.py$ ]]; then + continue + fi + + # Skip files not in app/ directory (not mounted in container) + if [[ ! "$file" =~ ^app/ ]]; then + continue + fi + + # Skip if file doesn't exist + if [ ! -f "$file" ]; then + continue + fi + + PY_FILES+=("$file") +done + +# Check if there are any Python files to check +if [ ${#PY_FILES[@]} -eq 0 ]; then + echo "No Python files to check" + exit 0 +fi + +# Run linter on each file via docker exec +for file in "${PY_FILES[@]}"; do + CHECKED_FILES=$((CHECKED_FILES + 1)) + + container_path=$(to_container_path "$file") + + # Run linter inside container + OUTPUT=$(docker exec "$CONTAINER_NAME" flake8 --max-line-length=120 "$container_path" 2>&1) + EXIT_CODE=$? + + if [ $EXIT_CODE -ne 0 ]; then + HAS_ERRORS=1 + echo "Style errors in: $file" + # Convert container paths back to host paths in output + echo "${OUTPUT//${CONTAINER_APP_PATH}/${HOST_APP_PATH}}" + echo "" + fi +done + +# Final result +if [ $HAS_ERRORS -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Code style check failed!" + echo "Total files checked: $CHECKED_FILES" + echo "Fix the errors above before committing." + exit 1 +fi + +echo "Code style check passed! ($CHECKED_FILES files checked)" +exit 0 diff --git a/python/check_mypy.sh b/python/check_mypy.sh new file mode 100755 index 0000000..2bc9645 --- /dev/null +++ b/python/check_mypy.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# ------------------------------------------------------------ +# Runs mypy static analysis for changed Python files locally. +# Accepts file paths as args and checks only app/*.py files. +# ------------------------------------------------------------ + +if [ $# -eq 0 ]; then + echo "No files to check" + exit 0 +fi + +PY_FILES=() + +for file in "$@"; do + # Skip non-Python files + if [[ ! "$file" =~ \.py$ ]]; then + continue + fi + + # Only files inside app/ + if [[ ! "$file" =~ ^app/ ]]; then + continue + fi + + # Skip if file doesn't exist + if [ ! -f "$file" ]; then + continue + fi + + PY_FILES+=("$file") +done + +if [ ${#PY_FILES[@]} -eq 0 ]; then + echo "No Python files to check" + exit 0 +fi + +OUTPUT=$(mypy \ + --pretty \ + --show-error-codes \ + --ignore-missing-imports \ + --follow-imports=skip \ + "${PY_FILES[@]}" 2>&1) + +EXIT_CODE=$? + +if [ $EXIT_CODE -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Static analysis failed!" + echo "----------------------------------------" + echo "$OUTPUT" + echo "----------------------------------------" + echo "Total files checked: ${#PY_FILES[@]}" + exit 1 +fi + +echo "Static analysis passed! (${#PY_FILES[@]} files checked)" +exit 0 diff --git a/python/check_mypy_in_docker.sh b/python/check_mypy_in_docker.sh new file mode 100755 index 0000000..1fba1cf --- /dev/null +++ b/python/check_mypy_in_docker.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# ------------------------------------------------------------ +# Runs mypy static analysis for changed Python files inside +# the running Docker container. Accepts file paths as args, +# filters only app/*.py, maps them to container paths, and +# executes mypy via docker exec. +# ------------------------------------------------------------ + +CONTAINER_NAME="app_dev" +HOST_APP_PATH="app/" +CONTAINER_APP_PATH="/app/" + +check_container_running() { + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "ERROR: Container '${CONTAINER_NAME}' is not running" + echo "Start it with: docker-compose -f docker/dev/docker-compose.yml up -d" + exit 1 + fi +} + +to_container_path() { + local file="$1" + echo "${file/#${HOST_APP_PATH}/${CONTAINER_APP_PATH}}" +} + +if [ $# -eq 0 ]; then + echo "No files to check" + exit 0 +fi + +check_container_running + +PY_FILES=() + +for file in "$@"; do + # Skip non-Python files + if [[ ! "$file" =~ \.py$ ]]; then + continue + fi + + # Skip files not in app/ directory (not mounted in container) + if [[ ! "$file" =~ ^app/ ]]; then + continue + fi + + # Skip if file doesn't exist + if [ ! -f "$file" ]; then + continue + fi + + PY_FILES+=("$file") +done + +if [ ${#PY_FILES[@]} -eq 0 ]; then + echo "No Python files to check" + exit 0 +fi + +CONTAINER_FILES=() +for file in "${PY_FILES[@]}"; do + CONTAINER_FILES+=("$(to_container_path "$file")") +done + +OUTPUT=$(docker exec "$CONTAINER_NAME" mypy \ + --pretty \ + --show-error-codes \ + --ignore-missing-imports \ + --follow-imports=skip \ + "${CONTAINER_FILES[@]}" 2>&1) + +EXIT_CODE=$? + +if [ $EXIT_CODE -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Static analysis failed!" + echo "----------------------------------------" + echo "${OUTPUT//${CONTAINER_APP_PATH}/${HOST_APP_PATH}}" + echo "----------------------------------------" + echo "Total files checked: ${#PY_FILES[@]}" + exit 1 +fi + +echo "Static analysis passed! (${#PY_FILES[@]} files checked)" +exit 0 diff --git a/python/check_pytest.sh b/python/check_pytest.sh new file mode 100755 index 0000000..5e0e65a --- /dev/null +++ b/python/check_pytest.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# ---------------------------------------- +# Run pytest locally (without Docker) +# +# This script: +# • executes the full pytest test suite locally +# • sets PYTHONPATH=./app so app modules resolve correctly +# • returns proper exit codes (0/1) for CI/CD pipelines +# +# Usage: +# ./run_tests.sh +# ---------------------------------------- + +# ----------------------------- +# CONFIG +# ----------------------------- +PYTHONPATH="./app" +TESTS_PATH="tests/" + +# ----------------------------- +# MAIN +# ----------------------------- +echo "Running full test suite (pytest)..." + +# Run tests locally with PYTHONPATH set +OUTPUT=$(PYTHONPATH="$PYTHONPATH" pytest -q "$TESTS_PATH" 2>&1) +EXIT_CODE=$? + +if [ $EXIT_CODE -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Tests failed!" + echo "----------------------------------------" + echo "$OUTPUT" + echo "----------------------------------------" + exit 1 +fi + +echo "All tests passed!" +exit 0 diff --git a/python/check_pytest_in_docker.sh b/python/check_pytest_in_docker.sh new file mode 100755 index 0000000..07ada9a --- /dev/null +++ b/python/check_pytest_in_docker.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# ---------------------------------------- +# Run pytest inside Docker container +# +# This script: +# • checks that the development container is running +# • executes the full pytest test suite inside the container +# • sets PYTHONPATH=/app so app modules resolve correctly +# • converts container paths (/app, /tests) to local paths +# (app/, tests/) for readable error output +# • returns proper exit codes (0/1) for CI/CD pipelines +# +# Usage: +# ./run_tests.sh +# ---------------------------------------- + +# ----------------------------- +# CONFIG +# ----------------------------- +CONTAINER_NAME="app_dev" + +# Path mapping: container path prefix -> host path prefix (for readable output) +CONTAINER_APP_PATH="/app/" +HOST_APP_PATH="app/" +CONTAINER_TESTS_PATH="/tests/" +HOST_TESTS_PATH="tests/" + +# ----------------------------- +# FUNCTIONS +# ----------------------------- +check_container_running() { + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "ERROR: Container '${CONTAINER_NAME}' is not running" + echo "Start it with: docker-compose -f docker/dev/docker-compose.yml up -d" + exit 1 + fi +} + +# ----------------------------- +# MAIN +# ----------------------------- +check_container_running + +echo "Running full test suite (pytest)..." + +# Run tests inside container. PYTHONPATH=/app so "routes" and "database" resolve to app code. +OUTPUT=$(docker exec -e PYTHONPATH=/app "$CONTAINER_NAME" pytest -q "$CONTAINER_TESTS_PATH" 2>&1) +EXIT_CODE=$? + +if [ $EXIT_CODE -ne 0 ]; then + echo "----------------------------------------" + echo "ERROR: Tests failed!" + echo "----------------------------------------" + # Convert container paths back to host paths in output + echo "$OUTPUT" | sed \ + -e "s|${CONTAINER_APP_PATH}|${HOST_APP_PATH}|g" \ + -e "s|${CONTAINER_TESTS_PATH}|${HOST_TESTS_PATH}|g" + echo "----------------------------------------" + exit 1 +fi + +echo "All tests passed!" +exit 0 diff --git a/python/find_test.sh b/python/find_test.sh new file mode 100755 index 0000000..e569b95 --- /dev/null +++ b/python/find_test.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# ------------------------------------------------------------------ +# Checks test files for services in app/services. +# Rules: +# - Only checks files in app/services/**/*.py +# - Each service must have exactly one test file +# - Test file mirrors the path relative to app/: +# app/services/.../x.py -> tests/services/.../test_x.py +# Usage: +# ./check_service_tests.sh ... +# ------------------------------------------------------------------ + +get_test_path() { + local file="$1" + + # Skip files already under tests/ + if [[ "$file" == tests/* ]]; then + echo "" + return + fi + + # Ensure it's a service file + if [[ ! "$file" =~ ^app/services/.*\.py$ ]]; then + echo "" + return + fi + + # Skip __init__.py + local filename + filename=$(basename "$file") + if [[ "$filename" == "__init__.py" ]]; then + echo "" + return + fi + + local relative="${file#app/}" + local dir + dir=$(dirname "$relative") + local test_filename="test_${filename}" + local test_path="tests/${dir}/${test_filename}" + + echo "$test_path" +} + +if [ $# -eq 0 ]; then + echo "No files to check" + exit 0 +fi + +MISSING_TESTS=() +DUPLICATE_TESTS=() +CHECKED_FILES=0 + +for file in "$@"; do + # Skip non-Python files + if [[ ! "$file" =~ \.py$ ]]; then + continue + fi + + test_path=$(get_test_path "$file") + + if [ -z "$test_path" ]; then + continue + fi + + CHECKED_FILES=$((CHECKED_FILES + 1)) + + # Check that the expected test exists + if [ ! -f "$test_path" ]; then + MISSING_TESTS+=("$file -> $test_path") + fi + + # Check duplicates: by "1 service = 1 test file" rule only test_.py is allowed + # Any other test_* .py in the same dir is considered a duplicate. + service_filename=$(basename "$file") + service_stem="${service_filename%.py}" + test_dir=$(dirname "$test_path") + expected_test="$test_path" + + shopt -s nullglob + candidates=("${test_dir}/test_${service_stem}"*.py) + shopt -u nullglob + + duplicates=() + for candidate in "${candidates[@]}"; do + if [ "$candidate" != "$expected_test" ]; then + duplicates+=("$candidate") + fi + done + + if [ ${#duplicates[@]} -gt 0 ]; then + DUPLICATE_TESTS+=("$file -> extra tests found: ${duplicates[*]}") + fi +done + +# Report results +if [ ${#MISSING_TESTS[@]} -gt 0 ]; then + echo "ERROR: Missing tests for the following services:" + for missing in "${MISSING_TESTS[@]}"; do + echo " - $missing" + done + echo "Total files checked: $CHECKED_FILES" + echo "Missing tests: ${#MISSING_TESTS[@]}" + exit 1 +fi + +if [ ${#DUPLICATE_TESTS[@]} -gt 0 ]; then + echo "ERROR: '1 service = 1 test file' rule violated (duplicates found):" + for dup in "${DUPLICATE_TESTS[@]}"; do + echo " - $dup" + done + echo "Total files checked: $CHECKED_FILES" + echo "Duplicate groups: ${#DUPLICATE_TESTS[@]}" + exit 1 +fi + +if [ $CHECKED_FILES -eq 0 ]; then + echo "No service files to check (skipping)." +else + echo "All checks passed! ($CHECKED_FILES files checked)" +fi + +exit 0 diff --git a/scripts/check_shellcheck.sh b/scripts/check_shellcheck.sh index f617d96..8f4ca41 100644 --- a/scripts/check_shellcheck.sh +++ b/scripts/check_shellcheck.sh @@ -1,7 +1,7 @@ #!/bin/bash # ------------------------------------------------------------------------------ # Runs ShellCheck locally on shell scripts provided as arguments. -# Reports warnings and errors using ShellCheck severity level "warning". +# Reports warnings and errors using ShellCheck. # Fails if any script contains issues. # ------------------------------------------------------------------------------ @@ -30,9 +30,9 @@ check_file() { echo "Checking $FILE..." if [[ -n "$EXCLUDE_STRING" ]]; then - shellcheck --severity=warning --exclude="$EXCLUDE_STRING" "$FILE" || ERROR_FOUND=1 + shellcheck --exclude="$EXCLUDE_STRING" "$FILE" || ERROR_FOUND=1 else - shellcheck --severity=warning "$FILE" || ERROR_FOUND=1 + shellcheck "$FILE" || ERROR_FOUND=1 fi } diff --git a/tests/lib/test_helper.bash b/tests/lib/test_helper.bash index 186cd52..0809022 100755 --- a/tests/lib/test_helper.bash +++ b/tests/lib/test_helper.bash @@ -120,6 +120,91 @@ EOF chmod +x "$TEST_DIR/vendor/bin/pint" } +mock_flake8() { + local exit_code="$1" + local output="${2:-}" + + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/flake8" << EOF +#!/bin/bash +echo "$output" +exit $exit_code +EOF + chmod +x "$TEST_DIR/bin/flake8" + export PATH="$TEST_DIR/bin:$PATH" +} + +mock_mypy() { + local exit_code="$1" + local output="${2:-}" + + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/mypy" << EOF +#!/bin/bash +echo "$output" +exit $exit_code +EOF + chmod +x "$TEST_DIR/bin/mypy" + export PATH="$TEST_DIR/bin:$PATH" +} + +mock_pytest_cmd() { + local exit_code="$1" + local output="${2:-}" + + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/pytest" << EOF +#!/bin/bash +echo "$output" +exit $exit_code +EOF + chmod +x "$TEST_DIR/bin/pytest" + export PATH="$TEST_DIR/bin:$PATH" +} + +mock_docker_running() { + local exec_exit_code="${1:-0}" + local exec_output="${2:-}" + + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/docker" << EOF +#!/bin/bash +if [[ "\$1" == "ps" ]]; then + echo "app_dev" +elif [[ "\$1" == "exec" ]]; then + echo "$exec_output" + exit $exec_exit_code +fi +exit 0 +EOF + chmod +x "$TEST_DIR/bin/docker" + export PATH="$TEST_DIR/bin:$PATH" +} + +mock_docker_stopped() { + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/docker" << 'EOF' +#!/bin/bash +if [[ "$1" == "ps" ]]; then + echo "" +fi +exit 0 +EOF + chmod +x "$TEST_DIR/bin/docker" + export PATH="$TEST_DIR/bin:$PATH" +} + +create_python_fixture() { + local path="$1" + local class_name="${2:-MyClass}" + + mkdir -p "$(dirname "$path")" + cat > "$path" << EOF +class $class_name: + pass +EOF +} + mock_git_branch() { local branch_name="$1" diff --git a/tests/python/find_test/cases/01_no_files_exits_zero.sh b/tests/python/find_test/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..9193b18 --- /dev/null +++ b/tests/python/find_test/cases/01_no_files_exits_zero.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided exits with 0 +# Expected: exit 0, message about no files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_no_files" + +run_hook "python/find_test.sh" + +assert_exit_code 0 +assert_output_contains "No files to check" + +test_passed diff --git a/tests/python/find_test/cases/02_non_service_file_skipped.sh b/tests/python/find_test/cases/02_non_service_file_skipped.sh new file mode 100755 index 0000000..b9a2603 --- /dev/null +++ b/tests/python/find_test/cases/02_non_service_file_skipped.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Non-service file is skipped +# Expected: exit 0, message about no service files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_non_service" + +create_python_fixture "app/models/user.py" "User" + +run_hook "python/find_test.sh" "app/models/user.py" + +assert_exit_code 0 +assert_output_contains "No service files to check" + +test_passed diff --git a/tests/python/find_test/cases/03_service_with_test_passes.sh b/tests/python/find_test/cases/03_service_with_test_passes.sh new file mode 100755 index 0000000..5114dd6 --- /dev/null +++ b/tests/python/find_test/cases/03_service_with_test_passes.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Service file with matching test passes +# Expected: exit 0, message about all checks passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_service_with_test" + +create_python_fixture "app/services/user_service.py" "UserService" +create_python_fixture "tests/services/test_user_service.py" "TestUserService" + +run_hook "python/find_test.sh" "app/services/user_service.py" + +assert_exit_code 0 +assert_output_contains "All checks passed" + +test_passed diff --git a/tests/python/find_test/cases/04_missing_test_fails.sh b/tests/python/find_test/cases/04_missing_test_fails.sh new file mode 100755 index 0000000..a9b511b --- /dev/null +++ b/tests/python/find_test/cases/04_missing_test_fails.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Service file without matching test fails +# Expected: exit 1, message about missing tests +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_missing_test" + +create_python_fixture "app/services/payment_service.py" "PaymentService" + +run_hook "python/find_test.sh" "app/services/payment_service.py" + +assert_exit_code 1 +assert_output_contains "Missing tests" + +test_passed diff --git a/tests/python/find_test/cases/05_duplicate_test_fails.sh b/tests/python/find_test/cases/05_duplicate_test_fails.sh new file mode 100755 index 0000000..704cbb2 --- /dev/null +++ b/tests/python/find_test/cases/05_duplicate_test_fails.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Duplicate test files violate 1-service-1-test rule +# Expected: exit 1, message about 1 service = 1 test file +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_duplicate" + +create_python_fixture "app/services/order_service.py" "OrderService" +create_python_fixture "tests/services/test_order_service.py" "TestOrderService" +create_python_fixture "tests/services/test_order_service_v2.py" "TestOrderServiceV2" + +run_hook "python/find_test.sh" "app/services/order_service.py" + +assert_exit_code 1 +assert_output_contains "1 service = 1 test file" + +test_passed diff --git a/tests/python/find_test/cases/06_init_py_skipped.sh b/tests/python/find_test/cases/06_init_py_skipped.sh new file mode 100755 index 0000000..bd61932 --- /dev/null +++ b/tests/python/find_test/cases/06_init_py_skipped.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: __init__.py files are skipped +# Expected: exit 0, message about no service files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "find_test_init_py" + +create_python_fixture "app/services/__init__.py" "Init" + +run_hook "python/find_test.sh" "app/services/__init__.py" + +assert_exit_code 0 +assert_output_contains "No service files to check" + +test_passed diff --git a/tests/python/find_test/run.sh b/tests/python/find_test/run.sh new file mode 100755 index 0000000..465205a --- /dev/null +++ b/tests/python/find_test/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all find_test hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Find test tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/flake8/cases/01_no_files_exits_zero.sh b/tests/python/flake8/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..53e98d9 --- /dev/null +++ b/tests/python/flake8/cases/01_no_files_exits_zero.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided exits with 0 +# Expected: exit 0, message about no files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_no_files" + +mock_flake8 0 + +run_hook "python/check_flake8.sh" + +assert_exit_code 0 +assert_output_contains "No files to check" + +test_passed diff --git a/tests/python/flake8/cases/02_no_py_files_exits_zero.sh b/tests/python/flake8/cases/02_no_py_files_exits_zero.sh new file mode 100755 index 0000000..c4eab1b --- /dev/null +++ b/tests/python/flake8/cases/02_no_py_files_exits_zero.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No .py files among arguments exits with 0 +# Expected: exit 0, message about no Python files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_no_py_files" + +mock_flake8 0 + +create_fixture "readme.md" "# Readme" + +run_hook "python/check_flake8.sh" "readme.md" + +assert_exit_code 0 +assert_output_contains "No Python files to check" + +test_passed diff --git a/tests/python/flake8/cases/03_clean_file_passes.sh b/tests/python/flake8/cases/03_clean_file_passes.sh new file mode 100755 index 0000000..ad0812a --- /dev/null +++ b/tests/python/flake8/cases/03_clean_file_passes.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Clean file passes flake8 check +# Expected: exit 0, message about check passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_clean_file" + +mock_flake8 0 + +create_python_fixture "app/clean.py" "CleanClass" + +run_hook "python/check_flake8.sh" "app/clean.py" + +assert_exit_code 0 +assert_output_contains "Code style check passed" + +test_passed diff --git a/tests/python/flake8/cases/04_file_with_errors_fails.sh b/tests/python/flake8/cases/04_file_with_errors_fails.sh new file mode 100755 index 0000000..09fc41c --- /dev/null +++ b/tests/python/flake8/cases/04_file_with_errors_fails.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: File with style errors fails +# Expected: exit 1, message about check failed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_file_with_errors" + +mock_flake8 1 "app/bad.py:1:1: E302 expected 2 blank lines" + +create_python_fixture "app/bad.py" "BadClass" + +run_hook "python/check_flake8.sh" "app/bad.py" + +assert_exit_code 1 +assert_output_contains "Code style check failed" + +test_passed diff --git a/tests/python/flake8/cases/05_nonexistent_file_skipped.sh b/tests/python/flake8/cases/05_nonexistent_file_skipped.sh new file mode 100755 index 0000000..a00e2c0 --- /dev/null +++ b/tests/python/flake8/cases/05_nonexistent_file_skipped.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Non-existent file is skipped +# Expected: exit 0, message about no Python files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_nonexistent_file" + +mock_flake8 0 + +run_hook "python/check_flake8.sh" "nonexistent.py" + +assert_exit_code 0 +assert_output_contains "No Python files to check" + +test_passed diff --git a/tests/python/flake8/run.sh b/tests/python/flake8/run.sh new file mode 100755 index 0000000..9f8cbad --- /dev/null +++ b/tests/python/flake8/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all flake8 hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Flake8 tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/flake8_docker/cases/01_no_files_exits_zero.sh b/tests/python/flake8_docker/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..7e58433 --- /dev/null +++ b/tests/python/flake8_docker/cases/01_no_files_exits_zero.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided exits with 0 +# Expected: exit 0, message about no files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_docker_no_files" + +mock_docker_running 0 + +run_hook "python/check_flake8_in_docker.sh" + +assert_exit_code 0 +assert_output_contains "No files to check" + +test_passed diff --git a/tests/python/flake8_docker/cases/02_container_not_running.sh b/tests/python/flake8_docker/cases/02_container_not_running.sh new file mode 100755 index 0000000..2f606ae --- /dev/null +++ b/tests/python/flake8_docker/cases/02_container_not_running.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Container not running exits with 1 +# Expected: exit 1, message about container not running +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_docker_container_stopped" + +mock_docker_stopped + +create_python_fixture "app/service.py" "Service" + +run_hook "python/check_flake8_in_docker.sh" "app/service.py" + +assert_exit_code 1 +assert_output_contains "Container 'app_dev' is not running" + +test_passed diff --git a/tests/python/flake8_docker/cases/03_clean_file_passes.sh b/tests/python/flake8_docker/cases/03_clean_file_passes.sh new file mode 100755 index 0000000..bf2161f --- /dev/null +++ b/tests/python/flake8_docker/cases/03_clean_file_passes.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Clean file passes in Docker +# Expected: exit 0, message about check passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "flake8_docker_clean_file" + +mock_docker_running 0 + +create_python_fixture "app/clean.py" "CleanClass" + +run_hook "python/check_flake8_in_docker.sh" "app/clean.py" + +assert_exit_code 0 +assert_output_contains "Code style check passed" + +test_passed diff --git a/tests/python/flake8_docker/run.sh b/tests/python/flake8_docker/run.sh new file mode 100755 index 0000000..06a0488 --- /dev/null +++ b/tests/python/flake8_docker/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all flake8 Docker hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Flake8 Docker tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/mypy/cases/01_no_files_exits_zero.sh b/tests/python/mypy/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..16c0387 --- /dev/null +++ b/tests/python/mypy/cases/01_no_files_exits_zero.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided exits with 0 +# Expected: exit 0, message about no files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_no_files" + +mock_mypy 0 + +run_hook "python/check_mypy.sh" + +assert_exit_code 0 +assert_output_contains "No files to check" + +test_passed diff --git a/tests/python/mypy/cases/02_no_app_py_files_exits_zero.sh b/tests/python/mypy/cases/02_no_app_py_files_exits_zero.sh new file mode 100755 index 0000000..1a9808e --- /dev/null +++ b/tests/python/mypy/cases/02_no_app_py_files_exits_zero.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No app/ .py files among arguments exits with 0 +# Expected: exit 0, message about no Python files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_no_app_py_files" + +mock_mypy 0 + +create_python_fixture "lib/helper.py" "Helper" + +run_hook "python/check_mypy.sh" "lib/helper.py" + +assert_exit_code 0 +assert_output_contains "No Python files to check" + +test_passed diff --git a/tests/python/mypy/cases/03_clean_file_passes.sh b/tests/python/mypy/cases/03_clean_file_passes.sh new file mode 100755 index 0000000..6a77b8c --- /dev/null +++ b/tests/python/mypy/cases/03_clean_file_passes.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Clean file passes mypy check +# Expected: exit 0, message about analysis passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_clean_file" + +mock_mypy 0 "Success: no issues found" + +create_python_fixture "app/clean.py" "CleanClass" + +run_hook "python/check_mypy.sh" "app/clean.py" + +assert_exit_code 0 +assert_output_contains "Static analysis passed" + +test_passed diff --git a/tests/python/mypy/cases/04_file_with_errors_fails.sh b/tests/python/mypy/cases/04_file_with_errors_fails.sh new file mode 100755 index 0000000..c3b468e --- /dev/null +++ b/tests/python/mypy/cases/04_file_with_errors_fails.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: File with type errors fails +# Expected: exit 1, message about analysis failed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_file_with_errors" + +mock_mypy 1 "app/bad.py:1: error: Incompatible return type" + +create_python_fixture "app/bad.py" "BadClass" + +run_hook "python/check_mypy.sh" "app/bad.py" + +assert_exit_code 1 +assert_output_contains "Static analysis failed" + +test_passed diff --git a/tests/python/mypy/run.sh b/tests/python/mypy/run.sh new file mode 100755 index 0000000..a40b598 --- /dev/null +++ b/tests/python/mypy/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all mypy hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Mypy tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/mypy_docker/cases/01_no_files_exits_zero.sh b/tests/python/mypy_docker/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..19bfd8e --- /dev/null +++ b/tests/python/mypy_docker/cases/01_no_files_exits_zero.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided exits with 0 +# Expected: exit 0, message about no files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_docker_no_files" + +mock_docker_running 0 + +run_hook "python/check_mypy_in_docker.sh" + +assert_exit_code 0 +assert_output_contains "No files to check" + +test_passed diff --git a/tests/python/mypy_docker/cases/02_container_not_running.sh b/tests/python/mypy_docker/cases/02_container_not_running.sh new file mode 100755 index 0000000..9ac47a9 --- /dev/null +++ b/tests/python/mypy_docker/cases/02_container_not_running.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Container not running exits with 1 +# Expected: exit 1, message about container not running +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_docker_container_stopped" + +mock_docker_stopped + +create_python_fixture "app/service.py" "Service" + +run_hook "python/check_mypy_in_docker.sh" "app/service.py" + +assert_exit_code 1 +assert_output_contains "Container 'app_dev' is not running" + +test_passed diff --git a/tests/python/mypy_docker/cases/03_clean_file_passes.sh b/tests/python/mypy_docker/cases/03_clean_file_passes.sh new file mode 100755 index 0000000..9631b8f --- /dev/null +++ b/tests/python/mypy_docker/cases/03_clean_file_passes.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Clean file passes in Docker +# Expected: exit 0, message about analysis passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "mypy_docker_clean_file" + +mock_docker_running 0 "Success: no issues found" + +create_python_fixture "app/clean.py" "CleanClass" + +run_hook "python/check_mypy_in_docker.sh" "app/clean.py" + +assert_exit_code 0 +assert_output_contains "Static analysis passed" + +test_passed diff --git a/tests/python/mypy_docker/run.sh b/tests/python/mypy_docker/run.sh new file mode 100755 index 0000000..7821c65 --- /dev/null +++ b/tests/python/mypy_docker/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all mypy Docker hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Mypy Docker tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/pytest_docker/cases/01_container_not_running.sh b/tests/python/pytest_docker/cases/01_container_not_running.sh new file mode 100755 index 0000000..227c314 --- /dev/null +++ b/tests/python/pytest_docker/cases/01_container_not_running.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Container not running exits with 1 +# Expected: exit 1, message about container not running +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "pytest_docker_container_stopped" + +mock_docker_stopped + +run_hook "python/check_pytest_in_docker.sh" + +assert_exit_code 1 +assert_output_contains "Container 'app_dev' is not running" + +test_passed diff --git a/tests/python/pytest_docker/cases/02_tests_pass.sh b/tests/python/pytest_docker/cases/02_tests_pass.sh new file mode 100755 index 0000000..b7a8edc --- /dev/null +++ b/tests/python/pytest_docker/cases/02_tests_pass.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Tests pass in Docker +# Expected: exit 0, message about all tests passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "pytest_docker_pass" + +mock_docker_running 0 "3 passed in 0.12s" + +run_hook "python/check_pytest_in_docker.sh" + +assert_exit_code 0 +assert_output_contains "All tests passed" + +test_passed diff --git a/tests/python/pytest_docker/cases/03_tests_fail.sh b/tests/python/pytest_docker/cases/03_tests_fail.sh new file mode 100755 index 0000000..d1990c5 --- /dev/null +++ b/tests/python/pytest_docker/cases/03_tests_fail.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Tests fail in Docker +# Expected: exit 1, message about tests failed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "pytest_docker_fail" + +mock_docker_running 1 "1 failed, 2 passed in 0.15s" + +run_hook "python/check_pytest_in_docker.sh" + +assert_exit_code 1 +assert_output_contains "Tests failed" + +test_passed diff --git a/tests/python/pytest_docker/run.sh b/tests/python/pytest_docker/run.sh new file mode 100755 index 0000000..8c3640f --- /dev/null +++ b/tests/python/pytest_docker/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all pytest Docker hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Pytest Docker tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/python/pytest_local/cases/01_tests_pass.sh b/tests/python/pytest_local/cases/01_tests_pass.sh new file mode 100755 index 0000000..2a6ee28 --- /dev/null +++ b/tests/python/pytest_local/cases/01_tests_pass.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Tests pass (pytest exit 0) +# Expected: exit 0, message about all tests passed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "pytest_local_pass" + +mock_pytest_cmd 0 "3 passed in 0.12s" + +mkdir -p "tests" + +run_hook "python/check_pytest.sh" + +assert_exit_code 0 +assert_output_contains "All tests passed" + +test_passed diff --git a/tests/python/pytest_local/cases/02_tests_fail.sh b/tests/python/pytest_local/cases/02_tests_fail.sh new file mode 100755 index 0000000..225d67b --- /dev/null +++ b/tests/python/pytest_local/cases/02_tests_fail.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Tests fail (pytest exit 1) +# Expected: exit 1, message about tests failed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "pytest_local_fail" + +mock_pytest_cmd 1 "1 failed, 2 passed in 0.15s" + +mkdir -p "tests" + +run_hook "python/check_pytest.sh" + +assert_exit_code 1 +assert_output_contains "Tests failed" + +test_passed diff --git a/tests/python/pytest_local/run.sh b/tests/python/pytest_local/run.sh new file mode 100755 index 0000000..5982a10 --- /dev/null +++ b/tests/python/pytest_local/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all pytest local hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Pytest local tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]]