"""Test the bootstrapping.""" # pylint: disable=protected-access import logging import os from unittest.mock import Mock from asynctest import patch import pytest from homeassistant import bootstrap import homeassistant.config as config_util from homeassistant.exceptions import HomeAssistantError import homeassistant.util.dt as dt_util from tests.common import ( MockConfigEntry, MockModule, flush_store, get_test_config_dir, mock_coro, mock_integration, ) ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE VERSION_PATH = os.path.join(get_test_config_dir(), config_util.VERSION_FILE) _LOGGER = logging.getLogger(__name__) @patch("homeassistant.bootstrap.async_enable_logging", Mock()) async def test_home_assistant_core_config_validation(hass): """Test if we pass in wrong information for HA conf.""" # Extensive HA conf validation testing is done result = await bootstrap.async_from_config_dict( {"homeassistant": {"latitude": "some string"}}, hass ) assert result is None async def test_load_hassio(hass): """Test that we load Hass.io component.""" with patch.dict(os.environ, {}, clear=True): assert bootstrap._get_domains(hass, {}) == set() with patch.dict(os.environ, {"HASSIO": "1"}): assert bootstrap._get_domains(hass, {}) == {"hassio"} async def test_empty_setup(hass): """Test an empty set up loads the core.""" await bootstrap.async_from_config_dict({}, hass) for domain in bootstrap.CORE_INTEGRATIONS: assert domain in hass.config.components, domain async def test_core_failure_loads_safe_mode(hass, caplog): """Test failing core setup aborts further setup.""" with patch( "homeassistant.components.homeassistant.async_setup", return_value=mock_coro(False), ): await bootstrap.async_from_config_dict({"group": {}}, hass) assert "core failed to initialize" in caplog.text # We aborted early, group not set up assert "group" not in hass.config.components async def test_setting_up_config(hass, caplog): """Test we set up domains in config.""" await bootstrap._async_set_up_integrations( hass, {"group hello": {}, "homeassistant": {}} ) assert "group" in hass.config.components async def test_setup_after_deps_all_present(hass, caplog): """Test after_dependencies when all present.""" caplog.set_level(logging.DEBUG) order = [] def gen_domain_setup(domain): async def async_setup(hass, config): order.append(domain) return True return async_setup mock_integration( hass, MockModule(domain="root", async_setup=gen_domain_setup("root")) ) mock_integration( hass, MockModule( domain="first_dep", async_setup=gen_domain_setup("first_dep"), partial_manifest={"after_dependencies": ["root"]}, ), ) mock_integration( hass, MockModule( domain="second_dep", async_setup=gen_domain_setup("second_dep"), partial_manifest={"after_dependencies": ["first_dep"]}, ), ) await bootstrap._async_set_up_integrations( hass, {"root": {}, "first_dep": {}, "second_dep": {}} ) assert "root" in hass.config.components assert "first_dep" in hass.config.components assert "second_dep" in hass.config.components assert order == ["root", "first_dep", "second_dep"] async def test_setup_after_deps_not_trigger_load(hass, caplog): """Test after_dependencies does not trigger loading it.""" caplog.set_level(logging.DEBUG) order = [] def gen_domain_setup(domain): async def async_setup(hass, config): order.append(domain) return True return async_setup mock_integration( hass, MockModule(domain="root", async_setup=gen_domain_setup("root")) ) mock_integration( hass, MockModule( domain="first_dep", async_setup=gen_domain_setup("first_dep"), partial_manifest={"after_dependencies": ["root"]}, ), ) mock_integration( hass, MockModule( domain="second_dep", async_setup=gen_domain_setup("second_dep"), partial_manifest={"after_dependencies": ["first_dep"]}, ), ) await bootstrap._async_set_up_integrations(hass, {"root": {}, "second_dep": {}}) assert "root" in hass.config.components assert "first_dep" not in hass.config.components assert "second_dep" in hass.config.components assert order == ["root", "second_dep"] async def test_setup_after_deps_not_present(hass, caplog): """Test after_dependencies when referenced integration doesn't exist.""" caplog.set_level(logging.DEBUG) order = [] def gen_domain_setup(domain): async def async_setup(hass, config): order.append(domain) return True return async_setup mock_integration( hass, MockModule(domain="root", async_setup=gen_domain_setup("root")) ) mock_integration( hass, MockModule( domain="second_dep", async_setup=gen_domain_setup("second_dep"), partial_manifest={"after_dependencies": ["first_dep"]}, ), ) await bootstrap._async_set_up_integrations( hass, {"root": {}, "first_dep": {}, "second_dep": {}} ) assert "root" in hass.config.components assert "first_dep" not in hass.config.components assert "second_dep" in hass.config.components assert order == ["root", "second_dep"] @pytest.fixture def mock_is_virtual_env(): """Mock enable logging.""" with patch( "homeassistant.bootstrap.is_virtual_env", return_value=False ) as is_virtual_env: yield is_virtual_env @pytest.fixture def mock_enable_logging(): """Mock enable logging.""" with patch("homeassistant.bootstrap.async_enable_logging") as enable_logging: yield enable_logging @pytest.fixture def mock_mount_local_lib_path(): """Mock enable logging.""" with patch( "homeassistant.bootstrap.async_mount_local_lib_path" ) as mount_local_lib_path: yield mount_local_lib_path @pytest.fixture def mock_process_ha_config_upgrade(): """Mock enable logging.""" with patch( "homeassistant.config.process_ha_config_upgrade" ) as process_ha_config_upgrade: yield process_ha_config_upgrade @pytest.fixture def mock_ensure_config_exists(): """Mock enable logging.""" with patch( "homeassistant.config.async_ensure_config_exists", return_value=True ) as ensure_config_exists: yield ensure_config_exists async def test_setup_hass( mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test it works.""" verbose = Mock() log_rotate_days = Mock() log_file = Mock() log_no_color = Mock() with patch( "homeassistant.config.async_hass_config_yaml", return_value={"browser": {}, "frontend": {}}, ): hass = await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=verbose, log_rotate_days=log_rotate_days, log_file=log_file, log_no_color=log_no_color, skip_pip=True, safe_mode=False, ) assert "browser" in hass.config.components assert "safe_mode" not in hass.config.components assert len(mock_enable_logging.mock_calls) == 1 assert mock_enable_logging.mock_calls[0][1] == ( hass, verbose, log_rotate_days, log_file, log_no_color, ) assert len(mock_mount_local_lib_path.mock_calls) == 1 assert len(mock_ensure_config_exists.mock_calls) == 1 assert len(mock_process_ha_config_upgrade.mock_calls) == 1 async def test_setup_hass_invalid_yaml( mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test it works.""" with patch( "homeassistant.config.async_hass_config_yaml", side_effect=HomeAssistantError ): hass = await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=False, log_rotate_days=10, log_file="", log_no_color=False, skip_pip=True, safe_mode=False, ) assert "safe_mode" in hass.config.components assert len(mock_mount_local_lib_path.mock_calls) == 0 async def test_setup_hass_config_dir_nonexistent( mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test it works.""" mock_ensure_config_exists.return_value = False assert ( await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=False, log_rotate_days=10, log_file="", log_no_color=False, skip_pip=True, safe_mode=False, ) is None ) async def test_setup_hass_safe_mode( hass, mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test it works.""" # Add a config entry to storage. MockConfigEntry(domain="browser").add_to_hass(hass) hass.config_entries._async_schedule_save() await flush_store(hass.config_entries._store) with patch("homeassistant.components.browser.setup") as browser_setup: hass = await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=False, log_rotate_days=10, log_file="", log_no_color=False, skip_pip=True, safe_mode=True, ) assert "safe_mode" in hass.config.components assert len(mock_mount_local_lib_path.mock_calls) == 0 # Validate we didn't try to set up config entry. assert "browser" not in hass.config.components assert len(browser_setup.mock_calls) == 0 async def test_setup_hass_invalid_core_config( mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test it works.""" with patch( "homeassistant.config.async_hass_config_yaml", return_value={"homeassistant": {"non-existing": 1}}, ): hass = await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=False, log_rotate_days=10, log_file="", log_no_color=False, skip_pip=True, safe_mode=False, ) assert "safe_mode" in hass.config.components async def test_setup_safe_mode_if_no_frontend( mock_enable_logging, mock_is_virtual_env, mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, ): """Test we setup safe mode if frontend didn't load.""" verbose = Mock() log_rotate_days = Mock() log_file = Mock() log_no_color = Mock() with patch( "homeassistant.config.async_hass_config_yaml", return_value={"map": {}, "person": {"invalid": True}}, ): hass = await bootstrap.async_setup_hass( config_dir=get_test_config_dir(), verbose=verbose, log_rotate_days=log_rotate_days, log_file=log_file, log_no_color=log_no_color, skip_pip=True, safe_mode=False, ) assert "safe_mode" in hass.config.components