Resolution center MVP (#74243)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Erik Montnemery 2022-07-08 05:49:07 +02:00 committed by GitHub
parent 405d323709
commit 0e3f7bc63a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 935 additions and 0 deletions

View File

@ -191,6 +191,7 @@ homeassistant.components.recollect_waste.*
homeassistant.components.recorder.*
homeassistant.components.remote.*
homeassistant.components.renault.*
homeassistant.components.resolution_center.*
homeassistant.components.ridwell.*
homeassistant.components.rituals_perfume_genie.*
homeassistant.components.roku.*

View File

@ -857,6 +857,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/renault/ @epenet
/tests/components/renault/ @epenet
/homeassistant/components/repetier/ @MTrab @ShadowBr0ther
/homeassistant/components/resolution_center/ @home-assistant/core
/tests/components/resolution_center/ @home-assistant/core
/homeassistant/components/rflink/ @javicalle
/tests/components/rflink/ @javicalle
/homeassistant/components/rfxtrx/ @danielhiversen @elupus @RobBie1221

View File

@ -0,0 +1,20 @@
"""The resolution center integration."""
from __future__ import annotations
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from . import websocket_api
from .const import DOMAIN
from .issue_handler import async_create_issue, async_delete_issue
from .issue_registry import async_load as async_load_issue_registry
__all__ = ["DOMAIN", "async_create_issue", "async_delete_issue"]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Resolution Center."""
websocket_api.async_setup(hass)
await async_load_issue_registry(hass)
return True

View File

@ -0,0 +1,3 @@
"""Constants for the Resolution Center integration."""
DOMAIN = "resolution_center"

View File

@ -0,0 +1,62 @@
"""The resolution center integration."""
from __future__ import annotations
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
from homeassistant.core import HomeAssistant, callback
from .issue_registry import async_get as async_get_issue_registry
from .models import IssueSeverity
@callback
def async_create_issue(
hass: HomeAssistant,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
learn_more_url: str | None = None,
severity: IssueSeverity,
translation_key: str,
translation_placeholders: dict[str, str] | None = None,
) -> None:
"""Create an issue, or replace an existing one."""
# Verify the breaks_in_ha_version is a valid version string
if breaks_in_ha_version:
AwesomeVersion(
breaks_in_ha_version,
ensure_strategy=AwesomeVersionStrategy.CALVER,
find_first_match=False,
)
issue_registry = async_get_issue_registry(hass)
issue_registry.async_get_or_create(
domain,
issue_id,
breaks_in_ha_version=breaks_in_ha_version,
learn_more_url=learn_more_url,
severity=severity,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
@callback
def async_delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None:
"""Delete an issue.
It is not an error to delete an issue that does not exist.
"""
issue_registry = async_get_issue_registry(hass)
issue_registry.async_delete(domain, issue_id)
@callback
def async_dismiss_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None:
"""Dismiss an issue.
Will raise if the issue does not exist.
"""
issue_registry = async_get_issue_registry(hass)
issue_registry.async_dismiss(domain, issue_id)

View File

@ -0,0 +1,164 @@
"""Persistently store issues raised by integrations."""
from __future__ import annotations
import dataclasses
from typing import cast
from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.storage import Store
from .models import IssueSeverity
DATA_REGISTRY = "issue_registry"
STORAGE_KEY = "resolution_center.issue_registry"
STORAGE_VERSION = 1
SAVE_DELAY = 10
SAVED_FIELDS = ("dismissed_version", "domain", "issue_id")
@dataclasses.dataclass(frozen=True)
class IssueEntry:
"""Issue Registry Entry."""
active: bool
breaks_in_ha_version: str | None
dismissed_version: str | None
domain: str
issue_id: str
learn_more_url: str | None
severity: IssueSeverity | None
translation_key: str | None
translation_placeholders: dict[str, str] | None
class IssueRegistry:
"""Class to hold a registry of issues."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the issue registry."""
self.hass = hass
self.issues: dict[tuple[str, str], IssueEntry] = {}
self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY, atomic_writes=True)
@callback
def async_get_issue(self, domain: str, issue_id: str) -> IssueEntry | None:
"""Get issue by id."""
return self.issues.get((domain, issue_id))
@callback
def async_get_or_create(
self,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
learn_more_url: str | None = None,
severity: IssueSeverity,
translation_key: str,
translation_placeholders: dict[str, str] | None = None,
) -> IssueEntry:
"""Get issue. Create if it doesn't exist."""
if (issue := self.async_get_issue(domain, issue_id)) is None:
issue = IssueEntry(
active=True,
breaks_in_ha_version=breaks_in_ha_version,
dismissed_version=None,
domain=domain,
issue_id=issue_id,
learn_more_url=learn_more_url,
severity=severity,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
self.issues[(domain, issue_id)] = issue
self.async_schedule_save()
else:
issue = self.issues[(domain, issue_id)] = dataclasses.replace(
issue,
active=True,
breaks_in_ha_version=breaks_in_ha_version,
learn_more_url=learn_more_url,
severity=severity,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
return issue
@callback
def async_delete(self, domain: str, issue_id: str) -> None:
"""Delete issue."""
if self.issues.pop((domain, issue_id), None) is None:
return
self.async_schedule_save()
@callback
def async_dismiss(self, domain: str, issue_id: str) -> IssueEntry:
"""Dismiss issue."""
old = self.issues[(domain, issue_id)]
if old.dismissed_version == ha_version:
return old
issue = self.issues[(domain, issue_id)] = dataclasses.replace(
old,
dismissed_version=ha_version,
)
self.async_schedule_save()
return issue
async def async_load(self) -> None:
"""Load the issue registry."""
data = await self._store.async_load()
issues: dict[tuple[str, str], IssueEntry] = {}
if isinstance(data, dict):
for issue in data["issues"]:
issues[(issue["domain"], issue["issue_id"])] = IssueEntry(
active=False,
breaks_in_ha_version=None,
dismissed_version=issue["dismissed_version"],
domain=issue["domain"],
issue_id=issue["issue_id"],
learn_more_url=None,
severity=None,
translation_key=None,
translation_placeholders=None,
)
self.issues = issues
@callback
def async_schedule_save(self) -> None:
"""Schedule saving the issue registry."""
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
@callback
def _data_to_save(self) -> dict[str, list[dict[str, str | None]]]:
"""Return data of issue registry to store in a file."""
data = {}
data["issues"] = [
{field: getattr(entry, field) for field in SAVED_FIELDS}
for entry in self.issues.values()
]
return data
@callback
def async_get(hass: HomeAssistant) -> IssueRegistry:
"""Get issue registry."""
return cast(IssueRegistry, hass.data[DATA_REGISTRY])
async def async_load(hass: HomeAssistant) -> None:
"""Load issue registry."""
assert DATA_REGISTRY not in hass.data
hass.data[DATA_REGISTRY] = IssueRegistry(hass)
await hass.data[DATA_REGISTRY].async_load()

View File

@ -0,0 +1,7 @@
{
"domain": "resolution_center",
"name": "Resolution Center",
"config_flow": false,
"documentation": "https://www.home-assistant.io/integrations/resolution_center",
"codeowners": ["@home-assistant/core"]
}

View File

@ -0,0 +1,12 @@
"""Models for Resolution Center."""
from __future__ import annotations
from homeassistant.backports.enum import StrEnum
class IssueSeverity(StrEnum):
"""Issue severity."""
CRITICAL = "critical"
ERROR = "error"
WARNING = "warning"

View File

@ -0,0 +1,62 @@
"""The resolution center websocket API."""
from __future__ import annotations
import dataclasses
from typing import Any
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from .issue_handler import async_dismiss_issue
from .issue_registry import async_get as async_get_issue_registry
@callback
def async_setup(hass: HomeAssistant) -> None:
"""Set up the resolution center websocket API."""
websocket_api.async_register_command(hass, ws_dismiss_issue)
websocket_api.async_register_command(hass, ws_list_issues)
@callback
@websocket_api.websocket_command(
{
vol.Required("type"): "resolution_center/dismiss_issue",
vol.Required("domain"): str,
vol.Required("issue_id"): str,
}
)
def ws_dismiss_issue(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
"""Fix an issue."""
async_dismiss_issue(hass, msg["domain"], msg["issue_id"])
connection.send_result(msg["id"])
@websocket_api.websocket_command(
{
vol.Required("type"): "resolution_center/list_issues",
}
)
@callback
def ws_list_issues(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
"""Return a list of issues."""
def ws_dict(kv_pairs: list[tuple[Any, Any]]) -> dict[Any, Any]:
result = {k: v for k, v in kv_pairs if k != "active"}
result["dismissed"] = result["dismissed_version"] is not None
return result
issue_registry = async_get_issue_registry(hass)
issues = [
dataclasses.asdict(issue, dict_factory=ws_dict)
for issue in issue_registry.issues.values()
]
connection.send_result(msg["id"], {"issues": issues})

View File

@ -1864,6 +1864,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.resolution_center.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.ridwell.*]
check_untyped_defs = true
disallow_incomplete_defs = true

View File

@ -80,6 +80,7 @@ NO_IOT_CLASS = [
"proxy",
"python_script",
"raspberry_pi",
"resolution_center",
"safe_mode",
"script",
"search",

View File

@ -0,0 +1 @@
"""Tests for the resolution center integration."""

View File

@ -0,0 +1,346 @@
"""Test the resolution center websocket API."""
import pytest
from homeassistant.components.resolution_center import (
async_create_issue,
async_delete_issue,
)
from homeassistant.components.resolution_center.const import DOMAIN
from homeassistant.components.resolution_center.issue_handler import async_dismiss_issue
from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
async def test_create_update_issue(hass: HomeAssistant, hass_ws_client) -> None:
"""Test creating and updating issues."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}
issues = [
{
"breaks_in_ha_version": "2022.9.0dev0",
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
{
"breaks_in_ha_version": "2022.8",
"domain": "test",
"issue_id": "issue_2",
"learn_more_url": "https://theuselessweb.com/abc",
"severity": "other",
"translation_key": "even_worse",
"translation_placeholders": {"def": "456"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 2, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
# Update an issue
async_create_issue(
hass,
issues[0]["domain"],
issues[0]["issue_id"],
breaks_in_ha_version=issues[0]["breaks_in_ha_version"],
learn_more_url="blablabla",
severity=issues[0]["severity"],
translation_key=issues[0]["translation_key"],
translation_placeholders=issues[0]["translation_placeholders"],
)
await client.send_json({"id": 3, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"]["issues"][0] == dict(
issues[0],
dismissed=False,
dismissed_version=None,
learn_more_url="blablabla",
)
@pytest.mark.parametrize("ha_version", ("2022.9.cat", "In the future: 2023.1.1"))
async def test_create_issue_invalid_version(
hass: HomeAssistant, hass_ws_client, ha_version
) -> None:
"""Test creating an issue with invalid breaks in version."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
issue = {
"breaks_in_ha_version": ha_version,
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
}
with pytest.raises(Exception):
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}
async def test_dismiss_issue(hass: HomeAssistant, hass_ws_client) -> None:
"""Test dismissing issues."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}
issues = [
{
"breaks_in_ha_version": "2022.9",
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 2, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
# Dismiss a non-existing issue
with pytest.raises(KeyError):
async_dismiss_issue(hass, issues[0]["domain"], "no_such_issue")
await client.send_json({"id": 3, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
# Dismiss an existing issue
async_dismiss_issue(hass, issues[0]["domain"], issues[0]["issue_id"])
await client.send_json({"id": 4, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=True,
dismissed_version=ha_version,
)
for issue in issues
]
}
# Dismiss the same issue again
async_dismiss_issue(hass, issues[0]["domain"], issues[0]["issue_id"])
await client.send_json({"id": 5, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=True,
dismissed_version=ha_version,
)
for issue in issues
]
}
# Update a dismissed issue
async_create_issue(
hass,
issues[0]["domain"],
issues[0]["issue_id"],
breaks_in_ha_version=issues[0]["breaks_in_ha_version"],
learn_more_url="blablabla",
severity=issues[0]["severity"],
translation_key=issues[0]["translation_key"],
translation_placeholders=issues[0]["translation_placeholders"],
)
await client.send_json({"id": 6, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"]["issues"][0] == dict(
issues[0],
dismissed=True,
dismissed_version=ha_version,
learn_more_url="blablabla",
)
async def test_delete_issue(hass: HomeAssistant, hass_ws_client) -> None:
"""Test we can delete an issue."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
issues = [
{
"breaks_in_ha_version": "2022.9",
"domain": "fake_integration",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
# Delete a non-existing issue
async_delete_issue(hass, issues[0]["domain"], "no_such_issue")
await client.send_json({"id": 2, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
# Delete an existing issue
async_delete_issue(hass, issues[0]["domain"], issues[0]["issue_id"])
await client.send_json({"id": 3, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}
# Delete the same issue again
async_delete_issue(hass, issues[0]["domain"], issues[0]["issue_id"])
await client.send_json({"id": 4, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}

View File

@ -0,0 +1,92 @@
"""Test the resolution center websocket API."""
from homeassistant.components.resolution_center import (
async_create_issue,
issue_registry,
)
from homeassistant.components.resolution_center.const import DOMAIN
from homeassistant.components.resolution_center.issue_handler import async_dismiss_issue
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import flush_store
async def test_load_issues(hass: HomeAssistant) -> None:
"""Make sure that we can load/save data correctly."""
assert await async_setup_component(hass, DOMAIN, {})
issues = [
{
"breaks_in_ha_version": "2022.9",
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
{
"breaks_in_ha_version": "2022.8",
"domain": "test",
"issue_id": "issue_2",
"learn_more_url": "https://theuselessweb.com/abc",
"severity": "other",
"translation_key": "even_worse",
"translation_placeholders": {"def": "456"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
async_dismiss_issue(hass, issues[0]["domain"], issues[0]["issue_id"])
registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY]
assert len(registry.issues) == 2
issue1 = registry.async_get_issue("test", "issue_1")
issue2 = registry.async_get_issue("test", "issue_2")
registry2 = issue_registry.IssueRegistry(hass)
await flush_store(registry._store)
await registry2.async_load()
assert list(registry.issues) == list(registry2.issues)
issue1_registry2 = registry2.async_get_issue("test", "issue_1")
assert issue1_registry2.dismissed_version == issue1.dismissed_version
issue2_registry2 = registry2.async_get_issue("test", "issue_2")
assert issue2_registry2.dismissed_version == issue2.dismissed_version
async def test_loading_issues_from_storage(hass: HomeAssistant, hass_storage) -> None:
"""Test loading stored issues on start."""
hass_storage[issue_registry.STORAGE_KEY] = {
"version": issue_registry.STORAGE_VERSION,
"data": {
"issues": [
{
"dismissed_version": "2022.7.0.dev0",
"domain": "test",
"issue_id": "issue_1",
},
{
"dismissed_version": None,
"domain": "test",
"issue_id": "issue_2",
},
]
},
}
assert await async_setup_component(hass, DOMAIN, {})
registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY]
assert len(registry.issues) == 2

View File

@ -0,0 +1,151 @@
"""Test the resolution center websocket API."""
from homeassistant.components.resolution_center import async_create_issue
from homeassistant.components.resolution_center.const import DOMAIN
from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
async def test_dismiss_issue(hass: HomeAssistant, hass_ws_client) -> None:
"""Test we can dismiss an issue."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
issues = [
{
"breaks_in_ha_version": "2022.9",
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}
await client.send_json(
{
"id": 2,
"type": "resolution_center/dismiss_issue",
"domain": "test",
"issue_id": "no_such_issue",
}
)
msg = await client.receive_json()
assert not msg["success"]
await client.send_json(
{
"id": 3,
"type": "resolution_center/dismiss_issue",
"domain": "test",
"issue_id": "issue_1",
}
)
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] is None
await client.send_json({"id": 4, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=True,
dismissed_version=ha_version,
)
for issue in issues
]
}
async def test_list_issues(hass: HomeAssistant, hass_ws_client) -> None:
"""Test we can list issues."""
assert await async_setup_component(hass, DOMAIN, {})
client = await hass_ws_client(hass)
await client.send_json({"id": 1, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {"issues": []}
issues = [
{
"breaks_in_ha_version": "2022.9",
"domain": "test",
"issue_id": "issue_1",
"learn_more_url": "https://theuselessweb.com",
"severity": "error",
"translation_key": "abc_123",
"translation_placeholders": {"abc": "123"},
},
{
"breaks_in_ha_version": "2022.8",
"domain": "test",
"issue_id": "issue_2",
"learn_more_url": "https://theuselessweb.com/abc",
"severity": "other",
"translation_key": "even_worse",
"translation_placeholders": {"def": "456"},
},
]
for issue in issues:
async_create_issue(
hass,
issue["domain"],
issue["issue_id"],
breaks_in_ha_version=issue["breaks_in_ha_version"],
learn_more_url=issue["learn_more_url"],
severity=issue["severity"],
translation_key=issue["translation_key"],
translation_placeholders=issue["translation_placeholders"],
)
await client.send_json({"id": 2, "type": "resolution_center/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
dict(
issue,
dismissed=False,
dismissed_version=None,
)
for issue in issues
]
}