Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tests #154

Merged
merged 2 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
asyncio_mode = auto
7 changes: 6 additions & 1 deletion scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
# Install requirements
pip install -r requirements_test.txt
# Run tests and get a summary of successes/failures and code coverage
pytest --durations=10 --cov-report term-missing --cov=custom_components.bermuda tests
pytest --durations=10 --cov-report term-missing --cov=custom_components.bermuda tests --cov-fail-under=43

# Note that the github workflow runs:
#
# pytest --timeout=9 --durations=10 -n auto -p no:sugar tests
# which doesn't care about coverage.
43 changes: 34 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@

import pytest

from .const import SERVICE_INFOS

# from custom_components.bermuda import BermudaDataUpdateCoordinator


pytest_plugins = "pytest_homeassistant_custom_component"


# This fixture enables loading custom integrations in all tests.
# Remove to enable selective use of this fixture
@pytest.fixture(autouse=True)
def auto_enable_custom_integrations(enable_custom_integrations):
"""Enable loading custom integrations."""
yield


# This fixture is used to prevent HomeAssistant from
# attempting to create and dismiss persistent
# notifications. These calls would fail without this
Expand All @@ -30,9 +43,8 @@ def skip_notifications_fixture():
@pytest.fixture(name="bypass_get_data")
def bypass_get_data_fixture():
"""Skip calls to get data from API."""
# AJG: We don't implement this, so nothing to monkeypatch.
# with patch("custom_components.bermuda.BermudaApiClient.async_get_data"):
# yield
with patch("custom_components.bermuda.BermudaDataUpdateCoordinator.async_refresh"):
yield


# In this fixture, we are forcing calls to async_get_data to raise
Expand All @@ -41,9 +53,22 @@ def bypass_get_data_fixture():
@pytest.fixture(name="error_on_get_data")
def error_get_data_fixture():
"""Simulate error when retrieving data from API."""
# AJG: again we don't implement async_get_data...
# with patch(
# "custom_components.bermuda.BermudaApiClient.async_get_data",
# side_effect=Exception,
# ):
# yield
with patch(
"custom_components.bermuda.BermudaDataUpdateCoordinator.async_refresh",
side_effect=Exception,
):
yield


@pytest.fixture(autouse=True)
def mock_bluetooth(enable_bluetooth):
"""Auto mock bluetooth."""


# This fixture ensures that the config flow gets service info for the anticipated address
# to go into configured_devices
@pytest.fixture(autouse=True)
def mock_service_info():
"""Simulate a discovered advertisement for config_flow"""
with patch("custom_components.bermuda.bluetooth.async_discovered_service_info"):
return SERVICE_INFOS
43 changes: 34 additions & 9 deletions tests/const.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
"""Constants for Bermuda BLE Trilateration tests."""

# AJG: We don't use these vars in our flow. TODO: Add some we _do_ use!
# from custom_components.bermuda.const import (
# CONF_PASSWORD,
# )
# from custom_components.bermuda.const import (
# CONF_USERNAME,
# )
# MOCK_CONFIG = {CONF_USERNAME: "test_username", CONF_PASSWORD: "test_password"}
from __future__ import annotations

MOCK_CONFIG = {}
import custom_components.bermuda.const

# from custom_components.bermuda.const import CONF_DEVICES
# from custom_components.bermuda.const import CONF_MAX_RADIUS


MOCK_OPTIONS = {
custom_components.bermuda.const.CONF_MAX_RADIUS: 20.0,
custom_components.bermuda.const.CONF_MAX_VELOCITY: 3.0,
custom_components.bermuda.const.CONF_DEVTRACK_TIMEOUT: 30,
custom_components.bermuda.const.CONF_UPDATE_INTERVAL: 10.0,
custom_components.bermuda.const.CONF_SMOOTHING_SAMPLES: 20,
custom_components.bermuda.const.CONF_ATTENUATION: 3.0,
custom_components.bermuda.const.CONF_REF_POWER: -55.0,
custom_components.bermuda.const.CONF_DEVICES: [], # ["EE:E8:37:9F:6B:54"],
}

MOCK_CONFIG = {"source": "user"}


SERVICE_INFOS = [
{
"name": "test device",
"advertisement": {"local_name": "test local name"},
"device": {"name": "test device name"},
"address": "EE:E8:37:9F:6B:54",
},
{
"name": "test device2",
"advertisement": {"local_name": "test local name2"},
"device": {"name": "test device name2"},
"address": "EE:E8:37:9F:6B:56",
},
]
23 changes: 11 additions & 12 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
from homeassistant import data_entry_flow
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda.const import BINARY_SENSOR
from custom_components.bermuda.const import DOMAIN
from custom_components.bermuda.const import PLATFORMS
from custom_components.bermuda.const import SENSOR
from custom_components.bermuda.const import SWITCH
from custom_components.bermuda.const import NAME

from .const import MOCK_CONFIG
from .const import MOCK_OPTIONS


# This fixture bypasses the actual setup of the integration
Expand All @@ -40,7 +38,7 @@
async def test_successful_config_flow(hass, bypass_get_data):
"""Test a successful config flow."""
# Initialize a config flow
result = await hass.config_entries.flow.async_init(

Check failure on line 41 in tests/test_config_flow.py

View workflow job for this annotation

GitHub Actions / Run tests

test_successful_config_flow homeassistant.exceptions.DependencyError: Could not setup dependencies: bluetooth_adapters
DOMAIN, context={"source": config_entries.SOURCE_USER}
)

Expand All @@ -57,8 +55,9 @@
# Check that the config flow is complete and a new entry is created with
# the input data
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "test_username"
assert result["data"] == MOCK_CONFIG
assert result["title"] == NAME
assert result["data"] == {"source": "user"}
assert result["options"] == {}
assert result["result"]


Expand All @@ -69,7 +68,7 @@
async def test_failed_config_flow(hass, error_on_get_data):
"""Test a failed config flow due to credential validation failure."""

result = await hass.config_entries.flow.async_init(

Check failure on line 71 in tests/test_config_flow.py

View workflow job for this annotation

GitHub Actions / Run tests

test_failed_config_flow homeassistant.exceptions.DependencyError: Could not setup dependencies: bluetooth_adapters
DOMAIN, context={"source": config_entries.SOURCE_USER}
)

Expand All @@ -80,8 +79,8 @@
result["flow_id"], user_input=MOCK_CONFIG
)

assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "auth"}
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
assert result.get("errors") is None


# Our config flow also has an options flow, so we must test it as well.
Expand All @@ -94,21 +93,21 @@

# Initialize an options flow
await hass.config_entries.async_setup(entry.entry_id)
result = await hass.config_entries.options.async_init(entry.entry_id)

Check failure on line 96 in tests/test_config_flow.py

View workflow job for this annotation

GitHub Actions / Run tests

test_options_flow homeassistant.exceptions.DependencyError: Could not setup dependencies: bluetooth_adapters

# Verify that the first options step is a user form
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
assert result["step_id"] == "globalopts"

# Enter some fake data into the form
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={platform: platform != SENSOR for platform in PLATFORMS},
user_input=MOCK_OPTIONS,
)

# Verify that the flow finishes
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "test_username"
assert result["title"] == NAME

# Verify that the options were updated
assert entry.options == {BINARY_SENSOR: True, SENSOR: False, SWITCH: True}
assert entry.options == MOCK_OPTIONS
22 changes: 14 additions & 8 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

from __future__ import annotations

import pytest
from homeassistant.exceptions import ConfigEntryNotReady
# from homeassistant.exceptions import ConfigEntryNotReady
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda import BermudaDataUpdateCoordinator
Expand Down Expand Up @@ -34,15 +33,15 @@ async def test_setup_unload_and_reload_entry(hass, bypass_get_data):
# call, no code from custom_components/bermuda/api.py actually runs.
assert await async_setup_entry(hass, config_entry)
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
assert type(hass.data[DOMAIN][config_entry.entry_id]).isinstance(
BermudaDataUpdateCoordinator
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id], BermudaDataUpdateCoordinator
)

# Reload the entry and assert that the data from above is still there
assert await async_reload_entry(hass, config_entry) is None
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
assert type(hass.data[DOMAIN][config_entry.entry_id]).isinstance(
BermudaDataUpdateCoordinator
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id], BermudaDataUpdateCoordinator
)

# Unload the entry and verify that the data has been removed
Expand All @@ -54,8 +53,15 @@ async def test_setup_entry_exception(hass, error_on_get_data):
"""Test ConfigEntryNotReady when API raises an exception during entry setup."""
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")

assert config_entry is not None

# In this case we are testing the condition where async_setup_entry raises
# ConfigEntryNotReady using the `error_on_get_data` fixture which simulates
# an error.
with pytest.raises(ConfigEntryNotReady):
assert await async_setup_entry(hass, config_entry)

# Hmmm... this doesn't seem to be how this works. The super's _async_refresh might
# handle exceptions, in which it then sets self.last_update_status, which is what
# async_setup_entry checks in order to raise ConfigEntryNotReady, but I don't think
# anything will "catch" our over-ridded async_refresh's exception.
# with pytest.raises(ConfigEntryNotReady):
# assert await async_setup_entry(hass, config_entry)
63 changes: 33 additions & 30 deletions tests/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@

from __future__ import annotations

from unittest.mock import call
from unittest.mock import patch

from homeassistant.components.switch import SERVICE_TURN_OFF
from homeassistant.components.switch import SERVICE_TURN_ON
from homeassistant.const import ATTR_ENTITY_ID
# from homeassistant.components.switch import SERVICE_TURN_OFF
# from homeassistant.components.switch import SERVICE_TURN_ON
# from homeassistant.const import ATTR_ENTITY_ID
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda import async_setup_entry
from custom_components.bermuda.const import DEFAULT_NAME

# from custom_components.bermuda.const import DEFAULT_NAME
from custom_components.bermuda.const import DOMAIN
from custom_components.bermuda.const import SWITCH

from .const import MOCK_CONFIG

# from unittest.mock import call
# from unittest.mock import patch


# from custom_components.bermuda.const import SWITCH


async def test_switch_services(hass):
"""Test switch services."""
Expand All @@ -29,25 +32,25 @@ async def test_switch_services(hass):
# in test code as well and can be used to test
# additional things, like whether a function
# was called or what arguments it was called with
with patch(
"custom_components.bermuda.BermudaApiClient.async_set_title"
) as title_func:
await hass.services.async_call(
SWITCH,
SERVICE_TURN_OFF,
service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
blocking=True,
)
assert title_func.called
assert title_func.call_args == call("foo")

title_func.reset_mock()

await hass.services.async_call(
SWITCH,
SERVICE_TURN_ON,
service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
blocking=True,
)
assert title_func.called
assert title_func.call_args == call("bar")
# with patch(
# "custom_components.bermuda.BermudaDataUpdateCoordinator.async_set_title"
# ) as title_func:
# await hass.services.async_call(
# SWITCH,
# SERVICE_TURN_OFF,
# service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
# blocking=True,
# )
# assert title_func.called
# assert title_func.call_args == call("foo")

# title_func.reset_mock()

# await hass.services.async_call(
# SWITCH,
# SERVICE_TURN_ON,
# service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
# blocking=True,
# )
# assert title_func.called
# assert title_func.call_args == call("bar")
Loading