mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	fixes
This commit is contained in:
		| @@ -852,3 +852,118 @@ async def test_add_includes_overwrites_existing_files( | ||||
|     mock_copy_file_if_changed.assert_called_once_with( | ||||
|         str(include_file), str(Path(CORE.build_path) / "src" / "header.h") | ||||
|     ) | ||||
|  | ||||
|  | ||||
| # Tests for skip_external_update functionality | ||||
|  | ||||
|  | ||||
| @patch("esphome.yaml_util.load_yaml") | ||||
| @patch("esphome.components.packages.do_packages_pass") | ||||
| @patch("esphome.components.external_components.do_external_components_pass") | ||||
| def test_validate_config_skip_update_true( | ||||
|     mock_ext_pass: MagicMock, mock_pkg_pass: MagicMock, mock_load_yaml: MagicMock | ||||
| ) -> None: | ||||
|     """Test that validate_config propagates skip_update=True.""" | ||||
|     from esphome.config import validate_config | ||||
|     from esphome.const import CONF_EXTERNAL_COMPONENTS, CONF_PACKAGES | ||||
|  | ||||
|     config_dict: dict[str, Any] = { | ||||
|         CONF_ESPHOME: {CONF_NAME: "test"}, | ||||
|         CONF_PACKAGES: {"test": {}}, | ||||
|         CONF_EXTERNAL_COMPONENTS: [{}], | ||||
|     } | ||||
|  | ||||
|     # Mock do_packages_pass to return config unchanged | ||||
|     mock_pkg_pass.side_effect = lambda c, **kwargs: c | ||||
|  | ||||
|     # Call validate_config with skip_external_update=True | ||||
|     validate_config(config_dict, {}, skip_external_update=True) | ||||
|  | ||||
|     # Verify both were called with skip_update=True | ||||
|     mock_pkg_pass.assert_called_once() | ||||
|     assert mock_pkg_pass.call_args.kwargs.get("skip_update") is True | ||||
|  | ||||
|     mock_ext_pass.assert_called_once() | ||||
|     assert mock_ext_pass.call_args.kwargs.get("skip_update") is True | ||||
|  | ||||
|  | ||||
| @patch("esphome.yaml_util.load_yaml") | ||||
| @patch("esphome.components.packages.do_packages_pass") | ||||
| @patch("esphome.components.external_components.do_external_components_pass") | ||||
| def test_validate_config_skip_update_false( | ||||
|     mock_ext_pass: MagicMock, mock_pkg_pass: MagicMock, mock_load_yaml: MagicMock | ||||
| ) -> None: | ||||
|     """Test that validate_config propagates skip_update=False.""" | ||||
|     from esphome.config import validate_config | ||||
|     from esphome.const import CONF_EXTERNAL_COMPONENTS, CONF_PACKAGES | ||||
|  | ||||
|     config_dict: dict[str, Any] = { | ||||
|         CONF_ESPHOME: {CONF_NAME: "test"}, | ||||
|         CONF_PACKAGES: {"test": {}}, | ||||
|         CONF_EXTERNAL_COMPONENTS: [{}], | ||||
|     } | ||||
|  | ||||
|     # Mock do_packages_pass to return config unchanged | ||||
|     mock_pkg_pass.side_effect = lambda c, **kwargs: c | ||||
|  | ||||
|     # Call validate_config with skip_external_update=False | ||||
|     validate_config(config_dict, {}, skip_external_update=False) | ||||
|  | ||||
|     # Verify both were called with skip_update=False | ||||
|     mock_pkg_pass.assert_called_once() | ||||
|     assert mock_pkg_pass.call_args.kwargs.get("skip_update") is False | ||||
|  | ||||
|     mock_ext_pass.assert_called_once() | ||||
|     assert mock_ext_pass.call_args.kwargs.get("skip_update") is False | ||||
|  | ||||
|  | ||||
| @patch("esphome.yaml_util.load_yaml") | ||||
| @patch("esphome.components.packages.do_packages_pass") | ||||
| @patch("esphome.components.external_components.do_external_components_pass") | ||||
| def test_validate_config_default_false( | ||||
|     mock_ext_pass: MagicMock, mock_pkg_pass: MagicMock, mock_load_yaml: MagicMock | ||||
| ) -> None: | ||||
|     """Test that validate_config defaults to skip_update=False.""" | ||||
|     from esphome.config import validate_config | ||||
|     from esphome.const import CONF_EXTERNAL_COMPONENTS, CONF_PACKAGES | ||||
|  | ||||
|     config_dict: dict[str, Any] = { | ||||
|         CONF_ESPHOME: {CONF_NAME: "test"}, | ||||
|         CONF_PACKAGES: {"test": {}}, | ||||
|         CONF_EXTERNAL_COMPONENTS: [{}], | ||||
|     } | ||||
|  | ||||
|     # Mock do_packages_pass to return config unchanged | ||||
|     mock_pkg_pass.side_effect = lambda c, **kwargs: c | ||||
|  | ||||
|     # Call validate_config without skip_external_update parameter | ||||
|     validate_config(config_dict, {}) | ||||
|  | ||||
|     # Verify both were called with skip_update=False (default) | ||||
|     mock_pkg_pass.assert_called_once() | ||||
|     assert mock_pkg_pass.call_args.kwargs.get("skip_update") is False | ||||
|  | ||||
|     mock_ext_pass.assert_called_once() | ||||
|     assert mock_ext_pass.call_args.kwargs.get("skip_update") is False | ||||
|  | ||||
|  | ||||
| @patch("esphome.config.load_config") | ||||
| def test_read_config_skip_update_parameter(mock_load_config: MagicMock) -> None: | ||||
|     """Test that read_config passes skip_external_update correctly.""" | ||||
|     from esphome.config import read_config | ||||
|  | ||||
|     # Setup | ||||
|     CORE.config_path = "test.yaml" | ||||
|     mock_load_config.return_value = MagicMock(errors=[]) | ||||
|  | ||||
|     # Test with skip_external_update=True | ||||
|     read_config({}, skip_external_update=True) | ||||
|     mock_load_config.assert_called_with({}, True) | ||||
|  | ||||
|     # Test with skip_external_update=False | ||||
|     read_config({}, skip_external_update=False) | ||||
|     mock_load_config.assert_called_with({}, False) | ||||
|  | ||||
|     # Test default (should be False) | ||||
|     read_config({}) | ||||
|     mock_load_config.assert_called_with({}, False) | ||||
|   | ||||
							
								
								
									
										147
									
								
								tests/unit_tests/test_git.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								tests/unit_tests/test_git.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| """Tests for git.py module.""" | ||||
|  | ||||
| from datetime import datetime, timedelta | ||||
| from unittest.mock import MagicMock, Mock, patch | ||||
|  | ||||
| from esphome import git | ||||
| from esphome.core import TimePeriodSeconds | ||||
|  | ||||
|  | ||||
| @patch("esphome.git.run_git_command") | ||||
| @patch("pathlib.Path.is_dir") | ||||
| def test_clone_or_update_with_none_refresh_no_update( | ||||
|     mock_is_dir: MagicMock, mock_run_git: MagicMock | ||||
| ) -> None: | ||||
|     """Test that refresh=None skips updates for existing repos.""" | ||||
|     # Setup - repo already exists | ||||
|     mock_is_dir.return_value = True | ||||
|  | ||||
|     # Mock file timestamps | ||||
|     with patch("pathlib.Path.exists") as mock_exists: | ||||
|         mock_exists.return_value = True | ||||
|         with patch("pathlib.Path.stat") as mock_stat: | ||||
|             mock_stat_result = Mock() | ||||
|             mock_stat_result.st_mtime = datetime.now().timestamp() | ||||
|             mock_stat.return_value = mock_stat_result | ||||
|  | ||||
|             # Call with refresh=None | ||||
|             repo_dir, revert = git.clone_or_update( | ||||
|                 url="https://github.com/test/repo", | ||||
|                 ref=None, | ||||
|                 refresh=None, | ||||
|                 domain="test", | ||||
|             ) | ||||
|  | ||||
|             # Should NOT call git fetch or any update commands | ||||
|             mock_run_git.assert_not_called() | ||||
|             assert revert is None | ||||
|  | ||||
|  | ||||
| @patch("esphome.git.run_git_command") | ||||
| @patch("pathlib.Path.is_dir") | ||||
| def test_clone_or_update_with_refresh_updates_old_repo( | ||||
|     mock_is_dir: MagicMock, mock_run_git: MagicMock | ||||
| ) -> None: | ||||
|     """Test that refresh triggers update for old repos.""" | ||||
|     # Setup - repo already exists | ||||
|     mock_is_dir.return_value = True | ||||
|     mock_run_git.return_value = "abc123"  # mock SHA | ||||
|  | ||||
|     # Mock file timestamps - 2 days old | ||||
|     with patch("pathlib.Path.exists") as mock_exists: | ||||
|         mock_exists.return_value = True | ||||
|         with patch("pathlib.Path.stat") as mock_stat: | ||||
|             mock_stat_result = Mock() | ||||
|             old_time = datetime.now() - timedelta(days=2) | ||||
|             mock_stat_result.st_mtime = old_time.timestamp() | ||||
|             mock_stat.return_value = mock_stat_result | ||||
|  | ||||
|             # Call with refresh=1d (1 day) | ||||
|             refresh = TimePeriodSeconds(days=1) | ||||
|             repo_dir, revert = git.clone_or_update( | ||||
|                 url="https://github.com/test/repo", | ||||
|                 ref=None, | ||||
|                 refresh=refresh, | ||||
|                 domain="test", | ||||
|             ) | ||||
|  | ||||
|             # Should call git fetch and update commands | ||||
|             assert mock_run_git.called | ||||
|             # Check for fetch command | ||||
|             fetch_calls = [ | ||||
|                 call for call in mock_run_git.call_args_list if "fetch" in str(call) | ||||
|             ] | ||||
|             assert len(fetch_calls) > 0 | ||||
|  | ||||
|  | ||||
| @patch("esphome.git.run_git_command") | ||||
| @patch("pathlib.Path.is_dir") | ||||
| def test_clone_or_update_with_refresh_skips_fresh_repo( | ||||
|     mock_is_dir: MagicMock, mock_run_git: MagicMock | ||||
| ) -> None: | ||||
|     """Test that refresh doesn't update fresh repos.""" | ||||
|     # Setup - repo already exists | ||||
|     mock_is_dir.return_value = True | ||||
|  | ||||
|     # Mock file timestamps - 1 hour old | ||||
|     with patch("pathlib.Path.exists") as mock_exists: | ||||
|         mock_exists.return_value = True | ||||
|         with patch("pathlib.Path.stat") as mock_stat: | ||||
|             mock_stat_result = Mock() | ||||
|             recent_time = datetime.now() - timedelta(hours=1) | ||||
|             mock_stat_result.st_mtime = recent_time.timestamp() | ||||
|             mock_stat.return_value = mock_stat_result | ||||
|  | ||||
|             # Call with refresh=1d (1 day) | ||||
|             refresh = TimePeriodSeconds(days=1) | ||||
|             repo_dir, revert = git.clone_or_update( | ||||
|                 url="https://github.com/test/repo", | ||||
|                 ref=None, | ||||
|                 refresh=refresh, | ||||
|                 domain="test", | ||||
|             ) | ||||
|  | ||||
|             # Should NOT call git fetch since repo is fresh | ||||
|             mock_run_git.assert_not_called() | ||||
|             assert revert is None | ||||
|  | ||||
|  | ||||
| @patch("esphome.git.run_git_command") | ||||
| @patch("pathlib.Path.is_dir") | ||||
| def test_clone_or_update_clones_missing_repo( | ||||
|     mock_is_dir: MagicMock, mock_run_git: MagicMock | ||||
| ) -> None: | ||||
|     """Test that missing repos are cloned regardless of refresh setting.""" | ||||
|     # Setup - repo doesn't exist | ||||
|     mock_is_dir.return_value = False | ||||
|  | ||||
|     # Test with refresh=None | ||||
|     repo_dir, revert = git.clone_or_update( | ||||
|         url="https://github.com/test/repo", | ||||
|         ref=None, | ||||
|         refresh=None, | ||||
|         domain="test", | ||||
|     ) | ||||
|  | ||||
|     # Should call git clone | ||||
|     assert mock_run_git.called | ||||
|     clone_calls = [call for call in mock_run_git.call_args_list if "clone" in str(call)] | ||||
|     assert len(clone_calls) > 0 | ||||
|  | ||||
|     # Reset mock | ||||
|     mock_run_git.reset_mock() | ||||
|  | ||||
|     # Test with refresh=1d | ||||
|     mock_is_dir.return_value = False | ||||
|     refresh = TimePeriodSeconds(days=1) | ||||
|     repo_dir, revert = git.clone_or_update( | ||||
|         url="https://github.com/test/repo2", | ||||
|         ref=None, | ||||
|         refresh=refresh, | ||||
|         domain="test", | ||||
|     ) | ||||
|  | ||||
|     # Should still call git clone | ||||
|     assert mock_run_git.called | ||||
|     clone_calls = [call for call in mock_run_git.call_args_list if "clone" in str(call)] | ||||
|     assert len(clone_calls) > 0 | ||||
		Reference in New Issue
	
	Block a user