From 4c7e5b1bc0f18b07e74333d717955d9825dc0ab9 Mon Sep 17 00:00:00 2001 From: Katerina Pilatova Date: Mon, 12 Jun 2017 18:26:43 +0200 Subject: [PATCH] Unit testing for ini - modifies custom module ini.py in such a way that its individual stages can be easily tested - tests for this module (test_ini.py) are created Change-Id: I0250837cc97705a7eb9eb81395aaeba86292bc89 Partial-Bug: #1594785 (cherry picked from commit dcb49e2ee0d7f7628ffa03720e7f22f38c08f94a) --- tripleo_validations/tests/test_ini.py | 127 ++++++++++++++++++++++++++ validations/library/ini.py | 97 ++++++++++++++------ 2 files changed, 197 insertions(+), 27 deletions(-) create mode 100644 tripleo_validations/tests/test_ini.py diff --git a/tripleo_validations/tests/test_ini.py b/tripleo_validations/tests/test_ini.py new file mode 100644 index 000000000..9400e3eef --- /dev/null +++ b/tripleo_validations/tests/test_ini.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +test_ini +---------------------------------- + +Tests for `ini` module. +""" + + +import os +import tempfile + +from tripleo_validations.tests import base +import validations.library.ini as validation + + +invalid_content = ''' +[DEFAULT# + hello = +''' + +valid_content = ''' +[DEFAULT] +debug=True + +[dhcp] +dhcp_start=192.168.0.1 +dhcp_end=192.168.0.254 + +[secrets] +password=1234 +''' + + +class TestIni(base.TestCase): + + def test_check_file_invalid_path(self): + '''Test ini when path is invalid''' + + msg = validation.check_file('non/existing/path', False) + self.assertEqual("Could not open the ini file: 'non/existing/path'", + msg) + + def test_check_file_ignore_missing(self): + '''Test ini when ignoring missing files''' + + msg = validation.check_file('non/existing/path', True) + self.assertEqual("Could not open the ini file: 'non/existing/path'", + msg) + + def test_check_file_valid_path(self): + '''Test ini when path is valid''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + msg = validation.check_file(tmp_name, False) + tmpfile.close() + + self.assertEqual('', msg) + + def test_get_result_invalid_format(self): + '''Test ini when file format is valid''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(invalid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'section', 'key') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.INVALID_FORMAT, ret) + self.assertEqual("The file '{}' is not in a valid INI format.".format( + tmp_name), msg) + self.assertIsNone(value) + + def test_get_result_key_not_found(self): + '''Test ini when key is not found''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'section', 'key') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.KEY_NOT_FOUND, ret) + self.assertEqual(("There is no key 'key' under the section 'section' " + "in file {}.").format(tmp_name), msg) + self.assertIsNone(value) + + def test_get_result_ok(self): + '''Test ini when key is not found''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'secrets', + 'password') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.OK, ret) + self.assertEqual(("The key 'password' under the section 'secrets'" + " in file {} has the value: '1234'").format( + tmp_name), msg) + self.assertEqual('1234', value) + + def create_tmp_ini(self): + '''Create temporary tmp.ini file, return its full name''' + + path = 'tripleo_validations/tests' + tmpfile = tempfile.NamedTemporaryFile(suffix='.ini', prefix='tmp', + dir=path) + return tmpfile diff --git a/validations/library/ini.py b/validations/library/ini.py index 7cf1c2e6e..f81d7c228 100644 --- a/validations/library/ini.py +++ b/validations/library/ini.py @@ -25,11 +25,60 @@ # # You can register the result and use it later with `{{ my_ini.value }}` -import ConfigParser -from os import path +try: + import configparser as ConfigParser +except ImportError: + import ConfigParser + +from enum import Enum +import os from ansible.module_utils.basic import * # NOQA + +# Possible return values +class ReturnValue(Enum): + OK = 0 + INVALID_FORMAT = 1 + KEY_NOT_FOUND = 2 + + +def check_file(path, ignore_missing): + '''Validate entered path''' + + if not (os.path.exists(path) and os.path.isfile(path)): + return "Could not open the ini file: '{}'".format(path) + else: + return '' + + +def get_result(path, section, key): + '''Get value based on section and key''' + + msg = '' + value = None + config = ConfigParser.SafeConfigParser() + + try: + config.read(path) + except Exception: + msg = "The file '{}' is not in a valid INI format.".format(path) + ret = ReturnValue.INVALID_FORMAT + return (ret, msg, value) + + try: + value = config.get(section, key) + msg = ("The key '{}' under the section '{}' in file {} " + "has the value: '{}'").format(key, section, path, value) + ret = ReturnValue.OK + return (ret, msg, value) + except ConfigParser.Error: + value = None + msg = "There is no key '{}' under the section '{}' in file {}.".format( + key, section, path) + ret = ReturnValue.KEY_NOT_FOUND + return (ret, msg, value) + DOCUMENTATION = ''' --- module: ini @@ -77,36 +126,30 @@ def main(): )) ini_file_path = module.params.get('path') + ignore_missing = module.params.get('ignore_missing_file') - if path.exists(ini_file_path) and path.isfile(ini_file_path): - config = ConfigParser.SafeConfigParser() - try: - config.read(ini_file_path) - except Exception: - module.fail_json(msg="The file '{}' is not in a valid INI format." - .format(ini_file_path)) + # Check that file exists + msg = check_file(ini_file_path, ignore_missing) + if msg != '': + # Opening file failed + if ignore_missing: + module.exit_json(msg=msg, changed=False, value=None) + else: + module.fail_json(msg=msg) + else: + # Try to parse the result from ini file section = module.params.get('section') key = module.params.get('key') - try: - value = config.get(section, key) - msg = ("The key '{}' under the section '{}' in file {} " - "has the value: '{}'" - .format(key, section, ini_file_path, value)) - module.exit_json(msg=msg, changed=False, value=value) - except ConfigParser.Error: - msg = ("There is no key '{}' under the section '{}' in file {}." - .format(key, section, ini_file_path)) - module.exit_json(msg=msg, changed=False, value=None) - else: - missing_file_message = "Could not open the ini file: '{}'".format( - ini_file_path) - if module.params.get('ignore_missing_file'): - module.exit_json(msg=missing_file_message, changed=False, - value=None) - else: - module.fail_json(msg=missing_file_message) + ret, msg, value = get_result(ini_file_path, section, key) + + if ret == ReturnValue.INVALID_FORMAT: + module.fail_json(msg=msg) + elif ret == ReturnValue.KEY_NOT_FOUND: + module.exit_json(msg=msg, changed=False, value=None) + elif ret == ReturnValue.OK: + module.exit_json(msg=msg, changed=False, value=value) if __name__ == '__main__':