def test_validate_platform_config(self):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({
            'hello': str,
        })
        platform_schema_base = PLATFORM_SCHEMA_BASE.extend({})
        loader.set_component(
            self.hass, 'platform_conf',
            MockModule('platform_conf',
                       platform_schema_base=platform_schema_base))

        loader.set_component(
            self.hass, 'platform_conf.whatever',
            MockPlatform('whatever', platform_schema=platform_schema))

        with assert_setup_component(0):
            assert setup.setup_component(
                self.hass, 'platform_conf', {
                    'platform_conf': {
                        'platform': 'whatever',
                        'hello': 'world',
                        'invalid': 'extra',
                    }
                })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert setup.setup_component(
                self.hass, 'platform_conf', {
                    'platform_conf': {
                        'platform': 'whatever',
                        'hello': 'world',
                    },
                    'platform_conf 2': {
                        'platform': 'whatever',
                        'invalid': True
                    }
                })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(0):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'not_existing',
                    'hello': 'world',
                }
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'whatever',
                    'hello': 'world',
                }
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': [{
                    'platform': 'whatever',
                    'hello': 'world',
                }]
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        # Any falsey platform config will be ignored (None, {}, etc)
        with assert_setup_component(0) as config:
            assert setup.setup_component(self.hass, 'platform_conf',
                                         {'platform_conf': None})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty

            assert setup.setup_component(self.hass, 'platform_conf',
                                         {'platform_conf': {}})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty
async def test_two_step_flow(opp, client, enable_custom_integrations):
    """Test we can finish a two step flow."""
    mock_integration(
        opp, MockModule("test", async_setup_entry=AsyncMock(return_value=True))
    )
    mock_entity_platform(opp, "config_flow.test", None)

    class TestFlow(core_ce.ConfigFlow):
        VERSION = 1

        async def async_step_user(self, user_input=None):
            return self.async_show_form(
                step_id="account", data_schema=vol.Schema({"user_title": str})
            )

        async def async_step_account(self, user_input=None):
            return self.async_create_entry(
                title=user_input["user_title"], data={"secret": "account_token"}
            )

    with patch.dict(HANDLERS, {"test": TestFlow}):
        resp = await client.post(
            "/api/config/config_entries/flow", json={"handler": "test"}
        )
        assert resp.status == 200
        data = await resp.json()
        flow_id = data.pop("flow_id")
        assert data == {
            "type": "form",
            "handler": "test",
            "step_id": "account",
            "data_schema": [{"name": "user_title", "type": "string"}],
            "description_placeholders": None,
            "errors": None,
            "last_step": None,
        }

    with patch.dict(HANDLERS, {"test": TestFlow}):
        resp = await client.post(
            f"/api/config/config_entries/flow/{flow_id}",
            json={"user_title": "user-title"},
        )
        assert resp.status == 200

        entries = opp.config_entries.async_entries("test")
        assert len(entries) == 1

        data = await resp.json()
        data.pop("flow_id")
        assert data == {
            "handler": "test",
            "type": "create_entry",
            "title": "user-title",
            "version": 1,
            "result": {
                "disabled_by": None,
                "domain": "test",
                "entry_id": entries[0].entry_id,
                "source": core_ce.SOURCE_USER,
                "state": core_ce.ConfigEntryState.LOADED.value,
                "supports_options": False,
                "supports_unload": False,
                "pref_disable_new_entities": False,
                "pref_disable_polling": False,
                "title": "user-title",
                "reason": None,
            },
            "description": None,
            "description_placeholders": None,
            "options": {},
        }
Exemple #3
0
async def test_setup_frontend_before_recorder(hass):
    """Test frontend is setup before recorder."""
    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="normal_integration",
            async_setup=gen_domain_setup("normal_integration"),
            partial_manifest={"after_dependencies": ["an_after_dep"]},
        ),
    )
    mock_integration(
        hass,
        MockModule(
            domain="an_after_dep",
            async_setup=gen_domain_setup("an_after_dep"),
        ),
    )
    mock_integration(
        hass,
        MockModule(
            domain="frontend",
            async_setup=gen_domain_setup("frontend"),
            partial_manifest={
                "dependencies": ["http"],
                "after_dependencies": ["an_after_dep"],
            },
        ),
    )
    mock_integration(
        hass,
        MockModule(
            domain="http",
            async_setup=gen_domain_setup("http"),
        ),
    )
    mock_integration(
        hass,
        MockModule(
            domain="recorder",
            async_setup=gen_domain_setup("recorder"),
        ),
    )

    await bootstrap._async_set_up_integrations(
        hass,
        {
            "frontend": {},
            "http": {},
            "recorder": {},
            "normal_integration": {},
            "an_after_dep": {},
        },
    )

    assert "frontend" in hass.config.components
    assert "normal_integration" in hass.config.components
    assert "recorder" in hass.config.components
    assert order == [
        "http",
        "frontend",
        "recorder",
        "an_after_dep",
        "normal_integration",
    ]
Exemple #4
0
    def test_validate_platform_config(self, caplog):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({"hello": str})
        platform_schema_base = PLATFORM_SCHEMA_BASE.extend({})
        mock_integration(
            self.opp,
            MockModule("platform_conf",
                       platform_schema_base=platform_schema_base),
        )
        mock_entity_platform(
            self.opp,
            "platform_conf.whatever",
            MockPlatform(platform_schema=platform_schema),
        )

        with assert_setup_component(0):
            assert setup.setup_component(
                self.opp,
                "platform_conf",
                {
                    "platform_conf": {
                        "platform": "not_existing",
                        "hello": "world"
                    }
                },
            )

        self.opp.data.pop(setup.DATA_SETUP)
        self.opp.config.components.remove("platform_conf")

        with assert_setup_component(1):
            assert setup.setup_component(
                self.opp,
                "platform_conf",
                {"platform_conf": {
                    "platform": "whatever",
                    "hello": "world"
                }},
            )

        self.opp.data.pop(setup.DATA_SETUP)
        self.opp.config.components.remove("platform_conf")

        with assert_setup_component(1):
            assert setup.setup_component(
                self.opp,
                "platform_conf",
                {
                    "platform_conf": [{
                        "platform": "whatever",
                        "hello": "world"
                    }]
                },
            )

        self.opp.data.pop(setup.DATA_SETUP)
        self.opp.config.components.remove("platform_conf")

        # Any falsey platform config will be ignored (None, {}, etc)
        with assert_setup_component(0) as config:
            assert setup.setup_component(self.opp, "platform_conf",
                                         {"platform_conf": None})
            assert "platform_conf" in self.opp.config.components
            assert not config["platform_conf"]  # empty

            assert setup.setup_component(self.opp, "platform_conf",
                                         {"platform_conf": {}})
            assert "platform_conf" in self.opp.config.components
            assert not config["platform_conf"]  # empty
async def test_get_integration_with_requirements_pip_install_fails_two_passes(
        hass):
    """Check getting an integration with loaded requirements and the pip install fails two passes."""
    hass.config.skip_pip = False
    mock_integration(
        hass,
        MockModule("test_component_dep",
                   requirements=["test-comp-dep==1.0.0"]))
    mock_integration(
        hass,
        MockModule("test_component_after_dep",
                   requirements=["test-comp-after-dep==1.0.0"]),
    )
    mock_integration(
        hass,
        MockModule(
            "test_component",
            requirements=["test-comp==1.0.0"],
            dependencies=["test_component_dep"],
            partial_manifest={
                "after_dependencies": ["test_component_after_dep"]
            },
        ),
    )

    def _mock_install_package(package, **kwargs):
        if package == "test-comp==1.0.0":
            return True
        return False

    # 1st pass
    with pytest.raises(RequirementsNotFound), patch(
            "homeassistant.util.package.is_installed",
            return_value=False) as mock_is_installed, patch(
                "homeassistant.util.package.install_package",
                side_effect=_mock_install_package) as mock_inst:

        integration = await async_get_integration_with_requirements(
            hass, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 3
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp-after-dep==1.0.0",
                      "test-comp-dep==1.0.0",
                      "test-comp==1.0.0",
                  ]

    assert len(mock_inst.mock_calls) == 7
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp-after-dep==1.0.0",
        "test-comp-after-dep==1.0.0",
        "test-comp-after-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp==1.0.0",
    ]

    # 2nd pass
    with pytest.raises(RequirementsNotFound), patch(
            "homeassistant.util.package.is_installed",
            return_value=False) as mock_is_installed, patch(
                "homeassistant.util.package.install_package",
                side_effect=_mock_install_package) as mock_inst:

        integration = await async_get_integration_with_requirements(
            hass, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 1
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp==1.0.0",
                  ]

    # On another attempt we remember failures and don't try again
    assert len(mock_inst.mock_calls) == 1
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp==1.0.0"
    ]

    # Now clear the history and so we try again
    async_clear_install_history(hass)

    with pytest.raises(RequirementsNotFound), patch(
            "homeassistant.util.package.is_installed",
            return_value=False) as mock_is_installed, patch(
                "homeassistant.util.package.install_package",
                side_effect=_mock_install_package) as mock_inst:

        integration = await async_get_integration_with_requirements(
            hass, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 3
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp-after-dep==1.0.0",
                      "test-comp-dep==1.0.0",
                      "test-comp==1.0.0",
                  ]

    assert len(mock_inst.mock_calls) == 7
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp-after-dep==1.0.0",
        "test-comp-after-dep==1.0.0",
        "test-comp-after-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp==1.0.0",
    ]

    # Now clear the history and mock success
    async_clear_install_history(hass)

    with patch("homeassistant.util.package.is_installed",
               return_value=False) as mock_is_installed, patch(
                   "homeassistant.util.package.install_package",
                   return_value=True) as mock_inst:

        integration = await async_get_integration_with_requirements(
            hass, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 3
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp-after-dep==1.0.0",
                      "test-comp-dep==1.0.0",
                      "test-comp==1.0.0",
                  ]

    assert len(mock_inst.mock_calls) == 3
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp-after-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp==1.0.0",
    ]
def mock_test_component():
    """Ensure a component called 'test' exists."""
    set_component('test', MockModule('test'))
Exemple #7
0
async def test_options_flow_with_invalid_data(hass, client):
    """Test an options flow with invalid_data."""
    mock_integration(
        hass, MockModule("test",
                         async_setup_entry=AsyncMock(return_value=True)))

    class TestFlow(core_ce.ConfigFlow):
        @staticmethod
        @callback
        def async_get_options_flow(config_entry):
            class OptionsFlowHandler(data_entry_flow.FlowHandler):
                async def async_step_init(self, user_input=None):
                    return self.async_show_form(
                        step_id="finish",
                        data_schema=vol.Schema({
                            vol.Required("choices",
                                         default=["invalid", "valid"]):
                            cv.multi_select({"valid": "Valid"})
                        }),
                    )

                async def async_step_finish(self, user_input=None):
                    return self.async_create_entry(title="Enable disable",
                                                   data=user_input)

            return OptionsFlowHandler()

    MockConfigEntry(
        domain="test",
        entry_id="test1",
        source="bla",
    ).add_to_hass(hass)
    entry = hass.config_entries.async_entries()[0]

    with patch.dict(HANDLERS, {"test": TestFlow}):
        url = "/api/config/config_entries/options/flow"
        resp = await client.post(url, json={"handler": entry.entry_id})

        assert resp.status == HTTPStatus.OK
        data = await resp.json()
        flow_id = data.pop("flow_id")
        assert data == {
            "type":
            "form",
            "handler":
            "test1",
            "step_id":
            "finish",
            "data_schema": [{
                "default": ["invalid", "valid"],
                "name": "choices",
                "options": {
                    "valid": "Valid"
                },
                "required": True,
                "type": "multi_select",
            }],
            "description_placeholders":
            None,
            "errors":
            None,
            "last_step":
            None,
        }

    with patch.dict(HANDLERS, {"test": TestFlow}):
        resp = await client.post(
            f"/api/config/config_entries/options/flow/{flow_id}",
            json={"choices": ["valid", "invalid"]},
        )
        assert resp.status == HTTPStatus.BAD_REQUEST
        data = await resp.json()
        assert data == {
            "message":
            "User input malformed: invalid is not a valid option for "
            "dictionary value @ data['choices']"
        }
def mock_test_component(hass):
    """Ensure a component called 'test' exists."""
    mock_integration(hass, MockModule("test"))
async def test_get_integration_with_requirements_pip_install_fails_two_passes(
        opp):
    """Check getting an integration with loaded requirements and the pip install fails two passes."""
    opp.config.skip_pip = False
    mock_integration(
        opp,
        MockModule("test_component_dep",
                   requirements=["test-comp-dep==1.0.0"]))
    mock_integration(
        opp,
        MockModule("test_component_after_dep",
                   requirements=["test-comp-after-dep==1.0.0"]),
    )
    mock_integration(
        opp,
        MockModule(
            "test_component",
            requirements=["test-comp==1.0.0"],
            dependencies=["test_component_dep"],
            partial_manifest={
                "after_dependencies": ["test_component_after_dep"]
            },
        ),
    )

    def _mock_install_package(package, **kwargs):
        if package == "test-comp==1.0.0":
            return True
        return False

    # 1st pass
    with pytest.raises(RequirementsNotFound), patch(
            "openpeerpower.util.package.is_installed",
            return_value=False) as mock_is_installed, patch(
                "openpeerpower.util.package.install_package",
                side_effect=_mock_install_package) as mock_inst:

        integration = await async_get_integration_with_requirements(
            opp, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 3
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp-after-dep==1.0.0",
                      "test-comp-dep==1.0.0",
                      "test-comp==1.0.0",
                  ]

    assert len(mock_inst.mock_calls) == 3
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp-after-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp==1.0.0",
    ]

    # 2nd pass
    with pytest.raises(RequirementsNotFound), patch(
            "openpeerpower.util.package.is_installed",
            return_value=False) as mock_is_installed, patch(
                "openpeerpower.util.package.install_package",
                side_effect=_mock_install_package) as mock_inst:

        integration = await async_get_integration_with_requirements(
            opp, "test_component")
        assert integration
        assert integration.domain == "test_component"

    assert len(mock_is_installed.mock_calls) == 3
    assert sorted(mock_call[1][0]
                  for mock_call in mock_is_installed.mock_calls) == [
                      "test-comp-after-dep==1.0.0",
                      "test-comp-dep==1.0.0",
                      "test-comp==1.0.0",
                  ]

    assert len(mock_inst.mock_calls) == 3
    assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [
        "test-comp-after-dep==1.0.0",
        "test-comp-dep==1.0.0",
        "test-comp==1.0.0",
    ]
Exemple #10
0
async def test_get_entries(hass, client, clear_handlers):
    """Test get entries."""
    mock_integration(hass, MockModule("comp1"))
    mock_integration(
        hass,
        MockModule("comp2", partial_manifest={"integration_type": "helper"}))
    mock_integration(hass, MockModule("comp3"))

    @HANDLERS.register("comp1")
    class Comp1ConfigFlow:
        """Config flow with options flow."""
        @staticmethod
        @callback
        def async_get_options_flow(config_entry):
            """Get options flow."""
            pass

        @classmethod
        @callback
        def async_supports_options_flow(cls, config_entry):
            """Return options flow support for this handler."""
            return True

    hass.helpers.config_entry_flow.register_discovery_flow(
        "comp2", "Comp 2", lambda: None)

    entry = MockConfigEntry(
        domain="comp1",
        title="Test 1",
        source="bla",
    )
    entry.supports_unload = True
    entry.add_to_hass(hass)
    MockConfigEntry(
        domain="comp2",
        title="Test 2",
        source="bla2",
        state=core_ce.ConfigEntryState.SETUP_ERROR,
        reason="Unsupported API",
    ).add_to_hass(hass)
    MockConfigEntry(
        domain="comp3",
        title="Test 3",
        source="bla3",
        disabled_by=core_ce.ConfigEntryDisabler.USER,
    ).add_to_hass(hass)

    resp = await client.get("/api/config/config_entries/entry")
    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    for entry in data:
        entry.pop("entry_id")
    assert data == [
        {
            "domain": "comp1",
            "title": "Test 1",
            "source": "bla",
            "state": core_ce.ConfigEntryState.NOT_LOADED.value,
            "supports_options": True,
            "supports_remove_device": False,
            "supports_unload": True,
            "pref_disable_new_entities": False,
            "pref_disable_polling": False,
            "disabled_by": None,
            "reason": None,
        },
        {
            "domain": "comp2",
            "title": "Test 2",
            "source": "bla2",
            "state": core_ce.ConfigEntryState.SETUP_ERROR.value,
            "supports_options": False,
            "supports_remove_device": False,
            "supports_unload": False,
            "pref_disable_new_entities": False,
            "pref_disable_polling": False,
            "disabled_by": None,
            "reason": "Unsupported API",
        },
        {
            "domain": "comp3",
            "title": "Test 3",
            "source": "bla3",
            "state": core_ce.ConfigEntryState.NOT_LOADED.value,
            "supports_options": False,
            "supports_remove_device": False,
            "supports_unload": False,
            "pref_disable_new_entities": False,
            "pref_disable_polling": False,
            "disabled_by": core_ce.ConfigEntryDisabler.USER,
            "reason": None,
        },
    ]

    resp = await client.get("/api/config/config_entries/entry?domain=comp3")
    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    assert len(data) == 1
    assert data[0]["domain"] == "comp3"

    resp = await client.get(
        "/api/config/config_entries/entry?domain=comp3&type=helper")
    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    assert len(data) == 0

    resp = await client.get(
        "/api/config/config_entries/entry?domain=comp3&type=integration")
    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    assert len(data) == 1

    resp = await client.get("/api/config/config_entries/entry?type=integration"
                            )
    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    assert len(data) == 2
    assert data[0]["domain"] == "comp1"
    assert data[1]["domain"] == "comp3"
async def test_component_failing_setup(hass):
    """Test component that fails setup."""
    mock_integration(hass, MockModule("comp", setup=lambda hass, config: False))

    assert not await setup.async_setup_component(hass, "comp", {})
    assert "comp" not in hass.config.components
Exemple #12
0
async def test_remove_config_entry_from_device_fails(hass, hass_ws_client):
    """Test removing config entry from device failing cases."""
    assert await async_setup_component(hass, "config", {})
    ws_client = await hass_ws_client(hass)
    device_registry = mock_device_registry(hass)

    async def async_remove_config_entry_device(hass, config_entry,
                                               device_entry):
        return True

    mock_integration(
        hass,
        MockModule("comp1"),
    )
    mock_integration(
        hass,
        MockModule(
            "comp2",
            async_remove_config_entry_device=async_remove_config_entry_device),
    )

    entry_1 = MockConfigEntry(
        domain="comp1",
        title="Test 1",
        source="bla",
    )
    entry_1.add_to_hass(hass)

    entry_2 = MockConfigEntry(
        domain="comp2",
        title="Test 1",
        source="bla",
    )
    entry_2.supports_remove_device = True
    entry_2.add_to_hass(hass)

    entry_3 = MockConfigEntry(
        domain="comp3",
        title="Test 1",
        source="bla",
    )
    entry_3.supports_remove_device = True
    entry_3.add_to_hass(hass)

    device_registry.async_get_or_create(
        config_entry_id=entry_1.entry_id,
        connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )
    device_registry.async_get_or_create(
        config_entry_id=entry_2.entry_id,
        connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )
    device_entry = device_registry.async_get_or_create(
        config_entry_id=entry_3.entry_id,
        connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )
    assert device_entry.config_entries == {
        entry_1.entry_id,
        entry_2.entry_id,
        entry_3.entry_id,
    }

    fake_entry_id = "abc123"
    assert entry_1.entry_id != fake_entry_id
    fake_device_id = "abc123"
    assert device_entry.id != fake_device_id

    # Try removing a non existing config entry from the device
    await ws_client.send_json({
        "id": 5,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": fake_entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"
    assert response["error"]["message"] == "Unknown config entry"

    # Try removing a config entry which does not support removal from the device
    await ws_client.send_json({
        "id": 6,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_1.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"
    assert (response["error"]["message"] ==
            "Config entry does not support device removal")

    # Try removing a config entry from a device which does not exist
    await ws_client.send_json({
        "id": 7,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_2.entry_id,
        "device_id": fake_device_id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"
    assert response["error"]["message"] == "Unknown device"

    # Try removing a config entry from a device which it's not connected to
    await ws_client.send_json({
        "id": 8,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_2.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert response["success"]
    assert set(response["result"]["config_entries"]) == {
        entry_1.entry_id,
        entry_3.entry_id,
    }

    await ws_client.send_json({
        "id": 9,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_2.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"
    assert response["error"]["message"] == "Config entry not in device"

    # Try removing a config entry which can't be loaded from a device - allowed
    await ws_client.send_json({
        "id": 10,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_3.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"
    assert response["error"]["message"] == "Integration not found"
Exemple #13
0
async def test_remove_config_entry_from_device(hass, hass_ws_client):
    """Test removing config entry from device."""
    assert await async_setup_component(hass, "config", {})
    ws_client = await hass_ws_client(hass)
    device_registry = mock_device_registry(hass)

    can_remove = False

    async def async_remove_config_entry_device(hass, config_entry,
                                               device_entry):
        return can_remove

    mock_integration(
        hass,
        MockModule(
            "comp1",
            async_remove_config_entry_device=async_remove_config_entry_device),
    )
    mock_integration(
        hass,
        MockModule(
            "comp2",
            async_remove_config_entry_device=async_remove_config_entry_device),
    )

    entry_1 = MockConfigEntry(
        domain="comp1",
        title="Test 1",
        source="bla",
    )
    entry_1.supports_remove_device = True
    entry_1.add_to_hass(hass)

    entry_2 = MockConfigEntry(
        domain="comp1",
        title="Test 1",
        source="bla",
    )
    entry_2.supports_remove_device = True
    entry_2.add_to_hass(hass)

    device_registry.async_get_or_create(
        config_entry_id=entry_1.entry_id,
        connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )
    device_entry = device_registry.async_get_or_create(
        config_entry_id=entry_2.entry_id,
        connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )
    assert device_entry.config_entries == {entry_1.entry_id, entry_2.entry_id}

    # Try removing a config entry from the device, it should fail because
    # async_remove_config_entry_device returns False
    await ws_client.send_json({
        "id": 5,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_1.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert not response["success"]
    assert response["error"]["code"] == "unknown_error"

    # Make async_remove_config_entry_device return True
    can_remove = True

    # Remove the 1st config entry
    await ws_client.send_json({
        "id": 6,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_1.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert response["success"]
    assert response["result"]["config_entries"] == [entry_2.entry_id]

    # Check that the config entry was removed from the device
    assert device_registry.async_get(
        device_entry.id).config_entries == {entry_2.entry_id}

    # Remove the 2nd config entry
    await ws_client.send_json({
        "id": 7,
        "type": "config/device_registry/remove_config_entry",
        "config_entry_id": entry_2.entry_id,
        "device_id": device_entry.id,
    })
    response = await ws_client.receive_json()

    assert response["success"]
    assert response["result"] is None

    # This was the last config entry, the device is removed
    assert not device_registry.async_get(device_entry.id)
async def test_two_step_options_flow(opp, client):
    """Test we can finish a two step options flow."""
    mock_integration(
        opp, MockModule("test", async_setup_entry=AsyncMock(return_value=True))
    )

    class TestFlow(core_ce.ConfigFlow):
        @staticmethod
        @callback
        def async_get_options_flow(config_entry):
            class OptionsFlowHandler(data_entry_flow.FlowHandler):
                async def async_step_init(self, user_input=None):
                    return self.async_show_form(
                        step_id="finish", data_schema=vol.Schema({"enabled": bool})
                    )

                async def async_step_finish(self, user_input=None):
                    return self.async_create_entry(
                        title="Enable disable", data=user_input
                    )

            return OptionsFlowHandler()

    MockConfigEntry(
        domain="test",
        entry_id="test1",
        source="bla",
    ).add_to_opp(opp)
    entry = opp.config_entries.async_entries()[0]

    with patch.dict(HANDLERS, {"test": TestFlow}):
        url = "/api/config/config_entries/options/flow"
        resp = await client.post(url, json={"handler": entry.entry_id})

        assert resp.status == 200
        data = await resp.json()
        flow_id = data.pop("flow_id")
        assert data == {
            "type": "form",
            "handler": "test1",
            "step_id": "finish",
            "data_schema": [{"name": "enabled", "type": "boolean"}],
            "description_placeholders": None,
            "errors": None,
            "last_step": None,
        }

    with patch.dict(HANDLERS, {"test": TestFlow}):
        resp = await client.post(
            f"/api/config/config_entries/options/flow/{flow_id}",
            json={"enabled": True},
        )
        assert resp.status == 200
        data = await resp.json()
        data.pop("flow_id")
        assert data == {
            "handler": "test1",
            "type": "create_entry",
            "title": "Enable disable",
            "version": 1,
            "description": None,
            "description_placeholders": None,
        }
Exemple #15
0
async def test_two_step_options_flow(hass, client):
    """Test we can finish a two step options flow."""
    mock_integration(
        hass, MockModule('test', async_setup_entry=mock_coro_func(True)))

    class TestFlow(core_ce.ConfigFlow):
        @staticmethod
        @callback
        def async_get_options_flow(config, options):
            class OptionsFlowHandler(data_entry_flow.FlowHandler):
                def __init__(self, config, options):
                    self.config = config
                    self.options = options

                async def async_step_init(self, user_input=None):
                    return self.async_show_form(step_id='finish',
                                                data_schema=vol.Schema(
                                                    {'enabled': bool}))

                async def async_step_finish(self, user_input=None):
                    return self.async_create_entry(title='Enable disable',
                                                   data=user_input)

            return OptionsFlowHandler(config, options)

    MockConfigEntry(
        domain='test',
        entry_id='test1',
        source='bla',
        connection_class=core_ce.CONN_CLASS_LOCAL_POLL,
    ).add_to_hass(hass)
    entry = hass.config_entries._entries[0]

    with patch.dict(HANDLERS, {'test': TestFlow}):
        url = '/api/config/config_entries/entry/option/flow'
        resp = await client.post(url, json={'handler': entry.entry_id})

        assert resp.status == 200
        data = await resp.json()
        flow_id = data.pop('flow_id')
        assert data == {
            'type': 'form',
            'handler': 'test1',
            'step_id': 'finish',
            'data_schema': [{
                'name': 'enabled',
                'type': 'boolean'
            }],
            'description_placeholders': None,
            'errors': None
        }

    with patch.dict(HANDLERS, {'test': TestFlow}):
        resp = await client.post(
            '/api/config/config_entries/options/flow/{}'.format(flow_id),
            json={'enabled': True})
        assert resp.status == 200
        data = await resp.json()
        data.pop('flow_id')
        assert data == {
            'handler': 'test1',
            'type': 'create_entry',
            'title': 'Enable disable',
            'version': 1,
            'description': None,
            'description_placeholders': None,
        }
Exemple #16
0
async def test_remove_entry(hass, manager):
    """Test that we can remove an entry."""
    async def mock_setup_entry(hass, entry):
        """Mock setting up entry."""
        hass.async_create_task(hass.config_entries.async_forward_entry_setup(
            entry, 'light'))
        return True

    async def mock_unload_entry(hass, entry):
        """Mock unloading an entry."""
        result = await hass.config_entries.async_forward_entry_unload(
            entry, 'light')
        assert result
        return result

    mock_remove_entry = MagicMock(
        side_effect=lambda *args, **kwargs: mock_coro())

    entity = MockEntity(
        unique_id='1234',
        name='Test Entity',
    )

    async def mock_setup_entry_platform(hass, entry, async_add_entities):
        """Mock setting up platform."""
        async_add_entities([entity])

    mock_integration(hass, MockModule(
        'test',
        async_setup_entry=mock_setup_entry,
        async_unload_entry=mock_unload_entry,
        async_remove_entry=mock_remove_entry
    ))
    mock_entity_platform(
        hass, 'light.test',
        MockPlatform(async_setup_entry=mock_setup_entry_platform))
    mock_entity_platform(hass, 'config_flow.test', None)

    MockConfigEntry(
        domain='test_other', entry_id='test1'
    ).add_to_manager(manager)
    entry = MockConfigEntry(
        domain='test',
        entry_id='test2',
    )
    entry.add_to_manager(manager)
    MockConfigEntry(
        domain='test_other', entry_id='test3'
    ).add_to_manager(manager)

    # Check all config entries exist
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test2', 'test3']

    # Setup entry
    await entry.async_setup(hass)
    await hass.async_block_till_done()

    # Check entity state got added
    assert hass.states.get('light.test_entity') is not None
    # Group all_lights, light.test_entity
    assert len(hass.states.async_all()) == 2

    # Check entity got added to entity registry
    ent_reg = await hass.helpers.entity_registry.async_get_registry()
    assert len(ent_reg.entities) == 1
    entity_entry = list(ent_reg.entities.values())[0]
    assert entity_entry.config_entry_id == entry.entry_id

    # Remove entry
    result = await manager.async_remove('test2')
    await hass.async_block_till_done()

    # Check that unload went well and so no need to restart
    assert result == {
        'require_restart': False
    }

    # Check the remove callback was invoked.
    assert mock_remove_entry.call_count == 1

    # Check that config entry was removed.
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test3']

    # Check that entity state has been removed
    assert hass.states.get('light.test_entity') is None
    # Just Group all_lights
    assert len(hass.states.async_all()) == 1

    # Check that entity registry entry has been removed
    entity_entry_list = list(ent_reg.entities.values())
    assert not entity_entry_list
Exemple #17
0
def test_saving_and_loading(hass):
    """Test that we're saving and loading correctly."""
    loader.set_component(hass, 'test', MockModule('test'))

    class TestFlow(data_entry_flow.FlowHandler):
        VERSION = 5

        @asyncio.coroutine
        def async_step_init(self, user_input=None):
            return self.async_create_entry(
                title='Test Title',
                data={
                    'token': 'abcd'
                }
            )

    with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
        yield from hass.config_entries.flow.async_init('test')

    class Test2Flow(data_entry_flow.FlowHandler):
        VERSION = 3

        @asyncio.coroutine
        def async_step_init(self, user_input=None):
            return self.async_create_entry(
                title='Test 2 Title',
                data={
                    'username': '******'
                }
            )

    json_path = 'homeassistant.util.json.open'

    with patch('homeassistant.config_entries.HANDLERS.get',
               return_value=Test2Flow), \
            patch.object(config_entries, 'SAVE_DELAY', 0):
        yield from hass.config_entries.flow.async_init('test')

    with patch(json_path, mock_open(), create=True) as mock_write:
        # To trigger the call_later
        yield from asyncio.sleep(0, loop=hass.loop)
        # To execute the save
        yield from hass.async_block_till_done()

    # Mock open calls are: open file, context enter, write, context leave
    written = mock_write.mock_calls[2][1][0]

    # Now load written data in new config manager
    manager = config_entries.ConfigEntries(hass, {})

    with patch('os.path.isfile', return_value=True), \
            patch(json_path, mock_open(read_data=written), create=True):
        yield from manager.async_load()

    # Ensure same order
    for orig, loaded in zip(hass.config_entries.async_entries(),
                            manager.async_entries()):
        assert orig.version == loaded.version
        assert orig.domain == loaded.domain
        assert orig.title == loaded.title
        assert orig.data == loaded.data
        assert orig.source == loaded.source
async def test_two_step_options_flow(hass, client):
    """Test we can finish a two step options flow."""
    mock_integration(
        hass, MockModule("test", async_setup_entry=mock_coro_func(True)))

    class TestFlow(core_ce.ConfigFlow):
        @staticmethod
        @callback
        def async_get_options_flow(config, options):
            class OptionsFlowHandler(data_entry_flow.FlowHandler):
                def __init__(self, config, options):
                    self.config = config
                    self.options = options

                async def async_step_init(self, user_input=None):
                    return self.async_show_form(step_id="finish",
                                                data_schema=vol.Schema(
                                                    {"enabled": bool}))

                async def async_step_finish(self, user_input=None):
                    return self.async_create_entry(title="Enable disable",
                                                   data=user_input)

            return OptionsFlowHandler(config, options)

    MockConfigEntry(
        domain="test",
        entry_id="test1",
        source="bla",
        connection_class=core_ce.CONN_CLASS_LOCAL_POLL,
    ).add_to_hass(hass)
    entry = hass.config_entries._entries[0]

    with patch.dict(HANDLERS, {"test": TestFlow}):
        url = "/api/config/config_entries/entry/option/flow"
        resp = await client.post(url, json={"handler": entry.entry_id})

        assert resp.status == 200
        data = await resp.json()
        flow_id = data.pop("flow_id")
        assert data == {
            "type": "form",
            "handler": "test1",
            "step_id": "finish",
            "data_schema": [{
                "name": "enabled",
                "type": "boolean"
            }],
            "description_placeholders": None,
            "errors": None,
        }

    with patch.dict(HANDLERS, {"test": TestFlow}):
        resp = await client.post(
            "/api/config/config_entries/options/flow/{}".format(flow_id),
            json={"enabled": True},
        )
        assert resp.status == 200
        data = await resp.json()
        data.pop("flow_id")
        assert data == {
            "handler": "test1",
            "type": "create_entry",
            "title": "Enable disable",
            "version": 1,
            "description": None,
            "description_placeholders": None,
        }
    def test_validate_platform_config(self):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({
            'hello': str,
        })
        loader.set_component(
            'platform_conf',
            MockModule('platform_conf', platform_schema=platform_schema))

        loader.set_component(
            'platform_conf.whatever', MockPlatform('whatever'))

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'hello': 'world',
                'invalid': 'extra',
            }
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'whatever',
                'hello': 'world',
            },

            'platform_conf 2': {
                'invalid': True
            }
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'not_existing',
                'hello': 'world',
            }
        })

        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'whatever',
                'hello': 'world',
            }
        })

        self.hass.config.components.remove('platform_conf')

        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': [{
                'platform': 'whatever',
                'hello': 'world',
            }]
        })

        self.hass.config.components.remove('platform_conf')

        # Any falsey paltform config will be ignored (None, {}, etc)
        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': None
        })

        self.hass.config.components.remove('platform_conf')

        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {}
        })