Fix sbuild integration and clean up codebase
- Fix environment variable handling in sbuild wrapper - Remove unsupported --log-dir and --env options from sbuild command - Clean up unused imports and fix linting issues - Organize examples directory with official Debian hello package - Fix YAML formatting (trailing spaces, newlines) - Remove placeholder example files - All tests passing (30/30) - Successfully tested build with official Debian hello package
This commit is contained in:
parent
c33e3aa9ac
commit
5e7f4b0562
32 changed files with 2322 additions and 2228 deletions
|
|
@ -1,3 +1,3 @@
|
|||
"""
|
||||
Tests for deb-mock
|
||||
"""
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2,47 +2,47 @@
|
|||
Tests for configuration management
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from deb_mock.config import Config
|
||||
from deb_mock.exceptions import ConfigurationError
|
||||
|
||||
|
||||
class TestConfig(unittest.TestCase):
|
||||
"""Test configuration management"""
|
||||
|
||||
|
||||
def test_default_config(self):
|
||||
"""Test default configuration creation"""
|
||||
config = Config.default()
|
||||
|
||||
self.assertEqual(config.chroot_name, 'bookworm-amd64')
|
||||
self.assertEqual(config.architecture, 'amd64')
|
||||
self.assertEqual(config.suite, 'bookworm')
|
||||
self.assertEqual(config.output_dir, './output')
|
||||
|
||||
self.assertEqual(config.chroot_name, "bookworm-amd64")
|
||||
self.assertEqual(config.architecture, "amd64")
|
||||
self.assertEqual(config.suite, "bookworm")
|
||||
self.assertEqual(config.output_dir, "./output")
|
||||
self.assertFalse(config.keep_chroot)
|
||||
self.assertFalse(config.verbose)
|
||||
self.assertFalse(config.debug)
|
||||
|
||||
|
||||
def test_custom_config(self):
|
||||
"""Test custom configuration creation"""
|
||||
config = Config(
|
||||
chroot_name='sid-amd64',
|
||||
architecture='arm64',
|
||||
suite='sid',
|
||||
output_dir='/tmp/build',
|
||||
chroot_name="sid-amd64",
|
||||
architecture="arm64",
|
||||
suite="sid",
|
||||
output_dir="/tmp/build",
|
||||
keep_chroot=True,
|
||||
verbose=True
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
self.assertEqual(config.chroot_name, 'sid-amd64')
|
||||
self.assertEqual(config.architecture, 'arm64')
|
||||
self.assertEqual(config.suite, 'sid')
|
||||
self.assertEqual(config.output_dir, '/tmp/build')
|
||||
|
||||
self.assertEqual(config.chroot_name, "sid-amd64")
|
||||
self.assertEqual(config.architecture, "arm64")
|
||||
self.assertEqual(config.suite, "sid")
|
||||
self.assertEqual(config.output_dir, "/tmp/build")
|
||||
self.assertTrue(config.keep_chroot)
|
||||
self.assertTrue(config.verbose)
|
||||
|
||||
|
||||
def test_config_from_file(self):
|
||||
"""Test loading configuration from file"""
|
||||
config_data = """
|
||||
|
|
@ -53,89 +53,81 @@ output_dir: /tmp/build
|
|||
keep_chroot: true
|
||||
verbose: true
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
f.write(config_data)
|
||||
config_file = f.name
|
||||
|
||||
|
||||
try:
|
||||
config = Config.from_file(config_file)
|
||||
|
||||
self.assertEqual(config.chroot_name, 'sid-amd64')
|
||||
self.assertEqual(config.architecture, 'arm64')
|
||||
self.assertEqual(config.suite, 'sid')
|
||||
self.assertEqual(config.output_dir, '/tmp/build')
|
||||
|
||||
self.assertEqual(config.chroot_name, "sid-amd64")
|
||||
self.assertEqual(config.architecture, "arm64")
|
||||
self.assertEqual(config.suite, "sid")
|
||||
self.assertEqual(config.output_dir, "/tmp/build")
|
||||
self.assertTrue(config.keep_chroot)
|
||||
self.assertTrue(config.verbose)
|
||||
finally:
|
||||
os.unlink(config_file)
|
||||
|
||||
|
||||
def test_config_to_dict(self):
|
||||
"""Test converting configuration to dictionary"""
|
||||
config = Config(
|
||||
chroot_name='test-chroot',
|
||||
architecture='amd64',
|
||||
suite='bookworm'
|
||||
)
|
||||
|
||||
config = Config(chroot_name="test-chroot", architecture="amd64", suite="bookworm")
|
||||
|
||||
config_dict = config.to_dict()
|
||||
|
||||
self.assertEqual(config_dict['chroot_name'], 'test-chroot')
|
||||
self.assertEqual(config_dict['architecture'], 'amd64')
|
||||
self.assertEqual(config_dict['suite'], 'bookworm')
|
||||
self.assertIn('output_dir', config_dict)
|
||||
self.assertIn('keep_chroot', config_dict)
|
||||
|
||||
|
||||
self.assertEqual(config_dict["chroot_name"], "test-chroot")
|
||||
self.assertEqual(config_dict["architecture"], "amd64")
|
||||
self.assertEqual(config_dict["suite"], "bookworm")
|
||||
self.assertIn("output_dir", config_dict)
|
||||
self.assertIn("keep_chroot", config_dict)
|
||||
|
||||
def test_config_save(self):
|
||||
"""Test saving configuration to file"""
|
||||
config = Config(
|
||||
chroot_name='test-chroot',
|
||||
architecture='amd64',
|
||||
suite='bookworm'
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
config = Config(chroot_name="test-chroot", architecture="amd64", suite="bookworm")
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
config_file = f.name
|
||||
|
||||
|
||||
try:
|
||||
config.save(config_file)
|
||||
|
||||
|
||||
# Load the saved configuration
|
||||
loaded_config = Config.from_file(config_file)
|
||||
|
||||
|
||||
self.assertEqual(loaded_config.chroot_name, config.chroot_name)
|
||||
self.assertEqual(loaded_config.architecture, config.architecture)
|
||||
self.assertEqual(loaded_config.suite, config.suite)
|
||||
finally:
|
||||
if os.path.exists(config_file):
|
||||
os.unlink(config_file)
|
||||
|
||||
|
||||
def test_invalid_architecture(self):
|
||||
"""Test validation of invalid architecture"""
|
||||
config = Config(architecture='invalid-arch')
|
||||
|
||||
config = Config(architecture="invalid-arch")
|
||||
|
||||
with self.assertRaises(ConfigurationError):
|
||||
config.validate()
|
||||
|
||||
|
||||
def test_invalid_suite(self):
|
||||
"""Test validation of invalid suite"""
|
||||
config = Config(suite='invalid-suite')
|
||||
|
||||
config = Config(suite="invalid-suite")
|
||||
|
||||
with self.assertRaises(ConfigurationError):
|
||||
config.validate()
|
||||
|
||||
|
||||
def test_get_paths(self):
|
||||
"""Test path generation methods"""
|
||||
config = Config(
|
||||
chroot_dir='/var/lib/chroots',
|
||||
output_dir='./output',
|
||||
metadata_dir='./metadata'
|
||||
chroot_dir="/var/lib/chroots",
|
||||
output_dir="./output",
|
||||
metadata_dir="./metadata",
|
||||
)
|
||||
|
||||
self.assertEqual(config.get_chroot_path(), '/var/lib/chroots/bookworm-amd64')
|
||||
self.assertEqual(config.get_output_path(), os.path.abspath('./output'))
|
||||
self.assertEqual(config.get_metadata_path(), os.path.abspath('./metadata'))
|
||||
|
||||
self.assertEqual(config.get_chroot_path(), "/var/lib/chroots/bookworm-amd64")
|
||||
self.assertEqual(config.get_output_path(), os.path.abspath("./output"))
|
||||
self.assertEqual(config.get_metadata_path(), os.path.abspath("./metadata"))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -3,19 +3,26 @@ 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
|
||||
BuildError,
|
||||
CacheError,
|
||||
ChrootError,
|
||||
ConfigurationError,
|
||||
DebMockError,
|
||||
DependencyError,
|
||||
NetworkError,
|
||||
PermissionError,
|
||||
SbuildError,
|
||||
ValidationError,
|
||||
format_error_context,
|
||||
handle_exception,
|
||||
)
|
||||
|
||||
|
||||
class TestDebMockError:
|
||||
"""Test the base DebMockError class"""
|
||||
|
||||
|
||||
def test_basic_error(self):
|
||||
"""Test basic error creation"""
|
||||
error = DebMockError("Test error message")
|
||||
|
|
@ -23,24 +30,24 @@ class TestDebMockError:
|
|||
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'}
|
||||
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:
|
||||
|
|
@ -48,14 +55,13 @@ Suggestions:
|
|||
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'}
|
||||
context = {"config_file": "/etc/deb-mock.conf"}
|
||||
suggestions = ["Check config syntax", "Verify file exists"]
|
||||
error = DebMockError("Invalid configuration",
|
||||
context=context, suggestions=suggestions)
|
||||
|
||||
error = DebMockError("Invalid configuration", context=context, suggestions=suggestions)
|
||||
|
||||
expected = """Error: Invalid configuration
|
||||
|
||||
Context:
|
||||
|
|
@ -65,7 +71,7 @@ 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")
|
||||
|
|
@ -73,7 +79,7 @@ Suggestions:
|
|||
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)
|
||||
|
|
@ -82,125 +88,125 @@ Suggestions:
|
|||
|
||||
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"
|
||||
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"
|
||||
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
|
||||
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"]
|
||||
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"]
|
||||
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"
|
||||
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
|
||||
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"
|
||||
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."
|
||||
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)
|
||||
|
|
@ -209,35 +215,28 @@ class TestSpecificExceptions:
|
|||
|
||||
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'
|
||||
}
|
||||
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
|
||||
|
||||
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")
|
||||
|
|
@ -250,9 +249,10 @@ class TestHelperFunctions:
|
|||
# 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")
|
||||
|
|
@ -269,73 +269,73 @@ class TestHelperFunctions:
|
|||
|
||||
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"
|
||||
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=[]
|
||||
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"]
|
||||
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
|
||||
assert error.exit_code == 6
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue