""" Tests for deb-mock API This module contains comprehensive tests for the deb-mock API to ensure stability and reliability for external integrations. """ import os import sys import tempfile import unittest from pathlib import Path from unittest.mock import Mock, patch, MagicMock # Add deb-mock to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from deb_mock.api import MockAPIClient, MockEnvironment, MockConfigBuilder, create_client from deb_mock.environment_manager import EnvironmentManager, EnvironmentInfo, BuildResult from deb_mock.config import Config from deb_mock.exceptions import ChrootError, ConfigurationError class TestMockConfigBuilder(unittest.TestCase): """Test the MockConfigBuilder class""" def setUp(self): self.builder = MockConfigBuilder() def test_basic_configuration(self): """Test basic configuration building""" config = (self.builder .environment("test-env") .architecture("amd64") .suite("trixie") .mirror("http://test.debian.org/debian/") .build()) self.assertEqual(config.chroot_name, "test-env") self.assertEqual(config.architecture, "amd64") self.assertEqual(config.suite, "trixie") self.assertEqual(config.mirror, "http://test.debian.org/debian/") def test_packages_configuration(self): """Test packages configuration""" packages = ["build-essential", "devscripts", "cmake"] config = self.builder.packages(packages).build() self.assertEqual(config.chroot_additional_packages, packages) def test_output_directory(self): """Test output directory configuration""" output_dir = "/tmp/test-output" config = self.builder.output_dir(output_dir).build() self.assertEqual(config.output_dir, output_dir) def test_cache_settings(self): """Test cache configuration""" config = self.builder.cache_enabled(True).build() self.assertTrue(config.use_root_cache) config = self.builder.cache_enabled(False).build() self.assertFalse(config.use_root_cache) def test_parallel_jobs(self): """Test parallel jobs configuration""" config = self.builder.parallel_jobs(8).build() self.assertEqual(config.parallel_jobs, 8) def test_verbose_debug(self): """Test verbose and debug configuration""" config = (self.builder .verbose(True) .debug(True) .build()) self.assertTrue(config.verbose) self.assertTrue(config.debug) class TestMockAPIClient(unittest.TestCase): """Test the MockAPIClient class""" def setUp(self): self.temp_dir = tempfile.mkdtemp() self.config = Config( chroot_dir=self.temp_dir, output_dir=os.path.join(self.temp_dir, "output"), chroot_config_dir=os.path.join(self.temp_dir, "config") ) # Create necessary directories os.makedirs(self.config.chroot_config_dir, exist_ok=True) # Mock the DebMock class to avoid actual chroot operations with patch('deb_mock.api.DebMock') as mock_deb_mock: self.mock_deb_mock_instance = Mock() mock_deb_mock.return_value = self.mock_deb_mock_instance # Mock chroot manager self.mock_chroot_manager = Mock() self.mock_deb_mock_instance.chroot_manager = self.mock_chroot_manager self.mock_chroot_manager.chroot_exists.return_value = False self.mock_chroot_manager.get_chroot_info.return_value = { 'name': 'test-env', 'status': 'active', 'size': 1024, 'created': None, 'modified': None } # Mock other methods self.mock_deb_mock_instance.init_chroot = Mock() self.mock_deb_mock_instance.install_packages = Mock(return_value={'success': True}) self.mock_deb_mock_instance.clean_chroot = Mock() self.mock_deb_mock_instance.list_chroots = Mock(return_value=[]) self.mock_deb_mock_instance.build = Mock(return_value={'success': True, 'artifacts': []}) self.mock_deb_mock_instance.build_parallel = Mock(return_value=[{'success': True}]) self.mock_deb_mock_instance.build_chain = Mock(return_value=[{'success': True}]) self.mock_deb_mock_instance.get_cache_stats = Mock(return_value={}) self.mock_deb_mock_instance.cleanup_caches = Mock(return_value={}) self.client = MockAPIClient(self.config) def tearDown(self): import shutil shutil.rmtree(self.temp_dir, ignore_errors=True) def test_client_initialization(self): """Test client initialization""" self.assertIsInstance(self.client, MockAPIClient) self.assertEqual(self.client.config, self.config) def test_create_environment(self): """Test environment creation""" env = self.client.create_environment("test-env", "amd64", "trixie", ["build-essential"]) self.assertIsInstance(env, MockEnvironment) self.assertEqual(env.name, "test-env") self.mock_deb_mock_instance.init_chroot.assert_called_once_with("test-env", "amd64", "trixie") self.mock_deb_mock_instance.install_packages.assert_called_once_with(["build-essential"]) def test_get_environment(self): """Test getting existing environment""" # Mock existing environment self.mock_chroot_manager.chroot_exists.return_value = True env = self.client.get_environment("test-env") self.assertIsInstance(env, MockEnvironment) self.assertEqual(env.name, "test-env") def test_get_nonexistent_environment(self): """Test getting non-existent environment""" self.mock_chroot_manager.chroot_exists.return_value = False with self.assertRaises(ValueError): self.client.get_environment("nonexistent-env") def test_list_environments(self): """Test listing environments""" self.mock_deb_mock_instance.list_chroots.return_value = ["env1", "env2"] environments = self.client.list_environments() self.assertEqual(environments, ["env1", "env2"]) def test_remove_environment(self): """Test removing environment""" self.client.remove_environment("test-env") self.mock_deb_mock_instance.clean_chroot.assert_called_once_with("test-env") def test_build_package(self): """Test building a package""" result = self.client.build_package("/path/to/package.dsc", "test-env") self.mock_deb_mock_instance.build.assert_called_once() self.assertEqual(result['success'], True) def test_build_parallel(self): """Test parallel building""" packages = ["/path/to/pkg1.dsc", "/path/to/pkg2.dsc"] results = self.client.build_parallel(packages, max_workers=2) self.mock_deb_mock_instance.build_parallel.assert_called_once_with(packages, 2) self.assertEqual(len(results), 1) self.assertEqual(results[0]['success'], True) def test_build_chain(self): """Test chain building""" packages = ["/path/to/pkg1.dsc", "/path/to/pkg2.dsc"] results = self.client.build_chain(packages) self.mock_deb_mock_instance.build_chain.assert_called_once_with(packages) self.assertEqual(len(results), 1) self.assertEqual(results[0]['success'], True) def test_environment_context_manager(self): """Test environment context manager""" # Mock existing environment self.mock_chroot_manager.chroot_exists.return_value = True with self.client.environment("test-env") as env: self.assertIsInstance(env, MockEnvironment) self.assertEqual(env.name, "test-env") self.assertTrue(env.is_active()) # Environment should be deactivated after context self.assertFalse(env.is_active()) class TestMockEnvironment(unittest.TestCase): """Test the MockEnvironment class""" def setUp(self): self.mock_deb_mock = Mock() self.mock_chroot_manager = Mock() self.mock_deb_mock.chroot_manager = self.mock_chroot_manager self.mock_chroot_manager.chroot_exists.return_value = True self.mock_chroot_manager.execute_in_chroot.return_value = Mock(returncode=0, stdout="test output") self.mock_chroot_manager.get_chroot_info.return_value = {'status': 'active'} self.env = MockEnvironment("test-env", self.mock_deb_mock) def test_environment_initialization(self): """Test environment initialization""" self.assertEqual(self.env.name, "test-env") self.assertFalse(self.env.is_active()) def test_activate_deactivate(self): """Test environment activation and deactivation""" self.env.activate() self.assertTrue(self.env.is_active()) self.env.deactivate() self.assertFalse(self.env.is_active()) def test_activate_nonexistent_environment(self): """Test activating non-existent environment""" self.mock_chroot_manager.chroot_exists.return_value = False with self.assertRaises(ChrootError): self.env.activate() def test_execute_command(self): """Test command execution""" self.env.activate() result = self.env.execute(["ls", "-la"]) self.mock_chroot_manager.execute_in_chroot.assert_called_once_with( "test-env", ["ls", "-la"], capture_output=True ) self.assertEqual(result.returncode, 0) def test_execute_command_string(self): """Test command execution with string command""" self.env.activate() result = self.env.execute("ls -la") self.mock_chroot_manager.execute_in_chroot.assert_called_once_with( "test-env", ["ls", "-la"], capture_output=True ) def test_execute_command_inactive(self): """Test executing command on inactive environment""" with self.assertRaises(RuntimeError): self.env.execute(["ls"]) def test_install_packages(self): """Test package installation""" self.env.activate() # Mock the return value properly self.mock_deb_mock.install_packages.return_value = {'success': True} result = self.env.install_packages(["build-essential"]) self.mock_deb_mock.install_packages.assert_called_once_with(["build-essential"]) self.assertEqual(result['success'], True) def test_copy_in(self): """Test copying files into environment""" self.env.activate() self.env.copy_in("/local/file", "/chroot/file") self.mock_chroot_manager.copy_to_chroot.assert_called_once_with( "/local/file", "/chroot/file", "test-env" ) def test_copy_out(self): """Test copying files out of environment""" self.env.activate() self.env.copy_out("/chroot/file", "/local/file") self.mock_chroot_manager.copy_from_chroot.assert_called_once_with( "/chroot/file", "/local/file", "test-env" ) def test_get_info(self): """Test getting environment info""" info = self.env.get_info() self.mock_chroot_manager.get_chroot_info.assert_called_once_with("test-env") self.assertEqual(info['status'], 'active') class TestEnvironmentManager(unittest.TestCase): """Test the EnvironmentManager class""" def setUp(self): self.temp_dir = tempfile.mkdtemp() self.config = Config( chroot_dir=self.temp_dir, output_dir=os.path.join(self.temp_dir, "output"), chroot_config_dir=os.path.join(self.temp_dir, "config") ) # Create necessary directories os.makedirs(self.config.chroot_config_dir, exist_ok=True) # Mock the DebMock class with patch('deb_mock.environment_manager.DebMock') as mock_deb_mock: self.mock_deb_mock_instance = Mock() mock_deb_mock.return_value = self.mock_deb_mock_instance # Mock chroot manager self.mock_chroot_manager = Mock() self.mock_deb_mock_instance.chroot_manager = self.mock_chroot_manager self.mock_chroot_manager.chroot_exists.return_value = False self.mock_chroot_manager.get_chroot_info.return_value = { 'name': 'test-env', 'status': 'active', 'size': 1024, 'created': None, 'modified': None } self.mock_chroot_manager.list_mounts.return_value = [] # Mock other methods self.mock_deb_mock_instance.init_chroot = Mock() self.mock_deb_mock_instance.install_packages = Mock(return_value={'success': True}) self.mock_deb_mock_instance.clean_chroot = Mock() self.mock_deb_mock_instance.list_chroots = Mock(return_value=[]) self.mock_deb_mock_instance.build = Mock(return_value={'success': True, 'artifacts': []}) self.mock_deb_mock_instance.update_chroot = Mock() self.manager = EnvironmentManager(self.config) def tearDown(self): import shutil shutil.rmtree(self.temp_dir, ignore_errors=True) def test_manager_initialization(self): """Test manager initialization""" self.assertIsInstance(self.manager, EnvironmentManager) self.assertEqual(self.manager.config, self.config) def test_create_environment(self): """Test environment creation""" # Mock environment exists to return False initially, then True after creation self.mock_chroot_manager.chroot_exists.side_effect = [False, True] info = self.manager.create_environment("test-env", "amd64", "trixie", ["build-essential"]) self.assertIsInstance(info, EnvironmentInfo) self.assertEqual(info.name, "test-env") self.assertEqual(info.architecture, "amd64") self.assertEqual(info.suite, "trixie") self.mock_deb_mock_instance.init_chroot.assert_called_once_with("test-env", "amd64", "trixie") def test_environment_exists(self): """Test checking if environment exists""" self.mock_chroot_manager.chroot_exists.return_value = True self.assertTrue(self.manager.environment_exists("test-env")) self.mock_chroot_manager.chroot_exists.return_value = False self.assertFalse(self.manager.environment_exists("test-env")) def test_get_environment_info(self): """Test getting environment info""" self.mock_chroot_manager.chroot_exists.return_value = True info = self.manager.get_environment_info("test-env") self.assertIsInstance(info, EnvironmentInfo) self.assertEqual(info.name, "test-env") def test_get_nonexistent_environment_info(self): """Test getting info for non-existent environment""" self.mock_chroot_manager.chroot_exists.return_value = False with self.assertRaises(ValueError): self.manager.get_environment_info("nonexistent-env") def test_list_environments(self): """Test listing environments""" self.mock_deb_mock_instance.list_chroots.return_value = ["env1", "env2"] self.mock_chroot_manager.chroot_exists.return_value = True environments = self.manager.list_environments() self.assertEqual(len(environments), 2) self.assertIsInstance(environments[0], EnvironmentInfo) def test_remove_environment(self): """Test removing environment""" self.mock_chroot_manager.chroot_exists.return_value = True self.manager.remove_environment("test-env") self.mock_deb_mock_instance.clean_chroot.assert_called_once_with("test-env") def test_execute_command(self): """Test command execution""" self.mock_chroot_manager.chroot_exists.return_value = True self.mock_chroot_manager.execute_in_chroot.return_value = Mock(returncode=0, stdout="test output") result = self.manager.execute_command("test-env", ["ls", "-la"]) self.mock_chroot_manager.execute_in_chroot.assert_called_once_with( "test-env", ["ls", "-la"], capture_output=True ) self.assertEqual(result.returncode, 0) def test_install_packages(self): """Test package installation""" self.mock_chroot_manager.chroot_exists.return_value = True result = self.manager.install_packages("test-env", ["build-essential"]) self.mock_deb_mock_instance.install_packages.assert_called_once_with(["build-essential"]) self.assertEqual(result['success'], True) def test_build_package(self): """Test package building""" self.mock_chroot_manager.chroot_exists.return_value = True result = self.manager.build_package("test-env", "/path/to/package.dsc") self.mock_deb_mock_instance.build.assert_called_once() self.assertIsInstance(result, BuildResult) self.assertTrue(result.success) class TestIntegration(unittest.TestCase): """Integration tests for the API""" def test_create_client_with_config_builder(self): """Test creating client with config builder""" config = (MockConfigBuilder() .environment("test-env") .architecture("amd64") .suite("trixie") .build()) # Mock the DebMock class to avoid sbuild dependency with patch('deb_mock.api.DebMock') as mock_deb_mock: mock_instance = Mock() mock_deb_mock.return_value = mock_instance client = MockAPIClient(config) self.assertIsInstance(client, MockAPIClient) self.assertEqual(client.config.chroot_name, "test-env") def test_quick_build_function(self): """Test the quick_build convenience function""" from deb_mock.api import quick_build with patch('deb_mock.api.MockAPIClient') as mock_client_class: mock_client = Mock() mock_client.build_package.return_value = {'success': True} mock_client_class.return_value = mock_client result = quick_build("/path/to/package.dsc") self.assertEqual(result['success'], True) mock_client.build_package.assert_called_once() if __name__ == '__main__': unittest.main()