From acba9dd8a23cc00052d688da63a10308d065ac7f Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Fri, 12 Mar 2021 14:22:32 -0500 Subject: [PATCH] Added exception handling + more test coverage --- tests/test_login.py | 305 ++++++++++++++++++++++++++++++++++++++++ ultrasync/config.py | 1 - ultrasync/exceptions.py | 39 +++++ ultrasync/main.py | 47 +++++-- 4 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 tests/test_login.py create mode 100644 ultrasync/exceptions.py diff --git a/tests/test_login.py b/tests/test_login.py new file mode 100644 index 0000000..641bc68 --- /dev/null +++ b/tests/test_login.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 Chris Caron +# All rights reserved. +# +# This code is licensed under the MIT License. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files(the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions : +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import mock +import requests +import pytest +from inspect import cleandoc + +from ultrasync import UltraSync +from ultrasync.exceptions import ( + UltraSyncAuthentication, UltraSyncUnsupported) + +# Disable logging for a cleaner testing output +import logging +logging.disable(logging.CRITICAL) + + +@mock.patch('requests.Session.post') +def test_login(mock_post): + """ + Test Login and all forms of it's error handling + + """ + + # A area response object + arobj = mock.Mock() + arobj.status_code = requests.codes.unauthorized + arobj.content = b"" + + # Our UltraSync Object + uobj = UltraSync() + + # We're already logged out, so this will be successful + assert uobj.logout() is True + + # We'll fail to login because there is no content + mock_post.return_value = arobj + assert uobj.login() is False + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + with pytest.raises(UltraSyncAuthentication): + uobj.login(raise_on_error=True) + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # Even with positive status codes, we'll fail because there + # is no content to extract authentication information from + arobj.status_code = requests.codes.ok + arobj.content = b"" + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + assert uobj.login() is False + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + with pytest.raises(UltraSyncAuthentication): + uobj.login(raise_on_error=True) + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # empty session id + # so that we satisfy a login + arobj.content = cleandoc(""" + + function getSession(){return "A2D6C62695D705D8";} + """).encode('utf-8') + + # We will however fail ot detect the model type at this point + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + assert uobj.login() is False + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + with pytest.raises(UltraSyncUnsupported): + uobj.login(raise_on_error=True) + + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # We want to write the bare minimum to our area/login.cgi + # so that we satisfy a login + arobj.content = cleandoc(""" + + function getSession(){return "A2D6C62695D705D8";} + """).encode('utf-8') + + # We will however fail ot detect the model type at this point + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + assert uobj.login() is False + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # Reset our mock object + mock_post.reset_mock() + mock_post.return_value = arobj + with pytest.raises(UltraSyncUnsupported): + uobj.login(raise_on_error=True) + + assert mock_post.call_count == 1 + assert mock_post.call_args_list[0][0][0] == \ + 'http://zerowire/login.cgi' + + # We want to write the bare minimum to our area/login.cgi + # so that we satisfy a login and the model (XX) which is unsupported + arobj.content = cleandoc(""" + +