Add Aussie Broadband integration (#53552)

This commit is contained in:
Nick Whyte
2022-01-20 19:34:25 -10:00
committed by GitHub
parent 1f00ded33a
commit 3dd7ec6856
15 changed files with 952 additions and 0 deletions
@@ -0,0 +1 @@
"""Tests for the Aussie Broadband integration."""
@@ -0,0 +1,58 @@
"""Aussie Broadband common helpers for tests."""
from unittest.mock import patch
from homeassistant.components.aussie_broadband.const import (
CONF_SERVICES,
DOMAIN as AUSSIE_BROADBAND_DOMAIN,
)
from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME
from tests.common import MockConfigEntry
FAKE_SERVICES = [
{
"service_id": "12345678",
"description": "Fake ABB NBN Service",
"type": "NBN",
"name": "NBN",
},
{
"service_id": "87654321",
"description": "Fake ABB Mobile Service",
"type": "PhoneMobile",
"name": "Mobile",
},
]
FAKE_DATA = {
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
}
async def setup_platform(hass, platforms=[], side_effect=None, usage={}):
"""Set up the Aussie Broadband platform."""
mock_entry = MockConfigEntry(
domain=AUSSIE_BROADBAND_DOMAIN,
data=FAKE_DATA,
options={CONF_SERVICES: ["12345678", "87654321"], CONF_SCAN_INTERVAL: 30},
)
mock_entry.add_to_hass(hass)
with patch("homeassistant.components.aussie_broadband.PLATFORMS", platforms), patch(
"aussiebb.asyncio.AussieBB.__init__", return_value=None
), patch(
"aussiebb.asyncio.AussieBB.login",
return_value=True,
side_effect=side_effect,
), patch(
"aussiebb.asyncio.AussieBB.get_services",
return_value=FAKE_SERVICES,
side_effect=side_effect,
), patch(
"aussiebb.asyncio.AussieBB.get_usage", return_value=usage
):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
return mock_entry
@@ -0,0 +1,322 @@
"""Test the Aussie Broadband config flow."""
from unittest.mock import patch
from aiohttp import ClientConnectionError
from aussiebb.asyncio import AuthenticationException
from homeassistant import config_entries, setup
from homeassistant.components.aussie_broadband.const import CONF_SERVICES, DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT,
RESULT_TYPE_CREATE_ENTRY,
RESULT_TYPE_FORM,
)
from .common import FAKE_DATA, FAKE_SERVICES, setup_platform
TEST_USERNAME = FAKE_DATA[CONF_USERNAME]
TEST_PASSWORD = FAKE_DATA[CONF_PASSWORD]
async def test_form(hass: HomeAssistant) -> None:
"""Test we get the form."""
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result1["type"] == RESULT_TYPE_FORM
assert result1["errors"] is None
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch(
"aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]
), patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
FAKE_DATA,
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == TEST_USERNAME
assert result2["data"] == FAKE_DATA
assert result2["options"] == {CONF_SERVICES: ["12345678"]}
assert len(mock_setup_entry.mock_calls) == 1
async def test_already_configured(hass: HomeAssistant) -> None:
"""Test already configured."""
# Setup an entry
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch(
"aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]
), patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
) as mock_setup_entry:
await hass.config_entries.flow.async_configure(
result1["flow_id"],
FAKE_DATA,
)
await hass.async_block_till_done()
# Test Already configured
result3 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch(
"aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]
), patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"],
FAKE_DATA,
)
await hass.async_block_till_done()
assert result4["type"] == RESULT_TYPE_ABORT
assert len(mock_setup_entry.mock_calls) == 0
async def test_no_services(hass: HomeAssistant) -> None:
"""Test when there are no services."""
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result1["type"] == RESULT_TYPE_FORM
assert result1["errors"] is None
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch("aussiebb.asyncio.AussieBB.get_services", return_value=[]), patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
FAKE_DATA,
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_ABORT
assert result2["reason"] == "no_services_found"
assert len(mock_setup_entry.mock_calls) == 0
async def test_form_multiple_services(hass: HomeAssistant) -> None:
"""Test the config flow with multiple services."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
assert result["errors"] is None
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch("aussiebb.asyncio.AussieBB.get_services", return_value=FAKE_SERVICES):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
FAKE_DATA,
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "service"
assert result2["errors"] is None
with patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_SERVICES: [FAKE_SERVICES[1]["service_id"]]},
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_CREATE_ENTRY
assert result3["title"] == TEST_USERNAME
assert result3["data"] == FAKE_DATA
assert result3["options"] == {
CONF_SERVICES: [FAKE_SERVICES[1]["service_id"]],
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
"""Test invalid auth is handled."""
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", side_effect=AuthenticationException()
):
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
FAKE_DATA,
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["errors"] == {"base": "invalid_auth"}
async def test_form_network_issue(hass: HomeAssistant) -> None:
"""Test network issues are handled."""
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", side_effect=ClientConnectionError()
):
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
FAKE_DATA,
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["errors"] == {"base": "cannot_connect"}
async def test_reauth(hass: HomeAssistant) -> None:
"""Test reauth flow."""
await setup.async_setup_component(hass, "persistent_notification", {})
# Test reauth but the entry doesn't exist
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_REAUTH}, data=FAKE_DATA
)
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch(
"aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]
), patch(
"homeassistant.components.aussie_broadband.async_setup_entry",
return_value=True,
):
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
{
CONF_PASSWORD: TEST_PASSWORD,
},
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == TEST_USERNAME
assert result2["data"] == FAKE_DATA
# Test failed reauth
result5 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_REAUTH},
data=FAKE_DATA,
)
assert result5["step_id"] == "reauth"
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", side_effect=AuthenticationException()
), patch("aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]):
result6 = await hass.config_entries.flow.async_configure(
result5["flow_id"],
{
CONF_PASSWORD: "test-wrongpassword",
},
)
await hass.async_block_till_done()
assert result6["step_id"] == "reauth"
# Test successful reauth
with patch("aussiebb.asyncio.AussieBB.__init__", return_value=None), patch(
"aussiebb.asyncio.AussieBB.login", return_value=True
), patch("aussiebb.asyncio.AussieBB.get_services", return_value=[FAKE_SERVICES[0]]):
result7 = await hass.config_entries.flow.async_configure(
result6["flow_id"],
{
CONF_PASSWORD: "test-newpassword",
},
)
await hass.async_block_till_done()
assert result7["type"] == "abort"
assert result7["reason"] == "reauth_successful"
async def test_options_flow(hass):
"""Test options flow."""
entry = await setup_platform(hass)
with patch("aussiebb.asyncio.AussieBB.get_services", return_value=FAKE_SERVICES):
result1 = await hass.config_entries.options.async_init(entry.entry_id)
assert result1["type"] == RESULT_TYPE_FORM
assert result1["step_id"] == "init"
result2 = await hass.config_entries.options.async_configure(
result1["flow_id"],
user_input={CONF_SERVICES: []},
)
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
assert entry.options == {CONF_SERVICES: []}
async def test_options_flow_auth_failure(hass):
"""Test options flow with auth failure."""
entry = await setup_platform(hass)
with patch(
"aussiebb.asyncio.AussieBB.get_services", side_effect=AuthenticationException()
):
result1 = await hass.config_entries.options.async_init(entry.entry_id)
assert result1["type"] == RESULT_TYPE_ABORT
assert result1["reason"] == "invalid_auth"
async def test_options_flow_network_failure(hass):
"""Test options flow with connectivity failure."""
entry = await setup_platform(hass)
with patch(
"aussiebb.asyncio.AussieBB.get_services", side_effect=ClientConnectionError()
):
result1 = await hass.config_entries.options.async_init(entry.entry_id)
assert result1["type"] == RESULT_TYPE_ABORT
assert result1["reason"] == "cannot_connect"
async def test_options_flow_not_loaded(hass):
"""Test the options flow aborts when the entry has unloaded due to a reauth."""
entry = await setup_platform(hass)
with patch(
"aussiebb.asyncio.AussieBB.get_services", side_effect=AuthenticationException()
):
entry.state = config_entries.ConfigEntryState.NOT_LOADED
result1 = await hass.config_entries.options.async_init(entry.entry_id)
assert result1["type"] == RESULT_TYPE_ABORT
assert result1["reason"] == "unknown"
@@ -0,0 +1,35 @@
"""Test the Aussie Broadband init."""
from unittest.mock import patch
from aiohttp import ClientConnectionError
from aussiebb.asyncio import AuthenticationException
from homeassistant import data_entry_flow
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from .common import setup_platform
async def test_unload(hass: HomeAssistant) -> None:
"""Test unload."""
entry = await setup_platform(hass)
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.NOT_LOADED
async def test_auth_failure(hass: HomeAssistant) -> None:
"""Test init with an authentication failure."""
with patch(
"homeassistant.components.aussie_broadband.config_flow.ConfigFlow.async_step_reauth",
return_value={"type": data_entry_flow.RESULT_TYPE_FORM},
) as mock_async_step_reauth:
await setup_platform(hass, side_effect=AuthenticationException())
mock_async_step_reauth.assert_called_once()
async def test_net_failure(hass: HomeAssistant) -> None:
"""Test init with a network failure."""
entry = await setup_platform(hass, side_effect=ClientConnectionError())
assert entry.state is ConfigEntryState.SETUP_RETRY
@@ -0,0 +1,50 @@
"""Aussie Broadband sensor platform tests."""
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from .common import setup_platform
MOCK_NBN_USAGE = {
"usedMb": 54321,
"downloadedMb": 50000,
"uploadedMb": 4321,
"daysTotal": 28,
"daysRemaining": 25,
}
MOCK_MOBILE_USAGE = {
"national": {"calls": 1, "cost": 0},
"mobile": {"calls": 2, "cost": 0},
"international": {"calls": 3, "cost": 0},
"sms": {"calls": 4, "cost": 0},
"internet": {"kbytes": 512, "cost": 0},
"voicemail": {"calls": 6, "cost": 0},
"other": {"calls": 7, "cost": 0},
"daysTotal": 31,
"daysRemaining": 30,
"historical": [],
}
async def test_nbn_sensor_states(hass):
"""Tests that the sensors are correct."""
await setup_platform(hass, [SENSOR_DOMAIN], usage=MOCK_NBN_USAGE)
assert hass.states.get("sensor.nbn_data_used").state == "54321"
assert hass.states.get("sensor.nbn_downloaded").state == "50000"
assert hass.states.get("sensor.nbn_uploaded").state == "4321"
assert hass.states.get("sensor.nbn_billing_cycle_length").state == "28"
assert hass.states.get("sensor.nbn_billing_cycle_remaining").state == "25"
async def test_phone_sensor_states(hass):
"""Tests that the sensors are correct."""
await setup_platform(hass, [SENSOR_DOMAIN], usage=MOCK_MOBILE_USAGE)
assert hass.states.get("sensor.mobile_national_calls").state == "1"
assert hass.states.get("sensor.mobile_mobile_calls").state == "2"
assert hass.states.get("sensor.mobile_sms_sent").state == "4"
assert hass.states.get("sensor.mobile_data_used").state == "512"
assert hass.states.get("sensor.mobile_billing_cycle_length").state == "31"
assert hass.states.get("sensor.mobile_billing_cycle_remaining").state == "30"