Improve test coverage for nextcloud (#123148)

* add first data driven tests

* remove unused mock

* test unique_id migration

* test errors during setup

* test error during data update

* test update entity

* system_versionis always available

* make use of snapshot_platform helper

* use parametrize test for coordinator update errors

* apply suggestions

* don't touch internals on coordinator tests

* rework to use async_get_or_create instead of mock_registry
This commit is contained in:
Michael 2024-08-27 19:48:39 +02:00 committed by GitHub
parent e2d84f9a58
commit ea04269c49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 4855 additions and 40 deletions

View File

@ -32,12 +32,12 @@ class NextcloudUpdateSensor(NextcloudEntity, UpdateEntity):
"""Represents a Nextcloud update entity."""
@property
def installed_version(self) -> str | None:
def installed_version(self) -> str:
"""Version installed and in use."""
return self.coordinator.data.get("system_version")
return self.coordinator.data["system_version"]
@property
def latest_version(self) -> str | None:
def latest_version(self) -> str:
"""Latest version available for install."""
return self.coordinator.data.get(
"update_available_version", self.installed_version
@ -46,7 +46,5 @@ class NextcloudUpdateSensor(NextcloudEntity, UpdateEntity):
@property
def release_url(self) -> str | None:
"""URL to the full release notes of the latest version available."""
if self.latest_version:
ver = "-".join(self.latest_version.split(".")[:3])
return f"https://nextcloud.com/changelog/#{ver}"
return None
ver = "-".join(self.latest_version.split(".")[:3])
return f"https://nextcloud.com/changelog/#{ver}"

View File

@ -1 +1,38 @@
"""Tests for the Nextcloud integration."""
from unittest.mock import Mock, patch
from homeassistant.components.nextcloud.const import DOMAIN
from homeassistant.const import CONF_URL
from homeassistant.core import HomeAssistant
from .const import MOCKED_ENTRY_ID
from tests.common import MockConfigEntry
def mock_config_entry(config: dict) -> MockConfigEntry:
"""Return a mocked config entry."""
return MockConfigEntry(
domain=DOMAIN, title=config[CONF_URL], data=config, entry_id=MOCKED_ENTRY_ID
)
async def init_integration(
hass: HomeAssistant, config: dict, data: dict
) -> MockConfigEntry:
"""Set up the nextcloud integration."""
entry = mock_config_entry(config)
entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.nextcloud.NextcloudMonitor",
) as mock_nextcloud_monitor,
):
mock_nextcloud_monitor.update = Mock(return_value=True)
mock_nextcloud_monitor.return_value.data = data
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry

View File

@ -1,19 +1,11 @@
"""Fixtrues for the Nextcloud integration tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, patch
import pytest
@pytest.fixture
def mock_nextcloud_monitor() -> Mock:
"""Mock of NextcloudMonitor."""
return Mock(
update=Mock(return_value=True),
)
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""

View File

@ -0,0 +1,182 @@
"""Constants for nextcloud tests."""
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, CONF_VERIFY_SSL
MOCKED_ENTRY_ID = "1234567890abcdef"
VALID_CONFIG = {
CONF_URL: "https://my.nc_url.local",
CONF_USERNAME: "nc_user",
CONF_PASSWORD: "nc_pass",
CONF_VERIFY_SSL: True,
}
NC_DATA = {
"nextcloud": {
"system": {
"version": "28.0.4.1",
"theme": "",
"enable_avatars": "yes",
"enable_previews": "yes",
"memcache.local": "\\OC\\Memcache\\APCu",
"memcache.distributed": "none",
"filelocking.enabled": "yes",
"memcache.locking": "none",
"debug": "no",
"freespace": 32769138688,
"cpuload": [2.06640625, 1.58447265625, 1.45263671875],
"mem_total": 30728192,
"mem_free": 6753280,
"swap_total": 10484736,
"swap_free": 10484736,
"apps": {
"num_installed": 41,
"num_updates_available": 0,
"app_updates": [],
},
"update": {"lastupdatedat": 1713048517, "available": False},
},
"storage": {
"num_users": 2,
"num_files": 6783,
"num_storages": 4,
"num_storages_local": 1,
"num_storages_home": 2,
"num_storages_other": 1,
},
"shares": {
"num_shares": 2,
"num_shares_user": 0,
"num_shares_groups": 0,
"num_shares_link": 2,
"num_shares_mail": 0,
"num_shares_room": 0,
"num_shares_link_no_password": 2,
"num_fed_shares_sent": 0,
"num_fed_shares_received": 1,
"permissions_3_17": 1,
"permissions_3_31": 1,
},
},
"server": {
"webserver": "Apache/2.4.57 (Debian)",
"php": {
"version": "8.2.18",
"memory_limit": 536870912,
"max_execution_time": 3600,
"upload_max_filesize": 536870912,
"opcache_revalidate_freq": 60,
"opcache": {
"opcache_enabled": True,
"cache_full": False,
"restart_pending": False,
"restart_in_progress": False,
"memory_usage": {
"used_memory": 72027112,
"free_memory": 62190616,
"wasted_memory": 0,
"current_wasted_percentage": 0,
},
"interned_strings_usage": {
"buffer_size": 33554432,
"used_memory": 12630360,
"free_memory": 20924072,
"number_of_strings": 69242,
},
"opcache_statistics": {
"num_cached_scripts": 1406,
"num_cached_keys": 2654,
"max_cached_keys": 16229,
"hits": 9739971,
"start_time": 1722222008,
"last_restart_time": 0,
"oom_restarts": 0,
"hash_restarts": 0,
"manual_restarts": 0,
"misses": 1406,
"blacklist_misses": 0,
"blacklist_miss_ratio": 0,
"opcache_hit_rate": 99.9855667222406,
},
"jit": {
"enabled": True,
"on": True,
"kind": 5,
"opt_level": 5,
"opt_flags": 6,
"buffer_size": 134217712,
"buffer_free": 133190688,
},
},
"apcu": {
"cache": {
"num_slots": 4099,
"ttl": 0,
"num_hits": 590911,
"num_misses": 55250,
"num_inserts": 55421,
"num_entries": 102,
"expunges": 0,
"start_time": 1722222008,
"mem_size": 175296,
"memory_type": "mmap",
},
"sma": {"num_seg": 1, "seg_size": 33554312, "avail_mem": 33342368},
},
"extensions": [
"Core",
"date",
"libxml",
"openssl",
"pcre",
"sqlite3",
"zlib",
"ctype",
"curl",
"dom",
"fileinfo",
"filter",
"hash",
"iconv",
"json",
"mbstring",
"SPL",
"session",
"PDO",
"pdo_sqlite",
"standard",
"posix",
"random",
"Reflection",
"Phar",
"SimpleXML",
"tokenizer",
"xml",
"xmlreader",
"xmlwriter",
"mysqlnd",
"apache2handler",
"apcu",
"bcmath",
"exif",
"ftp",
"gd",
"gmp",
"imagick",
"intl",
"ldap",
"memcached",
"pcntl",
"pdo_mysql",
"pdo_pgsql",
"redis",
"sodium",
"sysvsem",
"zip",
"Zend OPcache",
],
},
"database": {"type": "sqlite3", "version": "3.40.1", "size": "4784128"},
},
"activeUsers": {"last5minutes": 0, "last1hour": 0, "last24hours": 0},
}

View File

@ -0,0 +1,277 @@
# serializer version: 1
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_avatars_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_avatars_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Avatars enabled',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_system_enable_avatars',
'unique_id': '1234567890abcdef#system_enable_avatars',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_avatars_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local Avatars enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_avatars_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_debug_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_debug_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Debug enabled',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_system_debug',
'unique_id': '1234567890abcdef#system_debug',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_debug_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local Debug enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_debug_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_filelocking_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_filelocking_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Filelocking enabled',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_system_filelocking_enabled',
'unique_id': '1234567890abcdef#system_filelocking.enabled',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_filelocking_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local Filelocking enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_filelocking_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_jit_active-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_jit_active',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'JIT active',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_jit_on',
'unique_id': '1234567890abcdef#jit_on',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_jit_active-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local JIT active',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_jit_active',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_jit_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_jit_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'JIT enabled',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_jit_enabled',
'unique_id': '1234567890abcdef#jit_enabled',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_jit_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local JIT enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_jit_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_previews_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.my_nc_url_local_previews_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Previews enabled',
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'nextcloud_system_enable_previews',
'unique_id': '1234567890abcdef#system_enable_previews',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[binary_sensor.my_nc_url_local_previews_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'my.nc_url.local Previews enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.my_nc_url_local_previews_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -2,7 +2,7 @@
# name: test_reauth
dict({
'password': 'other_password',
'url': 'nc_url',
'url': 'https://my.nc_url.local',
'username': 'other_user',
'verify_ssl': True,
})
@ -10,7 +10,7 @@
# name: test_user_create_entry
dict({
'password': 'nc_pass',
'url': 'nc_url',
'url': 'https://my.nc_url.local',
'username': 'nc_user',
'verify_ssl': True,
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
# serializer version: 1
# name: test_async_setup_entry[update.my_nc_url_local_none-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'update',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'update.my_nc_url_local_none',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'nextcloud',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '1234567890abcdef#update',
'unit_of_measurement': None,
})
# ---
# name: test_async_setup_entry[update.my_nc_url_local_none-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'auto_update': False,
'entity_picture': 'https://brands.home-assistant.io/_/nextcloud/icon.png',
'friendly_name': 'my.nc_url.local None',
'in_progress': False,
'installed_version': '28.0.4.1',
'latest_version': '28.0.4.1',
'release_summary': None,
'release_url': 'https://nextcloud.com/changelog/#28-0-4',
'skipped_version': None,
'supported_features': <UpdateEntityFeature: 0>,
'title': None,
}),
'context': <ANY>,
'entity_id': 'update.my_nc_url_local_none',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,33 @@
"""Tests for the Nextcloud binary sensors."""
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import init_integration
from .const import NC_DATA, VALID_CONFIG
from tests.common import snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_async_setup_entry(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test a successful setup entry."""
with patch(
"homeassistant.components.nextcloud.PLATFORMS", [Platform.BINARY_SENSOR]
):
entry = await init_integration(hass, VALID_CONFIG, NC_DATA)
states = hass.states.async_all()
assert len(states) == 6
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)

View File

@ -1,6 +1,6 @@
"""Tests for the Nextcloud config flow."""
from unittest.mock import Mock, patch
from unittest.mock import patch
from nextcloudmonitor import (
NextcloudMonitorAuthorizationError,
@ -12,24 +12,19 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components.nextcloud.const import DOMAIN
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, CONF_VERIFY_SSL
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .const import VALID_CONFIG
from tests.common import MockConfigEntry
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
VALID_CONFIG = {
CONF_URL: "nc_url",
CONF_USERNAME: "nc_user",
CONF_PASSWORD: "nc_pass",
CONF_VERIFY_SSL: True,
}
async def test_user_create_entry(
hass: HomeAssistant, mock_nextcloud_monitor: Mock, snapshot: SnapshotAssertion
hass: HomeAssistant, snapshot: SnapshotAssertion
) -> None:
"""Test that the user step works."""
# start user flow
@ -85,7 +80,7 @@ async def test_user_create_entry(
# test success
with patch(
"homeassistant.components.nextcloud.config_flow.NextcloudMonitor",
return_value=mock_nextcloud_monitor,
return_value=True,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -94,17 +89,15 @@ async def test_user_create_entry(
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "nc_url"
assert result["title"] == "https://my.nc_url.local"
assert result["data"] == snapshot
async def test_user_already_configured(
hass: HomeAssistant, mock_nextcloud_monitor: Mock
) -> None:
async def test_user_already_configured(hass: HomeAssistant) -> None:
"""Test that errors are shown when duplicates are added."""
entry = MockConfigEntry(
domain=DOMAIN,
title="nc_url",
title="https://my.nc_url.local",
unique_id="nc_url",
data=VALID_CONFIG,
)
@ -119,7 +112,7 @@ async def test_user_already_configured(
with patch(
"homeassistant.components.nextcloud.config_flow.NextcloudMonitor",
return_value=mock_nextcloud_monitor,
return_value=True,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -131,13 +124,11 @@ async def test_user_already_configured(
assert result["reason"] == "already_configured"
async def test_reauth(
hass: HomeAssistant, mock_nextcloud_monitor: Mock, snapshot: SnapshotAssertion
) -> None:
async def test_reauth(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test that the re-auth flow works."""
entry = MockConfigEntry(
domain=DOMAIN,
title="nc_url",
title="https://my.nc_url.local",
unique_id="nc_url",
data=VALID_CONFIG,
)
@ -206,7 +197,7 @@ async def test_reauth(
# test success
with patch(
"homeassistant.components.nextcloud.config_flow.NextcloudMonitor",
return_value=mock_nextcloud_monitor,
return_value=True,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],

View File

@ -0,0 +1,69 @@
"""Tests for the Nextcloud coordinator."""
from unittest.mock import Mock, patch
from freezegun.api import FrozenDateTimeFactory
from nextcloudmonitor import (
NextcloudMonitor,
NextcloudMonitorAuthorizationError,
NextcloudMonitorConnectionError,
NextcloudMonitorError,
NextcloudMonitorRequestError,
)
import pytest
from homeassistant.components.nextcloud.const import DEFAULT_SCAN_INTERVAL
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from . import mock_config_entry
from .const import NC_DATA, VALID_CONFIG
from tests.common import async_fire_time_changed
@pytest.mark.parametrize(
("error"),
[
(NextcloudMonitorAuthorizationError),
(NextcloudMonitorConnectionError),
(NextcloudMonitorRequestError),
],
)
async def test_data_update(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, error: NextcloudMonitorError
) -> None:
"""Test a coordinator data updates."""
entry = mock_config_entry(VALID_CONFIG)
entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.nextcloud.NextcloudMonitor", spec=NextcloudMonitor
) as mock_nextcloud_monitor,
):
mock_nextcloud_monitor.return_value.update = Mock(
return_value=True,
side_effect=[None, error, None],
)
mock_nextcloud_monitor.return_value.data = NC_DATA
assert await hass.config_entries.async_setup(entry.entry_id)
# Test successful setup and first data fetch
await hass.async_block_till_done(wait_background_tasks=True)
states = hass.states.async_all()
assert (state != STATE_UNAVAILABLE for state in states)
# Test states get unavailable on error
freezer.tick(DEFAULT_SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
states = hass.states.async_all()
assert (state == STATE_UNAVAILABLE for state in states)
# Test successful data fetch
freezer.tick(DEFAULT_SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
states = hass.states.async_all()
assert (state != STATE_UNAVAILABLE for state in states)

View File

@ -0,0 +1,95 @@
"""Tests for the Nextcloud init."""
from unittest.mock import Mock, patch
from nextcloudmonitor import (
NextcloudMonitorAuthorizationError,
NextcloudMonitorConnectionError,
NextcloudMonitorError,
NextcloudMonitorRequestError,
)
import pytest
from homeassistant.components.nextcloud.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_URL, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import init_integration, mock_config_entry
from .const import MOCKED_ENTRY_ID, NC_DATA, VALID_CONFIG
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_async_setup_entry(
hass: HomeAssistant,
) -> None:
"""Test a successful setup entry."""
assert await init_integration(hass, VALID_CONFIG, NC_DATA)
async def test_unique_id_migration(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
) -> None:
"""Test migration of unique ids to stable ones."""
object_id = "my_nc_url_local_system_version"
entity_id = f"{Platform.SENSOR}.{object_id}"
entry = mock_config_entry(VALID_CONFIG)
entry.add_to_hass(hass)
entity = entity_registry.async_get_or_create(
Platform.SENSOR,
DOMAIN,
f"{VALID_CONFIG[CONF_URL]}#nextcloud_system_version",
suggested_object_id=object_id,
config_entry=entry,
)
# test old unique id
assert entity.entity_id == entity_id
assert entity.unique_id == f"{VALID_CONFIG[CONF_URL]}#nextcloud_system_version"
with (
patch(
"homeassistant.components.nextcloud.NextcloudMonitor"
) as mock_nextcloud_monitor,
):
mock_nextcloud_monitor.update = Mock(return_value=True)
mock_nextcloud_monitor.return_value.data = NC_DATA
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# test migrated unique id
reg_entry = entity_registry.async_get(entity_id)
assert reg_entry.unique_id == f"{MOCKED_ENTRY_ID}#system_version"
@pytest.mark.parametrize(
("exception", "expcted_entry_state"),
[
(NextcloudMonitorAuthorizationError, ConfigEntryState.SETUP_ERROR),
(NextcloudMonitorConnectionError, ConfigEntryState.SETUP_RETRY),
(NextcloudMonitorRequestError, ConfigEntryState.SETUP_RETRY),
],
)
async def test_setup_entry_errors(
hass: HomeAssistant,
exception: NextcloudMonitorError,
expcted_entry_state: ConfigEntryState,
) -> None:
"""Test a successful setup entry."""
entry = mock_config_entry(VALID_CONFIG)
entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.nextcloud.NextcloudMonitor", side_effect=exception
),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == expcted_entry_state

View File

@ -0,0 +1,31 @@
"""Tests for the Nextcloud sensors."""
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import init_integration
from .const import NC_DATA, VALID_CONFIG
from tests.common import snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_async_setup_entry(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test a successful setup entry."""
with patch("homeassistant.components.nextcloud.PLATFORMS", [Platform.SENSOR]):
entry = await init_integration(hass, VALID_CONFIG, NC_DATA)
states = hass.states.async_all()
assert len(states) == 80
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)

View File

@ -0,0 +1,80 @@
"""Tests for the Nextcloud update entity."""
from copy import deepcopy
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import STATE_OFF, STATE_ON, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import init_integration
from .const import NC_DATA, VALID_CONFIG
from tests.common import snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_async_setup_entry(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test a successful setup entry."""
with patch("homeassistant.components.nextcloud.PLATFORMS", [Platform.UPDATE]):
entry = await init_integration(hass, VALID_CONFIG, NC_DATA)
states = hass.states.async_all()
assert len(states) == 1
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
async def test_setup_entity_without_update(
hass: HomeAssistant, snapshot: SnapshotAssertion
) -> None:
"""Test update entity is created w/o available update."""
with patch("homeassistant.components.nextcloud.PLATFORMS", [Platform.UPDATE]):
await init_integration(hass, VALID_CONFIG, NC_DATA)
states = hass.states.async_all()
assert len(states) == 1
assert states[0].state == STATE_OFF
assert states[0].attributes["installed_version"] == "28.0.4.1"
assert states[0].attributes["latest_version"] == "28.0.4.1"
assert (
states[0].attributes["release_url"] == "https://nextcloud.com/changelog/#28-0-4"
)
async def test_setup_entity_with_update(
hass: HomeAssistant, snapshot: SnapshotAssertion
) -> None:
"""Test update entity is created with available update."""
data = deepcopy(NC_DATA)
data["nextcloud"]["system"]["update"]["available"] = True
data["nextcloud"]["system"]["update"]["available_version"] = "30.0.0.0"
with patch("homeassistant.components.nextcloud.PLATFORMS", [Platform.UPDATE]):
await init_integration(hass, VALID_CONFIG, data)
states = hass.states.async_all()
assert len(states) == 1
assert states[0].state == STATE_ON
assert states[0].attributes["installed_version"] == "28.0.4.1"
assert states[0].attributes["latest_version"] == "30.0.0.0"
assert (
states[0].attributes["release_url"] == "https://nextcloud.com/changelog/#30-0-0"
)
async def test_setup_no_entity(hass: HomeAssistant) -> None:
"""Test no update entity is created, when no data available."""
data = deepcopy(NC_DATA)
data["nextcloud"]["system"].pop("update") # only nc<28.0.0
with patch("homeassistant.components.nextcloud.PLATFORMS", [Platform.UPDATE]):
await init_integration(hass, VALID_CONFIG, data)
states = hass.states.async_all()
assert len(states) == 0