diff --git a/.pylintrc b/.pylintrc index 9e4eb71..af49c4f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,5 +1,5 @@ # pylint config [MESSAGES CONTROL] -disable=line-too-long, redefined-outer-name, too-many-arguments, too-many-instance-attributes, fixme, invalid-name, superfluous-parens, missing-function-docstring, missing-module-docstring, multiple-imports, no-else-return, too-many-return-statements +disable=line-too-long, redefined-outer-name, too-many-arguments, too-many-instance-attributes, fixme, invalid-name, superfluous-parens, missing-function-docstring, missing-module-docstring, multiple-imports, no-else-return, too-many-return-statements, too-many-branches, too-many-statements [MASTER] ignore-patterns=^test.* diff --git a/check_http_json.py b/check_http_json.py index 3b8d6d9..6b74bd9 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -424,7 +424,7 @@ def parseArgs(args): parser.add_argument('-s', '--ssl', action='store_true', help='use TLS to connect to remote host') parser.add_argument('-H', '--host', dest='host', - required=not ('-V' in sys.argv or '--version' in sys.argv), + required=not ('-V' in args or '--version' in args), help='remote host to query') parser.add_argument('-k', '--insecure', action='store_true', help='do not check server SSL certificate') @@ -524,10 +524,12 @@ def debugPrint(debug_flag, message, pretty_flag=False): print(message) -# Program entry point -if __name__ == "__main__": +def main(cliargs): + """ + Main entrypoint for CLI + """ - args = parseArgs(sys.argv[1:]) + args = parseArgs(cliargs) nagios = NagiosHelper() context = None @@ -607,7 +609,11 @@ if __name__ == "__main__": json_data = response.read() except HTTPError as e: - nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url)) + # Try to recover from HTTP Error, if there is JSON in the response + if "json" in e.info().get_content_subtype(): + json_data = e.read() + else: + nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url)) except URLError as e: nagios.append_critical(" URLError[%s], url:%s" % (str(e.reason), url)) @@ -630,4 +636,9 @@ if __name__ == "__main__": print(nagios.getMessage()) sys.exit(nagios.getCode()) + +if __name__ == "__main__": + # Program entry point + main(sys.argv[1:]) + #EOF diff --git a/makefile b/makefile new file mode 100644 index 0000000..3f3e98b --- /dev/null +++ b/makefile @@ -0,0 +1,9 @@ +.PHONY: lint test coverage + +lint: + python3 -m pylint check_http_json.py +test: + python3 -m unittest discover +coverage: + python3 -m coverage run -m unittest discover + python3 -m coverage report -m --include check_http_json.py diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..6467d27 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,2 @@ +coverage==5.0.3 +pylint==2.4.4 diff --git a/test/test_cli.py b/test/test_cli.py new file mode 100644 index 0000000..8766ef5 --- /dev/null +++ b/test/test_cli.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + + +import unittest +import unittest.mock as mock +import sys +import os + +sys.path.append('..') + +from check_http_json import debugPrint + + +class CLITest(unittest.TestCase): + """ + Tests for CLI + """ + + def setUp(self): + """ + Defining the exitcodes + """ + + self.exit_0 = 0 << 8 + self.exit_1 = 1 << 8 + self.exit_2 = 2 << 8 + self.exit_3 = 3 << 8 + + def test_debugprint(self): + with mock.patch('builtins.print') as mock_print: + debugPrint(True, 'debug') + mock_print.assert_called_once_with('debug') + + def test_debugprint_pprint(self): + with mock.patch('check_http_json.pprint') as mock_pprint: + debugPrint(True, 'debug', True) + mock_pprint.assert_called_once_with('debug') + + def test_cli_without_params(self): + + command = '/usr/bin/env python3 check_http_json.py > /dev/null 2>&1' + status = os.system(command) + + self.assertEqual(status, self.exit_2) diff --git a/test/test_main.py b/test/test_main.py index 873c62d..47d77c7 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -8,37 +8,90 @@ import os sys.path.append('..') -from check_http_json import debugPrint +from check_http_json import main + + +class MockResponse(): + def __init__(self, status_code=200, content='{}'): + self.status_code = status_code + self.content = content + + def read(self): + return self.content class MainTest(unittest.TestCase): """ - Tests for main + Tests for Main """ - def setUp(self): - """ - Defining the exitcodes - """ + @mock.patch('builtins.print') + def test_main_version(self, mock_print): + args = ['--version'] - self.exit_0 = 0 << 8 - self.exit_1 = 1 << 8 - self.exit_2 = 2 << 8 - self.exit_3 = 3 << 8 + with self.assertRaises(SystemExit) as test: + main(args) - def test_debugprint(self): - with mock.patch('builtins.print') as mock_print: - debugPrint(True, 'debug') - mock_print.assert_called_once_with('debug') + mock_print.assert_called_once() + self.assertEqual(test.exception.code, 0) - def test_debugprint_pprint(self): - with mock.patch('check_http_json.pprint') as mock_pprint: - debugPrint(True, 'debug', True) - mock_pprint.assert_called_once_with('debug') + @mock.patch('builtins.print') + @mock.patch('urllib.request.urlopen') + def test_main_with_ssl(self, mock_request, mock_print): + args = '-H localhost --ssl'.split(' ') - def test_cli_without_params(self): + mock_request.return_value = MockResponse() - command = '/usr/bin/env python3 check_http_json.py > /dev/null 2>&1' - status = os.system(command) + with self.assertRaises(SystemExit) as test: + main(args) - self.assertEqual(status, self.exit_2) + self.assertEqual(test.exception.code, 0) + + + @mock.patch('builtins.print') + @mock.patch('urllib.request.urlopen') + def test_main_with_parse_error(self, mock_request, mock_print): + args = '-H localhost'.split(' ') + + mock_request.return_value = MockResponse(content='not JSON') + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertTrue('Parser error' in str(mock_print.call_args)) + self.assertEqual(test.exception.code, 3) + + @mock.patch('builtins.print') + def test_main_with_url_error(self, mock_print): + args = '-H localhost'.split(' ') + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertTrue('URLError' in str(mock_print.call_args)) + self.assertEqual(test.exception.code, 3) + + @mock.patch('builtins.print') + @mock.patch('urllib.request.urlopen') + def test_main_with_http_error_no_json(self, mock_request, mock_print): + args = '-H localhost'.split(' ') + + mock_request.return_value = MockResponse(content='not JSON', status_code=503) + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertTrue('Parser error' in str(mock_print.call_args)) + self.assertEqual(test.exception.code, 3) + + @mock.patch('builtins.print') + @mock.patch('urllib.request.urlopen') + def test_main_with_http_error_valid_json(self, mock_request, mock_print): + args = '-H localhost'.split(' ') + + mock_request.return_value = MockResponse(status_code=503) + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertEqual(test.exception.code, 0)