"""Tests for the Update integration init."""
from __future__ import annotations

import asyncio
from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import Mock, patch

from aiohttp import ClientWebSocketResponse
import pytest

from homeassistant.components.update import (
    DOMAIN,
    IntegrationUpdateFailed,
    UpdateDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.common import mock_platform


async def setup_mock_domain(
    hass: HomeAssistant,
    async_list_updates: Callable[[HomeAssistant], Awaitable[list[UpdateDescription]]]
    | None = None,
    async_perform_update: Callable[[HomeAssistant, str, str], Awaitable[bool]]
    | None = None,
) -> None:
    """Set up a mock domain."""

    async def _mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        return [
            UpdateDescription(
                identifier="lorem_ipsum",
                name="Lorem Ipsum",
                current_version="1.0.0",
                available_version="1.0.1",
            )
        ]

    async def _mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs: Any,
    ) -> bool:
        return True

    mock_platform(
        hass,
        "some_domain.update",
        Mock(
            async_list_updates=async_list_updates or _mock_async_list_updates,
            async_perform_update=async_perform_update or _mock_async_perform_update,
        ),
    )

    assert await async_setup_component(hass, "some_domain", {})


async def gather_update_info(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> list[dict]:
    """Gather all info."""
    client = await hass_ws_client(hass)
    await client.send_json({"id": 1, "type": "update/info"})
    resp = await client.receive_json()
    return resp["result"]


async def test_update_updates(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test getting updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})

    with patch(
        "homeassistant.components.update.storage.Store.async_load",
        return_value={"skipped": []},
    ):
        data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    data = data[0] == {
        "domain": "some_domain",
        "identifier": "lorem_ipsum",
        "name": "Lorem Ipsum",
        "current_version": "1.0.0",
        "available_version": "1.0.1",
        "changelog_url": None,
        "icon_url": None,
    }


async def test_update_updates_with_timeout_error(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test timeout while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        raise asyncio.TimeoutError()

    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)

    assert await async_setup_component(hass, DOMAIN, {})

    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 0


async def test_update_updates_with_exception(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test exception while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        raise Exception()

    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 0


async def test_update_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test performing an update."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert resp["success"]


async def test_skip_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test skipping updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/skip",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert resp["success"]

    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_skip_non_existing_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test skipping non-existing updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/skip",
            "domain": "non_existing",
            "identifier": "non_existing",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]

    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1


async def test_update_update_non_existing(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": "does_not_exist",
            "identifier": "does_not_exist",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "not_found"


async def test_update_update_failed(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs,
    ) -> bool:
        raise IntegrationUpdateFailed("Test update failed")

    await setup_mock_domain(hass, async_perform_update=mock_async_perform_update)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "update_failed"
    assert resp["error"]["message"] == "Test update failed"


async def test_update_update_failed_generic(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs,
    ) -> bool:
        raise TypeError("Test update failed")

    await setup_mock_domain(hass, async_perform_update=mock_async_perform_update)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "update_failed"
    assert resp["error"]["message"] == "Unknown Error"
    assert "Test update failed" in caplog.text


async def test_update_before_info(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": "does_not_exist",
            "identifier": "does_not_exist",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "not_found"

29d749d0fe666b4f7277779db85a5fbb727f2a8e
<mask token>

<mask token>


async def setup_mock_domain(hass: HomeAssistant, async_list_updates: (
    Callable[[HomeAssistant], Awaitable[list[UpdateDescription]]] | None)=
    None, async_perform_update: (Callable[[HomeAssistant, str, str],
    Awaitable[bool]] | None)=None) ->None:
    """Set up a mock domain."""

    async def _mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        return [UpdateDescription(identifier='lorem_ipsum', name=
            'Lorem Ipsum', current_version='1.0.0', available_version='1.0.1')]

    async def _mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs: Any) ->bool:
        return True
    mock_platform(hass, 'some_domain.update', Mock(async_list_updates=
        async_list_updates or _mock_async_list_updates,
        async_perform_update=async_perform_update or
        _mock_async_perform_update))
    assert await async_setup_component(hass, 'some_domain', {})


async def gather_update_info(hass: HomeAssistant, hass_ws_client: Callable[
    [HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->list[dict]:
    """Gather all info."""
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/info'})
    resp = await client.receive_json()
    return resp['result']


async def test_update_updates(hass: HomeAssistant, hass_ws_client: Callable
    [[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test getting updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    with patch('homeassistant.components.update.storage.Store.async_load',
        return_value={'skipped': []}):
        data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    data = data[0] == {'domain': 'some_domain', 'identifier': 'lorem_ipsum',
        'name': 'Lorem Ipsum', 'current_version': '1.0.0',
        'available_version': '1.0.1', 'changelog_url': None, 'icon_url': None}


async def test_update_updates_with_timeout_error(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test timeout while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        raise asyncio.TimeoutError()
    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_update_updates_with_exception(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test exception while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        raise Exception()
    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_update_update(hass: HomeAssistant, hass_ws_client: Callable[
    [HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test performing an update."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert resp['success']


async def test_skip_update(hass: HomeAssistant, hass_ws_client: Callable[[
    HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test skipping updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/skip', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert resp['success']
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_skip_non_existing_update(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test skipping non-existing updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/skip', 'domain':
        'non_existing', 'identifier': 'non_existing', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1


async def test_update_update_non_existing(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        'does_not_exist', 'identifier': 'does_not_exist', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'not_found'


async def test_update_update_failed(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs) ->bool:
        raise IntegrationUpdateFailed('Test update failed')
    await setup_mock_domain(hass, async_perform_update=
        mock_async_perform_update)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'update_failed'
    assert resp['error']['message'] == 'Test update failed'


async def test_update_update_failed_generic(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]], caplog: pytest.LogCaptureFixture) ->None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs) ->bool:
        raise TypeError('Test update failed')
    await setup_mock_domain(hass, async_perform_update=
        mock_async_perform_update)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'update_failed'
    assert resp['error']['message'] == 'Unknown Error'
    assert 'Test update failed' in caplog.text


async def test_update_before_info(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        'does_not_exist', 'identifier': 'does_not_exist', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'not_found'

<mask token>
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import Mock, patch
from aiohttp import ClientWebSocketResponse
import pytest
from homeassistant.components.update import DOMAIN, IntegrationUpdateFailed, UpdateDescription
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import mock_platform


async def setup_mock_domain(hass: HomeAssistant, async_list_updates: (
    Callable[[HomeAssistant], Awaitable[list[UpdateDescription]]] | None)=
    None, async_perform_update: (Callable[[HomeAssistant, str, str],
    Awaitable[bool]] | None)=None) ->None:
    """Set up a mock domain."""

    async def _mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        return [UpdateDescription(identifier='lorem_ipsum', name=
            'Lorem Ipsum', current_version='1.0.0', available_version='1.0.1')]

    async def _mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs: Any) ->bool:
        return True
    mock_platform(hass, 'some_domain.update', Mock(async_list_updates=
        async_list_updates or _mock_async_list_updates,
        async_perform_update=async_perform_update or
        _mock_async_perform_update))
    assert await async_setup_component(hass, 'some_domain', {})


async def gather_update_info(hass: HomeAssistant, hass_ws_client: Callable[
    [HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->list[dict]:
    """Gather all info."""
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/info'})
    resp = await client.receive_json()
    return resp['result']


async def test_update_updates(hass: HomeAssistant, hass_ws_client: Callable
    [[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test getting updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    with patch('homeassistant.components.update.storage.Store.async_load',
        return_value={'skipped': []}):
        data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    data = data[0] == {'domain': 'some_domain', 'identifier': 'lorem_ipsum',
        'name': 'Lorem Ipsum', 'current_version': '1.0.0',
        'available_version': '1.0.1', 'changelog_url': None, 'icon_url': None}


async def test_update_updates_with_timeout_error(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test timeout while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        raise asyncio.TimeoutError()
    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_update_updates_with_exception(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test exception while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) ->list[
        UpdateDescription]:
        raise Exception()
    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_update_update(hass: HomeAssistant, hass_ws_client: Callable[
    [HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test performing an update."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert resp['success']


async def test_skip_update(hass: HomeAssistant, hass_ws_client: Callable[[
    HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test skipping updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/skip', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert resp['success']
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_skip_non_existing_update(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test skipping non-existing updates."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/skip', 'domain':
        'non_existing', 'identifier': 'non_existing', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1


async def test_update_update_non_existing(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]]) ->None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        'does_not_exist', 'identifier': 'does_not_exist', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'not_found'


async def test_update_update_failed(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs) ->bool:
        raise IntegrationUpdateFailed('Test update failed')
    await setup_mock_domain(hass, async_perform_update=
        mock_async_perform_update)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'update_failed'
    assert resp['error']['message'] == 'Test update failed'


async def test_update_update_failed_generic(hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[
    ClientWebSocketResponse]], caplog: pytest.LogCaptureFixture) ->None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(hass: HomeAssistant, identifier:
        str, version: str, **kwargs) ->bool:
        raise TypeError('Test update failed')
    await setup_mock_domain(hass, async_perform_update=
        mock_async_perform_update)
    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1
    update = data[0]
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        update['domain'], 'identifier': update['identifier'], 'version':
        update['available_version']})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'update_failed'
    assert resp['error']['message'] == 'Unknown Error'
    assert 'Test update failed' in caplog.text


async def test_update_before_info(hass: HomeAssistant, hass_ws_client:
    Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]]) ->None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)
    assert await async_setup_component(hass, DOMAIN, {})
    client = await hass_ws_client(hass)
    await client.send_json({'id': 1, 'type': 'update/update', 'domain':
        'does_not_exist', 'identifier': 'does_not_exist', 'version':
        'non_existing'})
    resp = await client.receive_json()
    assert not resp['success']
    assert resp['error']['code'] == 'not_found'

"""Tests for the Update integration init."""
from __future__ import annotations

import asyncio
from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import Mock, patch

from aiohttp import ClientWebSocketResponse
import pytest

from homeassistant.components.update import (
    DOMAIN,
    IntegrationUpdateFailed,
    UpdateDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.common import mock_platform


async def setup_mock_domain(
    hass: HomeAssistant,
    async_list_updates: Callable[[HomeAssistant], Awaitable[list[UpdateDescription]]]
    | None = None,
    async_perform_update: Callable[[HomeAssistant, str, str], Awaitable[bool]]
    | None = None,
) -> None:
    """Set up a mock domain."""

    async def _mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        return [
            UpdateDescription(
                identifier="lorem_ipsum",
                name="Lorem Ipsum",
                current_version="1.0.0",
                available_version="1.0.1",
            )
        ]

    async def _mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs: Any,
    ) -> bool:
        return True

    mock_platform(
        hass,
        "some_domain.update",
        Mock(
            async_list_updates=async_list_updates or _mock_async_list_updates,
            async_perform_update=async_perform_update or _mock_async_perform_update,
        ),
    )

    assert await async_setup_component(hass, "some_domain", {})


async def gather_update_info(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> list[dict]:
    """Gather all info."""
    client = await hass_ws_client(hass)
    await client.send_json({"id": 1, "type": "update/info"})
    resp = await client.receive_json()
    return resp["result"]


async def test_update_updates(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test getting updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})

    with patch(
        "homeassistant.components.update.storage.Store.async_load",
        return_value={"skipped": []},
    ):
        data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    data = data[0] == {
        "domain": "some_domain",
        "identifier": "lorem_ipsum",
        "name": "Lorem Ipsum",
        "current_version": "1.0.0",
        "available_version": "1.0.1",
        "changelog_url": None,
        "icon_url": None,
    }


async def test_update_updates_with_timeout_error(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test timeout while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        raise asyncio.TimeoutError()

    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)

    assert await async_setup_component(hass, DOMAIN, {})

    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 0


async def test_update_updates_with_exception(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test exception while getting updates."""

    async def mock_async_list_updates(hass: HomeAssistant) -> list[UpdateDescription]:
        raise Exception()

    await setup_mock_domain(hass, async_list_updates=mock_async_list_updates)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 0


async def test_update_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test performing an update."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert resp["success"]


async def test_skip_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test skipping updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/skip",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert resp["success"]

    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 0


async def test_skip_non_existing_update(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test skipping non-existing updates."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/skip",
            "domain": "non_existing",
            "identifier": "non_existing",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]

    data = await gather_update_info(hass, hass_ws_client)
    assert len(data) == 1


async def test_update_update_non_existing(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": "does_not_exist",
            "identifier": "does_not_exist",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "not_found"


async def test_update_update_failed(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs,
    ) -> bool:
        raise IntegrationUpdateFailed("Test update failed")

    await setup_mock_domain(hass, async_perform_update=mock_async_perform_update)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "update_failed"
    assert resp["error"]["message"] == "Test update failed"


async def test_update_update_failed_generic(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test that we correctly handle failed updates."""

    async def mock_async_perform_update(
        hass: HomeAssistant,
        identifier: str,
        version: str,
        **kwargs,
    ) -> bool:
        raise TypeError("Test update failed")

    await setup_mock_domain(hass, async_perform_update=mock_async_perform_update)

    assert await async_setup_component(hass, DOMAIN, {})
    data = await gather_update_info(hass, hass_ws_client)

    assert len(data) == 1
    update = data[0]

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": update["domain"],
            "identifier": update["identifier"],
            "version": update["available_version"],
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "update_failed"
    assert resp["error"]["message"] == "Unknown Error"
    assert "Test update failed" in caplog.text


async def test_update_before_info(
    hass: HomeAssistant,
    hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]],
) -> None:
    """Test that we fail when trying to update something that does not exist."""
    await setup_mock_domain(hass)

    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)
    await client.send_json(
        {
            "id": 1,
            "type": "update/update",
            "domain": "does_not_exist",
            "identifier": "does_not_exist",
            "version": "non_existing",
        }
    )
    resp = await client.receive_json()
    assert not resp["success"]
    assert resp["error"]["code"] == "not_found"
