<reponame>petewill/home-assistant
"""Tests for the Toon config flow."""

from unittest.mock import patch

import pytest
from toonapilib.toonapilibexceptions import (
    AgreementsRetrievalError,
    InvalidConsumerKey,
    InvalidConsumerSecret,
    InvalidCredentials,
)

from homeassistant import data_entry_flow
from homeassistant.components.toon import config_flow
from homeassistant.components.toon.const import (
    CONF_CLIENT_ID,
    CONF_CLIENT_SECRET,
    CONF_DISPLAY,
    CONF_TENANT,
    DOMAIN,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry, MockDependency

FIXTURE_APP = {
    DOMAIN: {CONF_CLIENT_ID: "1234567890abcdef", CONF_CLIENT_SECRET: "<KEY>"}
}

FIXTURE_CREDENTIALS = {
    CONF_USERNAME: "john.doe",
    CONF_PASSWORD: "<PASSWORD>",
    CONF_TENANT: "eneco",
}

FIXTURE_DISPLAY = {CONF_DISPLAY: "display1"}


@pytest.fixture
def mock_toonapilib():
    """Mock toonapilib."""
    with MockDependency("toonapilib") as mock_toonapilib_:
        mock_toonapilib_.Toon().display_names = [FIXTURE_DISPLAY[CONF_DISPLAY]]
        yield mock_toonapilib_


async def setup_component(hass):
    """Set up Toon component."""
    with patch("os.path.isfile", return_value=False):
        assert await async_setup_component(hass, DOMAIN, FIXTURE_APP)
        await hass.async_block_till_done()


async def test_abort_if_no_app_configured(hass):
    """Test abort if no app is configured."""
    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user()

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "no_app"


async def test_show_authenticate_form(hass):
    """Test that the authentication form is served."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=None)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"


@pytest.mark.parametrize(
    "side_effect,reason",
    [
        (InvalidConsumerKey, "client_id"),
        (InvalidConsumerSecret, "client_secret"),
        (AgreementsRetrievalError, "no_agreements"),
        (Exception, "unknown_auth_fail"),
    ],
)
async def test_toon_abort(hass, mock_toonapilib, side_effect, reason):
    """Test we abort on Toon error."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass

    mock_toonapilib.Toon.side_effect = side_effect

    result = await flow.async_step_authenticate(user_input=FIXTURE_CREDENTIALS)

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == reason


async def test_invalid_credentials(hass, mock_toonapilib):
    """Test we show authentication form on Toon auth error."""
    mock_toonapilib.Toon.side_effect = InvalidCredentials

    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"
    assert result["errors"] == {"base": "credentials"}


async def test_full_flow_implementation(hass, mock_toonapilib):
    """Test registering an integration and finishing flow works."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=None)
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"

    result = await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "display"

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["title"] == FIXTURE_DISPLAY[CONF_DISPLAY]
    assert result["data"][CONF_USERNAME] == FIXTURE_CREDENTIALS[CONF_USERNAME]
    assert result["data"][CONF_PASSWORD] == FIXTURE_CREDENTIALS[CONF_PASSWORD]
    assert result["data"][CONF_TENANT] == FIXTURE_CREDENTIALS[CONF_TENANT]
    assert result["data"][CONF_DISPLAY] == FIXTURE_DISPLAY[CONF_DISPLAY]


async def test_no_displays(hass, mock_toonapilib):
    """Test abort when there are no displays."""
    await setup_component(hass)

    mock_toonapilib.Toon().display_names = []

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    result = await flow.async_step_display(user_input=None)

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "no_displays"


async def test_display_already_exists(hass, mock_toonapilib):
    """Test showing display form again if display already exists."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    MockConfigEntry(domain=DOMAIN, data=FIXTURE_DISPLAY).add_to_hass(hass)

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "display"
    assert result["errors"] == {"base": "display_exists"}


async def test_abort_last_minute_fail(hass, mock_toonapilib):
    """Test we abort when API communication fails in the last step."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    mock_toonapilib.Toon.side_effect = Exception

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "unknown_auth_fail"

"""Tests for the Toon config flow."""

from unittest.mock import patch

import pytest
from toonapilib.toonapilibexceptions import (
    AgreementsRetrievalError,
    InvalidConsumerKey,
    InvalidConsumerSecret,
    InvalidCredentials,
)

from homeassistant import data_entry_flow
from homeassistant.components.toon import config_flow
from homeassistant.components.toon.const import (
    CONF_CLIENT_ID,
    CONF_CLIENT_SECRET,
    CONF_DISPLAY,
    CONF_TENANT,
    DOMAIN,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry, MockDependency

FIXTURE_APP = {
    DOMAIN: {CONF_CLIENT_ID: "1234567890abcdef", CONF_CLIENT_SECRET: "<KEY>"}
}

FIXTURE_CREDENTIALS = {
    CONF_USERNAME: "john.doe",
    CONF_PASSWORD: "<PASSWORD>",
    CONF_TENANT: "eneco",
}

FIXTURE_DISPLAY = {CONF_DISPLAY: "display1"}


@pytest.fixture
def mock_toonapilib():
    """Mock toonapilib."""
    with MockDependency("toonapilib") as mock_toonapilib_:
        mock_toonapilib_.Toon().display_names = [FIXTURE_DISPLAY[CONF_DISPLAY]]
        yield mock_toonapilib_


async def setup_component(hass):
    """Set up Toon component."""
    with patch("os.path.isfile", return_value=False):
        assert await async_setup_component(hass, DOMAIN, FIXTURE_APP)
        await hass.async_block_till_done()


async def test_abort_if_no_app_configured(hass):
    """Test abort if no app is configured."""
    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user()

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "no_app"


async def test_show_authenticate_form(hass):
    """Test that the authentication form is served."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=None)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"


@pytest.mark.parametrize(
    "side_effect,reason",
    [
        (InvalidConsumerKey, "client_id"),
        (InvalidConsumerSecret, "client_secret"),
        (AgreementsRetrievalError, "no_agreements"),
        (Exception, "unknown_auth_fail"),
    ],
)
async def test_toon_abort(hass, mock_toonapilib, side_effect, reason):
    """Test we abort on Toon error."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass

    mock_toonapilib.Toon.side_effect = side_effect

    result = await flow.async_step_authenticate(user_input=FIXTURE_CREDENTIALS)

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == reason


async def test_invalid_credentials(hass, mock_toonapilib):
    """Test we show authentication form on Toon auth error."""
    mock_toonapilib.Toon.side_effect = InvalidCredentials

    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"
    assert result["errors"] == {"base": "credentials"}


async def test_full_flow_implementation(hass, mock_toonapilib):
    """Test registering an integration and finishing flow works."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    result = await flow.async_step_user(user_input=None)
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "authenticate"

    result = await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "display"

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["title"] == FIXTURE_DISPLAY[CONF_DISPLAY]
    assert result["data"][CONF_USERNAME] == FIXTURE_CREDENTIALS[CONF_USERNAME]
    assert result["data"][CONF_PASSWORD] == FIXTURE_CREDENTIALS[CONF_PASSWORD]
    assert result["data"][CONF_TENANT] == FIXTURE_CREDENTIALS[CONF_TENANT]
    assert result["data"][CONF_DISPLAY] == FIXTURE_DISPLAY[CONF_DISPLAY]


async def test_no_displays(hass, mock_toonapilib):
    """Test abort when there are no displays."""
    await setup_component(hass)

    mock_toonapilib.Toon().display_names = []

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    result = await flow.async_step_display(user_input=None)

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "no_displays"


async def test_display_already_exists(hass, mock_toonapilib):
    """Test showing display form again if display already exists."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    MockConfigEntry(domain=DOMAIN, data=FIXTURE_DISPLAY).add_to_hass(hass)

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "display"
    assert result["errors"] == {"base": "display_exists"}


async def test_abort_last_minute_fail(hass, mock_toonapilib):
    """Test we abort when API communication fails in the last step."""
    await setup_component(hass)

    flow = config_flow.ToonFlowHandler()
    flow.hass = hass
    await flow.async_step_user(user_input=FIXTURE_CREDENTIALS)

    mock_toonapilib.Toon.side_effect = Exception

    result = await flow.async_step_display(user_input=FIXTURE_DISPLAY)
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "unknown_auth_fail"
Tests for the Toon config flow. Mock toonapilib. Set up Toon component. Test abort if no app is configured. Test that the authentication form is served. Test we abort on Toon error. Test we show authentication form on Toon auth error. Test registering an integration and finishing flow works. Test abort when there are no displays. Test showing display form again if display already exists. Test we abort when API communication fails in the last step.