Skip to content

Commit

Permalink
Use fixtures for Network component tests (#135220)
Browse files Browse the repository at this point in the history
abmantis authored Jan 20, 2025

Verified

This commit was signed with the committer’s verified signature.
pietroalbini Pietro Albini
1 parent 63d294e commit 3e716a1
Showing 3 changed files with 115 additions and 181 deletions.
3 changes: 3 additions & 0 deletions tests/components/network/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
"""Tests for the Network Configuration integration."""

NO_LOOPBACK_IPADDR = "192.168.1.5"
LOOPBACK_IPADDR = "127.0.0.1"
59 changes: 57 additions & 2 deletions tests/components/network/conftest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
"""Tests for the Network Configuration integration."""

from collections.abc import Generator
from unittest.mock import _patch
from unittest.mock import MagicMock, Mock, _patch, patch

import ifaddr
import pytest

from . import LOOPBACK_IPADDR, NO_LOOPBACK_IPADDR


def _generate_mock_adapters():
mock_lo0 = Mock(spec=ifaddr.Adapter)
mock_lo0.nice_name = "lo0"
mock_lo0.ips = [ifaddr.IP(LOOPBACK_IPADDR, 8, "lo0")]
mock_lo0.index = 0
mock_eth0 = Mock(spec=ifaddr.Adapter)
mock_eth0.nice_name = "eth0"
mock_eth0.ips = [ifaddr.IP(("2001:db8::", 1, 1), 8, "eth0")]
mock_eth0.index = 1
mock_eth1 = Mock(spec=ifaddr.Adapter)
mock_eth1.nice_name = "eth1"
mock_eth1.ips = [ifaddr.IP(NO_LOOPBACK_IPADDR, 23, "eth1")]
mock_eth1.index = 2
mock_vtun0 = Mock(spec=ifaddr.Adapter)
mock_vtun0.nice_name = "vtun0"
mock_vtun0.ips = [ifaddr.IP("169.254.3.2", 16, "vtun0")]
mock_vtun0.index = 3
return [mock_eth0, mock_lo0, mock_eth1, mock_vtun0]


def _mock_socket(sockname: list[str]) -> Generator[None]:
"""Mock the network socket."""
with patch(
"homeassistant.components.network.util.socket.socket",
return_value=MagicMock(getsockname=Mock(return_value=sockname)),
):
yield


@pytest.fixture(autouse=True)
def mock_network():
def mock_network() -> Generator[None]:
"""Override mock of network util's async_get_adapters."""
with patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
):
yield


@pytest.fixture(autouse=True)
@@ -19,3 +56,21 @@ def override_mock_get_source_ip(
mock_get_source_ip.stop()
yield
mock_get_source_ip.start()


@pytest.fixture
def mock_socket(request: pytest.FixtureRequest) -> Generator[None]:
"""Mock the network socket."""
yield from _mock_socket(request.param)


@pytest.fixture
def mock_socket_loopback() -> Generator[None]:
"""Mock the network socket with loopback address."""
yield from _mock_socket([LOOPBACK_IPADDR])


@pytest.fixture
def mock_socket_no_loopback() -> Generator[None]:
"""Mock the network socket with loopback address."""
yield from _mock_socket([NO_LOOPBACK_IPADDR])
234 changes: 55 additions & 179 deletions tests/components/network/test_init.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
from typing import Any
from unittest.mock import MagicMock, Mock, patch

import ifaddr
import pytest

from homeassistant.components import network
@@ -20,16 +19,9 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component

from tests.typing import WebSocketGenerator

_NO_LOOPBACK_IPADDR = "192.168.1.5"
_LOOPBACK_IPADDR = "127.0.0.1"

from . import LOOPBACK_IPADDR, NO_LOOPBACK_IPADDR

def _mock_socket(sockname):
mock_socket = MagicMock()
mock_socket.getsockname = Mock(return_value=sockname)
return mock_socket
from tests.typing import WebSocketGenerator


def _mock_cond_socket(sockname):
@@ -54,42 +46,13 @@ def _mock_socket_exception(exc):
return mock_socket


def _generate_mock_adapters():
mock_lo0 = Mock(spec=ifaddr.Adapter)
mock_lo0.nice_name = "lo0"
mock_lo0.ips = [ifaddr.IP("127.0.0.1", 8, "lo0")]
mock_lo0.index = 0
mock_eth0 = Mock(spec=ifaddr.Adapter)
mock_eth0.nice_name = "eth0"
mock_eth0.ips = [ifaddr.IP(("2001:db8::", 1, 1), 8, "eth0")]
mock_eth0.index = 1
mock_eth1 = Mock(spec=ifaddr.Adapter)
mock_eth1.nice_name = "eth1"
mock_eth1.ips = [ifaddr.IP("192.168.1.5", 23, "eth1")]
mock_eth1.index = 2
mock_vtun0 = Mock(spec=ifaddr.Adapter)
mock_vtun0.nice_name = "vtun0"
mock_vtun0.ips = [ifaddr.IP("169.254.3.2", 16, "vtun0")]
mock_vtun0.index = 3
return [mock_eth0, mock_lo0, mock_eth1, mock_vtun0]


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_async_detect_interfaces_setting_non_loopback_route(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test without default interface config and the route returns a non-loopback address."""
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([_NO_LOOPBACK_IPADDR]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

network_obj = hass.data[DOMAIN]
assert network_obj.configured_adapters == []
@@ -141,22 +104,13 @@ async def test_async_detect_interfaces_setting_non_loopback_route(
]


@pytest.mark.usefixtures("mock_socket_loopback")
async def test_async_detect_interfaces_setting_loopback_route(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test without default interface config and the route returns a loopback address."""
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([_LOOPBACK_IPADDR]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

network_obj = hass.data[DOMAIN]
assert network_obj.configured_adapters == []
@@ -207,22 +161,14 @@ async def test_async_detect_interfaces_setting_loopback_route(
]


@pytest.mark.parametrize("mock_socket", [[]], indirect=True)
@pytest.mark.usefixtures("mock_socket")
async def test_async_detect_interfaces_setting_empty_route(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test without default interface config and the route returns nothing."""
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

network_obj = hass.data[DOMAIN]
assert network_obj.configured_adapters == []
@@ -277,15 +223,9 @@ async def test_async_detect_interfaces_setting_exception(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test without default interface config and the route throws an exception."""
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket_exception(AttributeError),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
with patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket_exception(AttributeError),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
@@ -339,6 +279,7 @@ async def test_async_detect_interfaces_setting_exception(
]


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_interfaces_configured_from_storage(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -348,18 +289,9 @@ async def test_interfaces_configured_from_storage(
"key": STORAGE_KEY,
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth0", "eth1", "vtun0"]},
}
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([_NO_LOOPBACK_IPADDR]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

network_obj = hass.data[DOMAIN]
assert network_obj.configured_adapters == ["eth0", "eth1", "vtun0"]
@@ -422,15 +354,9 @@ async def test_interfaces_configured_from_storage_websocket_update(
"key": STORAGE_KEY,
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth0", "eth1", "vtun0"]},
}
with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([_NO_LOOPBACK_IPADDR]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
with patch(
"homeassistant.components.network.util.socket.socket",
return_value=MagicMock(getsockname=Mock(return_value=[NO_LOOPBACK_IPADDR])),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
@@ -546,6 +472,7 @@ async def test_interfaces_configured_from_storage_websocket_update(
]


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_async_get_source_ip_matching_interface(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -556,22 +483,13 @@ async def test_async_get_source_ip_matching_interface(
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with (
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket(["192.168.1.5"]),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == NO_LOOPBACK_IPADDR


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_async_get_source_ip_interface_not_match(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -582,22 +500,14 @@ async def test_async_get_source_ip_interface_not_match(
"data": {ATTR_CONFIGURED_ADAPTERS: ["vtun0"]},
}

with (
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket(["192.168.1.5"]),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "169.254.3.2"
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "169.254.3.2"


@pytest.mark.parametrize("mock_socket", [[None]], indirect=True)
@pytest.mark.usefixtures("mock_socket")
async def test_async_get_source_ip_cannot_determine_target(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -608,22 +518,13 @@ async def test_async_get_source_ip_cannot_determine_target(
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with (
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([None]),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == NO_LOOPBACK_IPADDR


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_async_get_ipv4_broadcast_addresses_default(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -634,24 +535,15 @@ async def test_async_get_ipv4_broadcast_addresses_default(
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket(["192.168.1.5"]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_ipv4_broadcast_addresses(hass) == {
IPv4Address("255.255.255.255")
}


@pytest.mark.usefixtures("mock_socket_loopback")
async def test_async_get_ipv4_broadcast_addresses_multiple(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
@@ -662,18 +554,8 @@ async def test_async_get_ipv4_broadcast_addresses_multiple(
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1", "vtun0"]},
}

with (
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([_LOOPBACK_IPADDR]),
),
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=_generate_mock_adapters(),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_ipv4_broadcast_addresses(hass) == {
IPv4Address("255.255.255.255"),
@@ -682,6 +564,7 @@ async def test_async_get_ipv4_broadcast_addresses_multiple(
}


@pytest.mark.usefixtures("mock_socket_no_loopback")
async def test_async_get_source_ip_no_enabled_addresses(
hass: HomeAssistant, hass_storage: dict[str, Any], caplog: pytest.LogCaptureFixture
) -> None:
@@ -692,24 +575,23 @@ async def test_async_get_source_ip_no_enabled_addresses(
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with (
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket(["192.168.1.5"]),
),
with patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
assert (
await network.async_get_source_ip(hass, MDNS_TARGET_IP)
== NO_LOOPBACK_IPADDR
)

assert "source address detection may be inaccurate" in caplog.text


@pytest.mark.parametrize("mock_socket", [[None]], indirect=True)
@pytest.mark.usefixtures("mock_socket")
async def test_async_get_source_ip_cannot_be_determined_and_no_enabled_addresses(
hass: HomeAssistant, hass_storage: dict[str, Any], caplog: pytest.LogCaptureFixture
) -> None:
@@ -720,15 +602,9 @@ async def test_async_get_source_ip_cannot_be_determined_and_no_enabled_addresses
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with (
patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([None]),
),
with patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
):
assert not await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
@@ -753,7 +629,7 @@ async def test_async_get_source_ip_no_ip_loopback(
),
patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_cond_socket(_LOOPBACK_IPADDR),
return_value=_mock_cond_socket(LOOPBACK_IPADDR),
),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})

0 comments on commit 3e716a1

Please sign in to comment.