Explorar el Código

testing units

xcad hace 2 meses
padre
commit
7cf643d078
Se han modificado 7 ficheros con 304 adiciones y 498 borrados
  1. 0 1
      .gitignore
  2. 16 0
      pyproject.toml
  3. 3 0
      tests/__init__.py
  4. 0 318
      tests/test-compose-template-errors.sh
  5. 0 179
      tests/test-compose-templates.sh
  6. 176 0
      tests/test_variable.py
  7. 109 0
      tests/test_version.py

+ 0 - 1
.gitignore

@@ -29,5 +29,4 @@ dist/
 .installed-version
 
 # Test outputs
-tests/
 config.yaml

+ 16 - 0
pyproject.toml

@@ -91,3 +91,19 @@ line-ending = "lf"
 # CLI command functions need many parameters for command-line arguments
 "cli/core/module/base_module.py" = ["PLR0913"]  # generate() needs all CLI options
 "cli/core/repo.py" = ["PLR0913"]                # add() needs all library config options
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+python_files = ["test_*.py"]
+python_classes = ["Test*"]
+python_functions = ["test_*"]
+addopts = [
+    "-v",
+    "--strict-markers",
+    "--strict-config",
+    "--tb=short",
+]
+markers = [
+    "slow: marks tests as slow (deselect with '-m \"not slow\"')",
+    "integration: marks tests as integration tests",
+]

+ 3 - 0
tests/__init__.py

@@ -0,0 +1,3 @@
+"""
+Unit tests for boilerplates CLI.
+"""

+ 0 - 318
tests/test-compose-template-errors.sh

@@ -1,318 +0,0 @@
-#!/usr/bin/env bash
-#
-# Test script for template rendering error handling
-# This script creates various templates with errors and validates them
-# to test the improved error handling and display
-
-set -e
-
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m' # No Color
-
-# Test directory
-TEST_DIR="/tmp/boilerplates-error-tests"
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Template Error Handling Tests${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-# Clean up test directory
-rm -rf "$TEST_DIR"
-mkdir -p "$TEST_DIR"
-
-# Function to create a test template
-create_test_template() {
-  local name=$1
-  local description=$2
-  local template_content=$3
-  local spec_content=$4
-  
-  local template_dir="$TEST_DIR/$name"
-  mkdir -p "$template_dir"
-  
-  # Create template.yaml
-  cat > "$template_dir/template.yaml" <<EOF
----
-kind: compose
-metadata:
-  name: $name Test
-  description: $description
-  version: 0.1.0
-  author: Test Suite
-  date: '2025-01-09'
-$spec_content
-EOF
-  
-  # Create compose.yaml.j2
-  echo "$template_content" > "$template_dir/compose.yaml.j2"
-  
-  echo "$template_dir"
-}
-
-# Function to run a test
-run_test() {
-  local test_name=$1
-  local template_path=$2
-  local expected_to_fail=$3
-  
-  echo -e "${YELLOW}Test: $test_name${NC}"
-  echo -e "${BLUE}Template path: $template_path${NC}"
-  echo ""
-  
-  # Run validation with debug mode
-  if python3 -m cli --log-level DEBUG compose validate --path "$template_path" 2>&1; then
-    if [ "$expected_to_fail" = "true" ]; then
-      echo -e "${RED}✗ UNEXPECTED: Test passed but was expected to fail${NC}"
-      return 1
-    else
-      echo -e "${GREEN}✓ Test passed as expected${NC}"
-      return 0
-    fi
-  else
-    if [ "$expected_to_fail" = "true" ]; then
-      echo -e "${GREEN}✓ Test failed as expected${NC}"
-      return 0
-    else
-      echo -e "${RED}✗ UNEXPECTED: Test failed but was expected to pass${NC}"
-      return 1
-    fi
-  fi
-}
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 1: Undefined Variable Error${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_1=$(create_test_template \
-  "undefined-variable" \
-  "Template with undefined variable" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:{{ nginx_version }}
-    container_name: {{ undefined_variable }}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      nginx_version:
-        type: str
-        description: Nginx version
-        default: latest
-')
-
-run_test "Undefined Variable" "$TEMPLATE_1" "true"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 2: Jinja2 Syntax Error - Missing endif${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_2=$(create_test_template \
-  "syntax-error-endif" \
-  "Template with missing endif" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:latest
-    {% if enable_ports %}
-    ports:
-      - "80:80"
-    # Missing {% endif %}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      enable_ports:
-        type: bool
-        description: Enable ports
-        default: true
-')
-
-run_test "Syntax Error - Missing endif" "$TEMPLATE_2" "true"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 3: Jinja2 Syntax Error - Unclosed bracket${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_3=$(create_test_template \
-  "syntax-error-bracket" \
-  "Template with unclosed bracket" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:{{ version
-    container_name: {{ service_name }}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      version:
-        type: str
-        description: Version
-        default: latest
-')
-
-run_test "Syntax Error - Unclosed Bracket" "$TEMPLATE_3" "true"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 4: Filter Error - Unknown filter${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_4=$(create_test_template \
-  "filter-error" \
-  "Template with unknown filter" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:{{ version | unknown_filter }}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      version:
-        type: str
-        description: Version
-        default: latest
-')
-
-run_test "Filter Error - Unknown Filter" "$TEMPLATE_4" "true"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 5: Valid Template - Should Pass${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_5=$(create_test_template \
-  "valid-template" \
-  "Valid template that should pass validation" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:{{ version }}
-    container_name: {{ service_name }}
-    {% if enable_ports %}
-    ports:
-      - "{{ port }}:80"
-    {% endif %}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      version:
-        type: str
-        description: Version
-        default: latest
-      enable_ports:
-        type: bool
-        description: Enable ports
-        default: true
-      port:
-        type: int
-        description: External port
-        default: 8080
-')
-
-run_test "Valid Template" "$TEMPLATE_5" "false"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 6: Nested Variable with Typo${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_6=$(create_test_template \
-  "typo-variable" \
-  "Template with typo in variable name" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:latest
-    environment:
-      - SERVICE_NAME={{ servce_name }}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-')
-
-run_test "Typo in Variable Name" "$TEMPLATE_6" "true"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test 7: Template with Default Filter${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-
-TEMPLATE_7=$(create_test_template \
-  "default-filter" \
-  "Template using default filter - should pass" \
-  'version: "3.8"
-services:
-  {{ service_name }}:
-    image: nginx:{{ version | default("latest") }}
-    container_name: {{ container_name | default(service_name) }}
-' \
-  'spec:
-  general:
-    vars:
-      service_name:
-        type: str
-        description: Service name
-        default: myservice
-      version:
-        type: str
-        description: Version
-        default: ""
-')
-
-run_test "Default Filter Usage" "$TEMPLATE_7" "false"
-echo ""
-
-echo -e "${BLUE}========================================${NC}"
-echo -e "${BLUE}Test Summary${NC}"
-echo -e "${BLUE}========================================${NC}"
-echo ""
-echo -e "${GREEN}All tests completed!${NC}"
-echo -e "${YELLOW}Check the output above for detailed error messages.${NC}"
-echo ""
-echo -e "${BLUE}Test directory: $TEST_DIR${NC}"
-echo -e "${YELLOW}Note: Test directory has been preserved for manual inspection${NC}"

+ 0 - 179
tests/test-compose-templates.sh

@@ -1,179 +0,0 @@
-#!/usr/bin/env bash
-# Test script for validating all compose templates
-# This script iterates through all templates and runs validation
-# Output is GitHub Actions friendly and easy to parse
-
-set -euo pipefail
-
-# Colors for output (only if terminal supports it)
-if [[ -t 1 ]]; then
-    RED='\033[0;31m'
-    GREEN='\033[0;32m'
-    YELLOW='\033[1;33m'
-    BLUE='\033[0;34m'
-    NC='\033[0m'
-else
-    RED=''
-    GREEN=''
-    YELLOW=''
-    BLUE=''
-    NC=''
-fi
-
-# Counters
-TOTAL=0
-PASSED=0
-FAILED=0
-FAILED_TEMPLATES=()
-
-# Get script directory and project root
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
-
-# Function to print status (GitHub Actions annotations format)
-print_status() {
-    local status=$1
-    local message=$2
-    case $status in
-        "INFO")
-            echo "[INFO] ${message}"
-            ;;
-        "SUCCESS")
-            echo "[PASS] ${message}"
-            ;;
-        "ERROR")
-            echo "[FAIL] ${message}"
-            # GitHub Actions error annotation
-            if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
-                echo "::error::${message}"
-            fi
-            ;;
-        "WARNING")
-            echo "[WARN] ${message}"
-            if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
-                echo "::warning::${message}"
-            fi
-            ;;
-    esac
-}
-
-# Function to validate and test a single template
-validate_template() {
-    local template_id=$1
-    local template_name=$2
-    
-    TOTAL=$((TOTAL + 1))
-    
-    echo -e "${BLUE}────────────────────────────────────────${NC}"
-    print_status "INFO" "Testing: ${template_id} (${template_name})"
-    
-    # Step 1: Run validation
-    echo -e "  ${YELLOW}→${NC} Running validation..."
-    local temp_output=$(mktemp)
-    if ! python3 -m cli compose validate "${template_id}" \
-        > "${temp_output}" 2>&1; then
-        echo -e "  ${RED}✗${NC} Validation failed"
-        print_status "ERROR" "${template_id} - Validation failed"
-        FAILED=$((FAILED + 1))
-        FAILED_TEMPLATES+=("${template_id}")
-        
-        # Show error message (first few lines)
-        if [[ -s "${temp_output}" ]]; then
-            echo "  └─ Validation error:"
-            head -n 5 "${temp_output}" | sed 's/^/     /'
-        fi
-        rm -f "${temp_output}"
-        return 1
-    fi
-    echo -e "  ${GREEN}✓${NC} Validation passed"
-    rm -f "${temp_output}"
-    
-    # Step 2: Run dry-run generation with quiet mode
-    echo -e "  ${YELLOW}→${NC} Running dry-run generation..."
-    local temp_gen=$(mktemp)
-    if ! python3 -m cli compose generate "${template_id}" \
-        --dry-run \
-        --no-interactive \
-        --quiet \
-        > "${temp_gen}" 2>&1; then
-        echo -e "  ${RED}✗${NC} Generation failed"
-        print_status "ERROR" "${template_id} - Generation failed"
-        FAILED=$((FAILED + 1))
-        FAILED_TEMPLATES+=("${template_id}")
-        
-        # Show error message (first few lines)
-        if [[ -s "${temp_gen}" ]]; then
-            echo "  └─ Generation error:"
-            head -n 5 "${temp_gen}" | sed 's/^/     /'
-        fi
-        rm -f "${temp_gen}"
-        return 1
-    fi
-    echo -e "  ${GREEN}✓${NC} Generation passed"
-    rm -f "${temp_gen}"
-    
-    # Both validation and generation passed
-    print_status "SUCCESS" "${template_id}"
-    PASSED=$((PASSED + 1))
-    echo ""
-    return 0
-}
-
-# Main execution
-main() {
-    cd "${PROJECT_ROOT}"
-    
-    echo "=========================================="
-    echo "Compose Template Validation Tests"
-    echo "=========================================="
-    print_status "INFO" "Working directory: ${PROJECT_ROOT}"
-    echo ""
-    
-    # Get list of all compose templates
-    local templates
-    if ! templates=$(python3 -m cli compose list --raw 2>&1); then
-        print_status "ERROR" "Failed to retrieve template list"
-        echo "${templates}"
-        exit 1
-    fi
-    
-    # Count total templates
-    local template_count
-    template_count=$(echo "${templates}" | wc -l | tr -d ' ')
-    
-    print_status "INFO" "Found ${template_count} templates to test"
-    echo ""
-    
-    # Iterate through each template
-    while IFS=$'\t' read -r template_id template_name tags version library; do
-        # Continue even if validation fails (don't let set -e stop us)
-        validate_template "${template_id}" "${template_name}" || true
-    done <<< "${templates}"
-    
-    # Print summary
-    echo ""
-    echo "=========================================="
-    echo "Test Summary"
-    echo "=========================================="
-    echo "Total:    ${TOTAL}"
-    echo "Passed:   ${PASSED}"
-    echo "Failed:   ${FAILED}"
-    
-    # List failed templates if any
-    if [ "${FAILED}" -gt 0 ]; then
-        echo ""
-        echo "Failed templates:"
-        for template in "${FAILED_TEMPLATES[@]}"; do
-            echo "  - ${template}"
-        done
-        echo ""
-        print_status "ERROR" "${FAILED} template(s) failed validation"
-        exit 1
-    else
-        echo ""
-        print_status "SUCCESS" "All templates passed validation!"
-    fi
-}
-
-# Run main function
-main "$@"

+ 176 - 0
tests/test_variable.py

@@ -0,0 +1,176 @@
+"""Unit tests for Variable class."""
+
+import pytest
+
+from cli.core.template.variable import Variable
+
+# Test constants
+TEST_PORT = 8080
+TEST_COUNT = 42
+TEST_RATE = 3.14
+
+
+class TestVariableInitialization:
+    """Tests for Variable initialization."""
+
+    def test_create_simple_variable(self):
+        """Test creating a simple string variable."""
+        var = Variable({"name": "test_var", "type": "str"})
+        assert var.name == "test_var"
+        assert var.type == "str"
+        assert var.value is None
+
+    def test_create_variable_with_default(self):
+        """Test creating a variable with default value."""
+        var = Variable({"name": "port", "type": "int", "default": TEST_PORT})
+        assert var.name == "port"
+        assert var.value == TEST_PORT
+
+    def test_create_bool_variable_without_default(self):
+        """Test that bool variables default to False."""
+        var = Variable({"name": "enabled", "type": "bool"})
+        assert var.value is False
+
+    def test_create_variable_with_description(self):
+        """Test creating a variable with description."""
+        var = Variable({"name": "test", "type": "str", "description": "Test variable"})
+        assert var.description == "Test variable"
+
+    def test_missing_name_raises_error(self):
+        """Test that missing name key raises ValueError."""
+        with pytest.raises(ValueError, match="must contain 'name' key"):
+            Variable({"type": "str"})
+
+    def test_invalid_data_type_raises_error(self):
+        """Test that non-dict data raises ValueError."""
+        with pytest.raises(ValueError, match="must be a dictionary"):
+            Variable("not a dict")
+
+
+class TestVariableTypes:
+    """Tests for variable type handling."""
+
+    def test_string_type(self):
+        """Test string variable type."""
+        var = Variable({"name": "test", "type": "str", "default": "hello"})
+        assert var.value == "hello"
+
+    def test_int_type(self):
+        """Test integer variable type."""
+        var = Variable({"name": "count", "type": "int", "default": TEST_COUNT})
+        assert var.value == TEST_COUNT
+
+    def test_bool_type_true(self):
+        """Test boolean variable with true value."""
+        var = Variable({"name": "enabled", "type": "bool", "default": True})
+        assert var.value is True
+
+    def test_bool_type_false(self):
+        """Test boolean variable with false value."""
+        var = Variable({"name": "disabled", "type": "bool", "default": False})
+        assert var.value is False
+
+    def test_float_type(self):
+        """Test float variable type."""
+        var = Variable({"name": "rate", "type": "float", "default": TEST_RATE})
+        assert var.value == TEST_RATE
+
+
+class TestVariableProperties:
+    """Tests for variable properties."""
+
+    def test_sensitive_flag(self):
+        """Test sensitive flag for passwords."""
+        var = Variable({"name": "password", "type": "str", "sensitive": True})
+        assert var.sensitive is True
+
+    def test_autogenerated_flag(self):
+        """Test autogenerated flag."""
+        var = Variable({"name": "secret", "type": "str", "autogenerated": True})
+        assert var.autogenerated is True
+
+    def test_required_flag(self):
+        """Test required flag."""
+        var = Variable({"name": "hostname", "type": "str", "required": True})
+        assert var.required is True
+
+    def test_options_list(self):
+        """Test enum variable with options."""
+        var = Variable({"name": "mode", "type": "enum", "options": ["dev", "prod"]})
+        assert var.options == ["dev", "prod"]
+
+    def test_extra_help_text(self):
+        """Test extra help text."""
+        var = Variable({"name": "test", "type": "str", "extra": "Additional info"})
+        assert var.extra == "Additional info"
+
+
+class TestVariableNeeds:
+    """Tests for variable dependency constraints."""
+
+    def test_needs_single_string(self):
+        """Test needs constraint as single string."""
+        var = Variable({"name": "test", "type": "str", "needs": "other_var=value"})
+        assert var.needs == ["other_var=value"]
+
+    def test_needs_semicolon_separated(self):
+        """Test needs constraint with semicolon-separated conditions."""
+        var = Variable({"name": "test", "type": "str", "needs": "var1=value1;var2=value2"})
+        assert var.needs == ["var1=value1", "var2=value2"]
+
+    def test_needs_list(self):
+        """Test needs constraint as list."""
+        var = Variable({"name": "test", "type": "str", "needs": ["var1=value1", "var2=value2"]})
+        assert var.needs == ["var1=value1", "var2=value2"]
+
+    def test_needs_empty(self):
+        """Test variable without needs constraint."""
+        var = Variable({"name": "test", "type": "str"})
+        assert var.needs == []
+
+
+class TestVariableConversion:
+    """Tests for variable value conversion."""
+
+    def test_convert_string_to_int(self):
+        """Test converting string to integer."""
+        var = Variable({"name": "count", "type": "int"})
+        result = var.convert("42")
+        assert result == TEST_COUNT
+        assert isinstance(result, int)
+
+    def test_convert_string_to_bool_true(self):
+        """Test converting string to boolean true."""
+        var = Variable({"name": "enabled", "type": "bool"})
+        assert var.convert("true") is True
+        assert var.convert("1") is True
+        assert var.convert("yes") is True
+
+    def test_convert_string_to_bool_false(self):
+        """Test converting string to boolean false."""
+        var = Variable({"name": "disabled", "type": "bool"})
+        assert var.convert("false") is False
+        assert var.convert("0") is False
+        assert var.convert("no") is False
+
+    def test_convert_string_to_float(self):
+        """Test converting string to float."""
+        var = Variable({"name": "rate", "type": "float"})
+        result = var.convert("3.14")
+        assert result == TEST_RATE
+        assert isinstance(result, float)
+
+
+@pytest.mark.parametrize(
+    "var_type,default_value,expected",
+    [
+        ("str", "hello", "hello"),
+        ("int", TEST_COUNT, TEST_COUNT),
+        ("bool", True, True),
+        ("float", TEST_RATE, TEST_RATE),
+    ],
+)
+def test_variable_types_parametrized(var_type, default_value, expected):
+    """Test various variable types with parametrized inputs."""
+    var = Variable({"name": "test", "type": var_type, "default": default_value})
+    assert var.value == expected

+ 109 - 0
tests/test_version.py

@@ -0,0 +1,109 @@
+"""Unit tests for version comparison utilities."""
+
+import pytest
+
+from cli.core.version import compare_versions, is_compatible, parse_version
+
+
+class TestParseVersion:
+    """Tests for parse_version function."""
+
+    def test_parse_simple_version(self):
+        """Test parsing simple version string."""
+        assert parse_version("1.0") == (1, 0)
+        assert parse_version("1.2") == (1, 2)
+        assert parse_version("2.5") == (2, 5)
+
+    def test_parse_version_with_v_prefix(self):
+        """Test parsing version with 'v' prefix."""
+        assert parse_version("v1.0") == (1, 0)
+        assert parse_version("v2.3") == (2, 3)
+
+    def test_parse_version_empty_string(self):
+        """Test parsing empty string raises ValueError."""
+        with pytest.raises(ValueError, match="cannot be empty"):
+            parse_version("")
+
+    def test_parse_version_invalid_format(self):
+        """Test parsing invalid format raises ValueError."""
+        with pytest.raises(ValueError, match="Invalid version format"):
+            parse_version("1")
+        with pytest.raises(ValueError, match="Invalid version format"):
+            parse_version("1.2.3")
+        with pytest.raises(ValueError, match="Invalid version format"):
+            parse_version("invalid")
+
+
+class TestCompareVersions:
+    """Tests for compare_versions function."""
+
+    def test_compare_equal_versions(self):
+        """Test comparing equal versions."""
+        assert compare_versions("1.0", "1.0") == 0
+        assert compare_versions("2.5", "2.5") == 0
+
+    def test_compare_major_version_difference(self):
+        """Test comparing versions with different major numbers."""
+        assert compare_versions("2.0", "1.0") == 1
+        assert compare_versions("1.0", "2.0") == -1
+
+    def test_compare_minor_version_difference(self):
+        """Test comparing versions with different minor numbers."""
+        assert compare_versions("1.2", "1.0") == 1
+        assert compare_versions("1.0", "1.2") == -1
+
+    def test_compare_with_v_prefix(self):
+        """Test comparing versions with 'v' prefix."""
+        assert compare_versions("v1.0", "v1.0") == 0
+        assert compare_versions("v1.2", "v1.0") == 1
+
+    @pytest.mark.parametrize(
+        "v1,v2,expected",
+        [
+            ("1.0", "1.0", 0),
+            ("1.1", "1.0", 1),
+            ("1.0", "1.1", -1),
+            ("2.0", "1.9", 1),
+            ("0.9", "1.0", -1),
+        ],
+    )
+    def test_compare_versions_parametrized(self, v1, v2, expected):
+        """Test comparing various version combinations."""
+        assert compare_versions(v1, v2) == expected
+
+
+class TestIsCompatible:
+    """Tests for is_compatible function."""
+
+    def test_compatible_equal_versions(self):
+        """Test compatibility with equal versions."""
+        assert is_compatible("1.0", "1.0") is True
+
+    def test_compatible_newer_version(self):
+        """Test compatibility with newer current version."""
+        assert is_compatible("1.2", "1.0") is True
+        assert is_compatible("2.0", "1.0") is True
+
+    def test_incompatible_older_version(self):
+        """Test incompatibility with older current version."""
+        assert is_compatible("1.0", "1.2") is False
+        assert is_compatible("1.0", "2.0") is False
+
+    def test_incompatible_invalid_versions(self):
+        """Test that invalid versions return False for safety."""
+        assert is_compatible("invalid", "1.0") is False
+        assert is_compatible("1.0", "invalid") is False
+
+    @pytest.mark.parametrize(
+        "current,required,expected",
+        [
+            ("1.0", "1.0", True),
+            ("1.2", "1.0", True),
+            ("2.0", "1.0", True),
+            ("1.0", "1.2", False),
+            ("0.9", "1.0", False),
+        ],
+    )
+    def test_is_compatible_parametrized(self, current, required, expected):
+        """Test compatibility checks with various version combinations."""
+        assert is_compatible(current, required) is expected