"""Tests for the tools to communicate with the cloud.""" from unittest.mock import MagicMock, patch from botocore.exceptions import ClientError import pytest from homeassistant.components.cloud import DOMAIN, auth_api MOCK_AUTH = { "id_token": "fake_id_token", "access_token": "fake_access_token", "refresh_token": "fake_refresh_token", } @pytest.fixture def cloud_hass(hass): """Fixture to return a hass instance with cloud mode set.""" hass.data[DOMAIN] = {'mode': 'development'} return hass @pytest.fixture def mock_write(): """Mock reading authentication.""" with patch.object(auth_api, '_write_info') as mock: yield mock @pytest.fixture def mock_read(): """Mock writing authentication.""" with patch.object(auth_api, '_read_info') as mock: yield mock @pytest.fixture def mock_cognito(): """Mock warrant.""" with patch('homeassistant.components.cloud.auth_api._cognito') as mock_cog: yield mock_cog() @pytest.fixture def mock_auth(): """Mock warrant.""" with patch('homeassistant.components.cloud.auth_api.Auth') as mock_auth: yield mock_auth() def aws_error(code, message='Unknown', operation_name='fake_operation_name'): """Generate AWS error response.""" response = { 'Error': { 'Code': code, 'Message': message } } return ClientError(response, operation_name) def test_load_auth_with_no_stored_auth(cloud_hass, mock_read): """Test loading authentication with no stored auth.""" mock_read.return_value = None auth = auth_api.load_auth(cloud_hass) assert auth.cognito is None def test_load_auth_with_invalid_auth(cloud_hass, mock_read, mock_cognito): """Test calling load_auth when auth is no longer valid.""" mock_cognito.get_user.side_effect = aws_error('SomeError') auth = auth_api.load_auth(cloud_hass) assert auth.cognito is None def test_load_auth_with_valid_auth(cloud_hass, mock_read, mock_cognito): """Test calling load_auth when valid auth.""" auth = auth_api.load_auth(cloud_hass) assert auth.cognito is not None def test_auth_properties(): """Test Auth class properties.""" auth = auth_api.Auth(None, None) assert not auth.is_logged_in auth.account = {} assert auth.is_logged_in def test_auth_validate_auth_verification_fails(mock_cognito): """Test validate authentication with verify request failing.""" mock_cognito.get_user.side_effect = aws_error('UserNotFoundException') auth = auth_api.Auth(None, mock_cognito) assert auth.validate_auth() is False def test_auth_validate_auth_token_refresh_needed_fails(mock_cognito): """Test validate authentication with refresh needed which gets 401.""" mock_cognito.get_user.side_effect = aws_error('NotAuthorizedException') mock_cognito.renew_access_token.side_effect = \ aws_error('NotAuthorizedException') auth = auth_api.Auth(None, mock_cognito) assert auth.validate_auth() is False def test_auth_validate_auth_token_refresh_needed_succeeds(mock_write, mock_cognito): """Test validate authentication with refresh.""" mock_cognito.get_user.side_effect = [ aws_error('NotAuthorizedException'), MagicMock(email='hello@home-assistant.io') ] auth = auth_api.Auth(None, mock_cognito) assert auth.validate_auth() is True assert len(mock_write.mock_calls) == 1 def test_auth_login_invalid_auth(mock_cognito, mock_write): """Test trying to login with invalid credentials.""" mock_cognito.authenticate.side_effect = aws_error('NotAuthorizedException') auth = auth_api.Auth(None, None) with pytest.raises(auth_api.Unauthenticated): auth.login('user', 'pass') assert not auth.is_logged_in assert len(mock_cognito.get_user.mock_calls) == 0 assert len(mock_write.mock_calls) == 0 def test_auth_login_user_not_found(mock_cognito, mock_write): """Test trying to login with invalid credentials.""" mock_cognito.authenticate.side_effect = aws_error('UserNotFoundException') auth = auth_api.Auth(None, None) with pytest.raises(auth_api.UserNotFound): auth.login('user', 'pass') assert not auth.is_logged_in assert len(mock_cognito.get_user.mock_calls) == 0 assert len(mock_write.mock_calls) == 0 def test_auth_login_user_not_confirmed(mock_cognito, mock_write): """Test trying to login without confirming account.""" mock_cognito.authenticate.side_effect = \ aws_error('UserNotConfirmedException') auth = auth_api.Auth(None, None) with pytest.raises(auth_api.UserNotConfirmed): auth.login('user', 'pass') assert not auth.is_logged_in assert len(mock_cognito.get_user.mock_calls) == 0 assert len(mock_write.mock_calls) == 0 def test_auth_login(cloud_hass, mock_cognito, mock_write): """Test trying to login without confirming account.""" mock_cognito.get_user.return_value = \ MagicMock(email='hello@home-assistant.io') auth = auth_api.Auth(cloud_hass, None) auth.login('user', 'pass') assert auth.is_logged_in assert len(mock_cognito.authenticate.mock_calls) == 1 assert len(mock_write.mock_calls) == 1 result_hass, result_auth = mock_write.mock_calls[0][1] assert result_hass is cloud_hass assert result_auth is auth def test_auth_renew_access_token(mock_write, mock_cognito): """Test renewing an access token.""" auth = auth_api.Auth(None, mock_cognito) assert auth.renew_access_token() assert len(mock_write.mock_calls) == 1 def test_auth_renew_access_token_fails(mock_write, mock_cognito): """Test failing to renew an access token.""" mock_cognito.renew_access_token.side_effect = aws_error('SomeError') auth = auth_api.Auth(None, mock_cognito) assert not auth.renew_access_token() assert len(mock_write.mock_calls) == 0 def test_auth_logout(mock_write, mock_cognito): """Test renewing an access token.""" auth = auth_api.Auth(None, mock_cognito) auth.account = MagicMock() auth.logout() assert auth.account is None assert len(mock_write.mock_calls) == 1 def test_auth_logout_fails(mock_write, mock_cognito): """Test error while logging out.""" mock_cognito.logout.side_effect = aws_error('SomeError') auth = auth_api.Auth(None, mock_cognito) auth.account = MagicMock() with pytest.raises(auth_api.CloudError): auth.logout() assert auth.account is not None assert len(mock_write.mock_calls) == 0 def test_register(mock_cognito): """Test registering an account.""" auth_api.register(None, 'email@home-assistant.io', 'password') assert len(mock_cognito.register.mock_calls) == 1 result_email, result_password = mock_cognito.register.mock_calls[0][1] assert result_email == 'email@home-assistant.io' assert result_password == 'password' def test_register_fails(mock_cognito): """Test registering an account.""" mock_cognito.register.side_effect = aws_error('SomeError') with pytest.raises(auth_api.CloudError): auth_api.register(None, 'email@home-assistant.io', 'password') def test_confirm_register(mock_cognito): """Test confirming a registration of an account.""" auth_api.confirm_register(None, '123456', 'email@home-assistant.io') assert len(mock_cognito.confirm_sign_up.mock_calls) == 1 result_code, result_email = mock_cognito.confirm_sign_up.mock_calls[0][1] assert result_email == 'email@home-assistant.io' assert result_code == '123456' def test_confirm_register_fails(mock_cognito): """Test an error during confirmation of an account.""" mock_cognito.confirm_sign_up.side_effect = aws_error('SomeError') with pytest.raises(auth_api.CloudError): auth_api.confirm_register(None, '123456', 'email@home-assistant.io') def test_forgot_password(mock_cognito): """Test starting forgot password flow.""" auth_api.forgot_password(None, 'email@home-assistant.io') assert len(mock_cognito.initiate_forgot_password.mock_calls) == 1 def test_forgot_password_fails(mock_cognito): """Test failure when starting forgot password flow.""" mock_cognito.initiate_forgot_password.side_effect = aws_error('SomeError') with pytest.raises(auth_api.CloudError): auth_api.forgot_password(None, 'email@home-assistant.io') def test_confirm_forgot_password(mock_cognito): """Test confirming forgot password.""" auth_api.confirm_forgot_password( None, '123456', 'email@home-assistant.io', 'new password') assert len(mock_cognito.confirm_forgot_password.mock_calls) == 1 result_code, result_password = \ mock_cognito.confirm_forgot_password.mock_calls[0][1] assert result_code == '123456' assert result_password == 'new password' def test_confirm_forgot_password_fails(mock_cognito): """Test failure when confirming forgot password.""" mock_cognito.confirm_forgot_password.side_effect = aws_error('SomeError') with pytest.raises(auth_api.CloudError): auth_api.confirm_forgot_password( None, '123456', 'email@home-assistant.io', 'new password')