# Generated by Copilot
# Maintained by Dinesh Kumar C (https://github.com/Dinesh210805/blender-mcp)
# Forked from original work by Siddharth Ahuja (https://github.com/ahujasid/blender-mcp)

"""Tests for validation utilities."""

import pytest
from pathlib import Path
import tempfile

from src.blender_mcp.utils.validation import (
    validate_object_name,
    validate_file_path,
    validate_url,
    validate_code_safety,
    validate_asset_params,
    validate_numeric_range,
    validate_bbox_condition,
    sanitize_filename,
)
from src.blender_mcp.exceptions import ValidationError, SecurityError


class TestValidateObjectName:
    """Test object name validation."""
    
    def test_valid_object_name(self):
        """Test valid object names."""
        assert validate_object_name("Cube") == "Cube"
        assert validate_object_name("My_Object-1.001") == "My_Object-1.001"
        assert validate_object_name("  Trimmed  ") == "Trimmed"
    
    def test_invalid_object_name(self):
        """Test invalid object names."""
        with pytest.raises(ValidationError):
            validate_object_name("")
        
        with pytest.raises(ValidationError):
            validate_object_name(None)
        
        with pytest.raises(ValidationError):
            validate_object_name("   ")
    
    def test_sanitize_object_name(self):
        """Test object name sanitization."""
        result = validate_object_name("Obj<>ect")
        assert "<" not in result
        assert ">" not in result


class TestValidateFilePath:
    """Test file path validation."""
    
    def test_valid_file_path(self):
        """Test valid file paths."""
        with tempfile.NamedTemporaryFile() as tmp:
            path = validate_file_path(tmp.name, must_exist=True)
            assert path.exists()
    
    def test_path_traversal_protection(self):
        """Test protection against path traversal."""
        with pytest.raises(SecurityError):
            validate_file_path("../../../etc/passwd")
    
    def test_nonexistent_file_required(self):
        """Test error when required file doesn't exist."""
        with pytest.raises(ValidationError):
            validate_file_path("/nonexistent/file.txt", must_exist=True)


class TestValidateUrl:
    """Test URL validation."""
    
    def test_valid_urls(self):
        """Test valid URLs."""
        assert validate_url("https://example.com") == "https://example.com"
        assert validate_url("http://localhost:8080") == "http://localhost:8080"
    
    def test_invalid_urls(self):
        """Test invalid URLs."""
        with pytest.raises(ValidationError):
            validate_url("not-a-url")
        
        with pytest.raises(ValidationError):
            validate_url("ftp://example.com")  # Only HTTP/HTTPS allowed
        
        with pytest.raises(ValidationError):
            validate_url("")


class TestValidateCodeSafety:
    """Test code safety validation."""
    
    def test_safe_code(self):
        """Test safe code passes validation."""
        safe_code = "x = 1 + 2\nprint(x)"
        assert validate_code_safety(safe_code) is True
    
    def test_dangerous_code(self):
        """Test dangerous code is rejected."""
        dangerous_patterns = [
            "import os",
            "exec('malicious code')",
            "eval('1+1')",
            "__import__('os')",
            "open('/etc/passwd')",
        ]
        
        for pattern in dangerous_patterns:
            with pytest.raises(SecurityError):
                validate_code_safety(pattern)
    
    def test_code_length_limit(self):
        """Test code length limit."""
        long_code = "x = 1\n" * 10000
        with pytest.raises(SecurityError):
            validate_code_safety(long_code)


class TestValidateAssetParams:
    """Test asset parameter validation."""
    
    def test_valid_asset_params(self):
        """Test valid asset parameters."""
        result = validate_asset_params("test_asset", "textures", "2k")
        assert result["asset_id"] == "test_asset"
        assert result["asset_type"] == "textures"
        assert result["resolution"] == "2k"
    
    def test_invalid_asset_type(self):
        """Test invalid asset type."""
        with pytest.raises(ValidationError):
            validate_asset_params("test", "invalid_type", "1k")
    
    def test_invalid_resolution(self):
        """Test invalid resolution."""
        with pytest.raises(ValidationError):
            validate_asset_params("test", "textures", "invalid_res")
    
    def test_invalid_asset_id(self):
        """Test invalid asset ID."""
        with pytest.raises(ValidationError):
            validate_asset_params("test@asset", "textures", "1k")


class TestValidateBboxCondition:
    """Test bounding box validation."""
    
    def test_valid_bbox(self):
        """Test valid bounding box."""
        result = validate_bbox_condition([2.0, 4.0, 1.0])
        assert result == [50, 100, 25]  # Normalized to percentages
    
    def test_none_bbox(self):
        """Test None bounding box."""
        assert validate_bbox_condition(None) is None
    
    def test_invalid_bbox_length(self):
        """Test invalid bounding box length."""
        with pytest.raises(ValidationError):
            validate_bbox_condition([1.0, 2.0])  # Only 2 values
    
    def test_invalid_bbox_values(self):
        """Test invalid bounding box values."""
        with pytest.raises(ValidationError):
            validate_bbox_condition([1.0, -2.0, 3.0])  # Negative value


class TestSanitizeFilename:
    """Test filename sanitization."""
    
    def test_valid_filename(self):
        """Test valid filenames."""
        assert sanitize_filename("test.txt") == "test.txt"
        assert sanitize_filename("my_file-1.png") == "my_file-1.png"
    
    def test_sanitize_dangerous_chars(self):
        """Test sanitization of dangerous characters."""
        result = sanitize_filename("file<>name.txt")
        assert "<" not in result
        assert ">" not in result
    
    def test_empty_filename(self):
        """Test empty filename."""
        with pytest.raises(ValidationError):
            sanitize_filename("")
    
    def test_filename_length_limit(self):
        """Test filename length limit."""
        long_name = "a" * 300 + ".txt"
        result = sanitize_filename(long_name)
        assert len(result) <= 255
