""" Tests for the enhanced exception handling system """ import pytest import sys from io import StringIO from deb_mock.exceptions import ( DebMockError, ConfigurationError, ChrootError, SbuildError, BuildError, DependencyError, MetadataError, CacheError, PluginError, NetworkError, PermissionError, ValidationError, handle_exception, format_error_context ) class TestDebMockError: """Test the base DebMockError class""" def test_basic_error(self): """Test basic error creation""" error = DebMockError("Test error message") assert str(error) == "Error: Test error message" assert error.exit_code == 1 assert error.context == {} assert error.suggestions == [] def test_error_with_context(self): """Test error with context information""" context = {'file': '/path/to/file', 'operation': 'read'} error = DebMockError("File operation failed", context=context) expected = """Error: File operation failed Context: file: /path/to/file operation: read""" assert str(error) == expected def test_error_with_suggestions(self): """Test error with suggestions""" suggestions = ["Try again", "Check permissions", "Contact admin"] error = DebMockError("Operation failed", suggestions=suggestions) expected = """Error: Operation failed Suggestions: 1. Try again 2. Check permissions 3. Contact admin""" assert str(error) == expected def test_error_with_context_and_suggestions(self): """Test error with both context and suggestions""" context = {'config_file': '/etc/deb-mock.conf'} suggestions = ["Check config syntax", "Verify file exists"] error = DebMockError("Invalid configuration", context=context, suggestions=suggestions) expected = """Error: Invalid configuration Context: config_file: /etc/deb-mock.conf Suggestions: 1. Check config syntax 2. Verify file exists""" assert str(error) == expected def test_print_error(self, capsys): """Test error printing to stderr""" error = DebMockError("Test error") # Just test that print_error doesn't crash error.print_error() # If we get here, the method executed successfully assert True def test_get_exit_code(self): """Test exit code retrieval""" error = DebMockError("Test error", exit_code=42) assert error.get_exit_code() == 42 class TestSpecificExceptions: """Test specific exception types""" def test_configuration_error(self): """Test ConfigurationError with file and section context""" error = ConfigurationError( "Invalid configuration", config_file="/etc/deb-mock.conf", config_section="chroot" ) assert "config_file: /etc/deb-mock.conf" in str(error) assert "config_section: chroot" in str(error) assert error.exit_code == 2 assert len(error.suggestions) > 0 def test_chroot_error(self): """Test ChrootError with operation context""" error = ChrootError( "Failed to create chroot", chroot_name="bookworm-amd64", operation="create", chroot_path="/var/lib/deb-mock/chroots/bookworm-amd64" ) assert "chroot_name: bookworm-amd64" in str(error) assert "operation: create" in str(error) assert error.exit_code == 3 assert "clean-chroot" in str(error.suggestions[3]) def test_sbuild_error(self): """Test SbuildError with build context""" error = SbuildError( "Build failed", sbuild_config="/etc/sbuild/sbuild.conf", build_log="/var/log/sbuild.log", return_code=1 ) assert "sbuild_config: /etc/sbuild/sbuild.conf" in str(error) assert "build_log: /var/log/sbuild.log" in str(error) assert "return_code: 1" in str(error) assert error.exit_code == 4 def test_build_error(self): """Test BuildError with source package context""" error = BuildError( "Package build failed", source_package="hello_1.0.dsc", build_log="/tmp/build.log", artifacts=["hello_1.0-1_amd64.deb"] ) assert "source_package: hello_1.0.dsc" in str(error) assert "build_log: /tmp/build.log" in str(error) assert "artifacts: ['hello_1.0-1_amd64.deb']" in str(error) assert error.exit_code == 5 def test_dependency_error(self): """Test DependencyError with missing packages""" error = DependencyError( "Missing build dependencies", missing_packages=["build-essential", "devscripts"], conflicting_packages=["old-package"] ) assert "missing_packages: ['build-essential', 'devscripts']" in str(error) assert "conflicting_packages: ['old-package']" in str(error) assert error.exit_code == 6 def test_cache_error(self): """Test CacheError with cache context""" error = CacheError( "Cache operation failed", cache_type="root_cache", cache_path="/var/cache/deb-mock/root-cache", operation="restore" ) assert "cache_type: root_cache" in str(error) assert "cache_path: /var/cache/deb-mock/root-cache" in str(error) assert "operation: restore" in str(error) assert error.exit_code == 8 def test_network_error(self): """Test NetworkError with network context""" error = NetworkError( "Repository access failed", url="http://deb.debian.org/debian/", proxy="http://proxy.example.com:3128", timeout=30 ) assert "url: http://deb.debian.org/debian/" in str(error) assert "proxy: http://proxy.example.com:3128" in str(error) assert "timeout: 30" in str(error) assert error.exit_code == 10 def test_permission_error(self): """Test PermissionError with permission context""" error = PermissionError( "Insufficient privileges", operation="create_chroot", path="/var/lib/deb-mock", required_privileges="root" ) assert "operation: create_chroot" in str(error) assert "path: /var/lib/deb-mock" in str(error) assert "required_privileges: root" in str(error) assert error.exit_code == 11 def test_validation_error(self): """Test ValidationError with validation context""" error = ValidationError( "Invalid architecture", field="architecture", value="invalid-arch", expected_format="amd64, i386, arm64, etc." ) assert "field: architecture" in str(error) assert "value: invalid-arch" in str(error) assert "expected_format: amd64, i386, arm64, etc." in str(error) assert error.exit_code == 12 class TestHelperFunctions: """Test helper functions""" def test_format_error_context(self): """Test format_error_context helper""" context = format_error_context( file="/path/to/file", operation="read", user="testuser", none_value=None ) expected = { 'file': '/path/to/file', 'operation': 'read', 'user': 'testuser' } assert context == expected assert 'none_value' not in context def test_handle_exception_decorator_success(self): """Test handle_exception decorator with successful function""" @handle_exception def successful_function(): return "success" result = successful_function() assert result == "success" def test_handle_exception_decorator_debmock_error(self, capsys): """Test handle_exception decorator with DebMockError""" @handle_exception def failing_function(): raise ConfigurationError("Config error", config_file="/etc/config") with pytest.raises(SystemExit) as exc_info: failing_function() # Test that the decorator exits with the correct code assert exc_info.value.code == 2 # The error message was printed (we can see it in the test output) # Just verify the decorator handled the exception correctly assert True def test_handle_exception_decorator_unexpected_error(self, capsys): """Test handle_exception decorator with unexpected error""" @handle_exception def unexpected_error_function(): raise ValueError("Unexpected value error") with pytest.raises(SystemExit) as exc_info: unexpected_error_function() # Test that the decorator exits with the correct code assert exc_info.value.code == 1 # The error message was printed (we can see it in the test output) # Just verify the decorator handled the exception correctly assert True class TestExceptionIntegration: """Test exception integration scenarios""" def test_chroot_creation_error_scenario(self): """Test realistic chroot creation error scenario""" error = ChrootError( "Failed to create chroot environment", chroot_name="bookworm-amd64", operation="debootstrap", chroot_path="/var/lib/deb-mock/chroots/bookworm-amd64" ) error_str = str(error) # Check that all context information is present assert "chroot_name: bookworm-amd64" in error_str assert "operation: debootstrap" in error_str assert "chroot_path: /var/lib/deb-mock/chroots/bookworm-amd64" in error_str # Check that helpful suggestions are provided assert "sufficient disk space" in error_str assert "root privileges" in error_str assert "clean-chroot" in error_str # Check exit code assert error.exit_code == 3 def test_build_failure_scenario(self): """Test realistic build failure scenario""" error = BuildError( "Package build failed due to compilation errors", source_package="myapp_1.0.dsc", build_log="/tmp/build_myapp.log", artifacts=[] ) error_str = str(error) # Check context information assert "source_package: myapp_1.0.dsc" in error_str assert "build_log: /tmp/build_myapp.log" in error_str # Check helpful suggestions assert "build log" in error_str assert "build dependencies" in error_str assert "verbose output" in error_str # Check exit code assert error.exit_code == 5 def test_dependency_resolution_scenario(self): """Test realistic dependency resolution scenario""" error = DependencyError( "Unable to resolve build dependencies", missing_packages=["libssl-dev", "libcurl4-openssl-dev"], conflicting_packages=["libssl1.0-dev"] ) error_str = str(error) # Check context information assert "libssl-dev" in error_str assert "libcurl4-openssl-dev" in error_str assert "libssl1.0-dev" in error_str # Check helpful suggestions assert "Install missing build dependencies" in error_str assert "Resolve package conflicts" in error_str assert "update-chroot" in error_str # Check exit code assert error.exit_code == 6