diff options
author | Matt Davis <nitzmahone@users.noreply.github.com> | 2016-10-02 08:03:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-02 08:03:42 -0700 |
commit | aa0ad073b875b386213c840a6cfd5791b8b51636 (patch) | |
tree | 657ba4c96f5938f93da9dd5d1637e4952eddc574 /test | |
parent | 657506cddd1921b349d6ec593135bc39395fc006 (diff) | |
download | ansible-aa0ad073b875b386213c840a6cfd5791b8b51636.tar.gz |
bugfixes to JSON junk filter, added unit/integration tests to exercise (#17834)
Diffstat (limited to 'test')
5 files changed, 192 insertions, 39 deletions
diff --git a/test/integration/roles/test_async/library/async_test.py b/test/integration/roles/test_async/library/async_test.py new file mode 100644 index 0000000000..5c77a27c8d --- /dev/null +++ b/test/integration/roles/test_async/library/async_test.py @@ -0,0 +1,39 @@ +import sys +import json +from ansible.module_utils.basic import AnsibleModule + +def main(): + if "--interactive" in sys.argv: + import ansible.module_utils.basic + ansible.module_utils.basic._ANSIBLE_ARGS = json.dumps(dict( + ANSIBLE_MODULE_ARGS=dict( + fail_mode="graceful" + ) + )) + + module = AnsibleModule(argument_spec = dict( + fail_mode = dict(type='list', default=['success']) + ) + ) + + result = dict(changed=True) + + fail_mode = module.params['fail_mode'] + + try: + if 'leading_junk' in fail_mode: + print("leading junk before module output") + + if 'graceful' in fail_mode: + module.fail_json(msg="failed gracefully") + + if 'exception' in fail_mode: + raise Exception('failing via exception') + + module.exit_json(**result) + + finally: + if 'trailing_junk' in fail_mode: + print("trailing junk after module output") + +main()
\ No newline at end of file diff --git a/test/integration/roles/test_async/tasks/main.yml b/test/integration/roles/test_async/tasks/main.yml index 8aa8f60ece..c6739dc256 100644 --- a/test/integration/roles/test_async/tasks/main.yml +++ b/test/integration/roles/test_async/tasks/main.yml @@ -87,3 +87,68 @@ assert: that: - fnf_result.finished + +- name: test graceful module failure + async_test: + fail_mode: graceful + async: 30 + poll: 1 + register: async_result + ignore_errors: true + +- name: assert task failed correctly + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result | changed == false + - async_result | failed + - async_result.msg == 'failed gracefully' + +- name: test exception module failure + async_test: + fail_mode: exception + async: 5 + poll: 1 + register: async_result + ignore_errors: true + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == false + - async_result | failed == true + - async_result.stderr is search('failing via exception', multiline=True) + +- name: test leading junk before JSON + async_test: + fail_mode: leading_junk + async: 5 + poll: 1 + register: async_result + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == true + - async_result | success + +- name: test trailing junk after JSON + async_test: + fail_mode: trailing_junk + async: 5 + poll: 1 + register: async_result + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == true + - async_result | success + - async_result.warnings[0] is search('trailing junk after module output') diff --git a/test/units/module_utils/json_utils/__init__.py b/test/units/module_utils/json_utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/units/module_utils/json_utils/__init__.py diff --git a/test/units/module_utils/json_utils/test_filter_non_json_lines.py b/test/units/module_utils/json_utils/test_filter_non_json_lines.py new file mode 100644 index 0000000000..b111cda040 --- /dev/null +++ b/test/units/module_utils/json_utils/test_filter_non_json_lines.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# (c) 2016, Matt Davis <mdavis@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division) +__metaclass__ = type + +import json + +from ansible.compat.tests import unittest +from nose.tools import eq_, raises + +from ansible.module_utils.json_utils import _filter_non_json_lines + +class TestAnsibleModuleExitJson(unittest.TestCase): + single_line_json_dict = u"""{"key": "value", "olá": "mundo"}""" + single_line_json_array = u"""["a","b","c"]""" + multi_line_json_dict = u"""{ +"key":"value" +}""" + multi_line_json_array = u"""[ +"a", +"b", +"c"]""" + + all_inputs = [single_line_json_dict, + single_line_json_array, + multi_line_json_dict, + multi_line_json_array] + + junk = [u"single line of junk", u"line 1/2 of junk\nline 2/2 of junk"] + + unparsable_cases = ( + u'No json here', + u'"olá": "mundo"', + u'{"No json": "ending"', + u'{"wrong": "ending"]', + u'["wrong": "ending"}', + ) + + def test_just_json(self): + for i in self.all_inputs: + filtered, warnings = _filter_non_json_lines(i) + self.assertEquals(filtered, i) + self.assertEquals(warnings, []) + + def test_leading_junk(self): + for i in self.all_inputs: + for j in self.junk: + filtered, warnings = _filter_non_json_lines(j + "\n" + i) + self.assertEquals(filtered, i) + self.assertEquals(warnings, []) + + def test_trailing_junk(self): + for i in self.all_inputs: + for j in self.junk: + filtered, warnings = _filter_non_json_lines(i + "\n" + j) + self.assertEquals(filtered, i) + self.assertEquals(warnings, [u"Module invocation had junk after the JSON data: %s" % j.strip()]) + + def test_leading_and_trailing_junk(self): + for i in self.all_inputs: + for j in self.junk: + filtered, warnings = _filter_non_json_lines("\n".join([j, i, j])) + self.assertEquals(filtered, i) + self.assertEquals(warnings, [u"Module invocation had junk after the JSON data: %s" % j.strip()]) + + def test_unparsable_filter_non_json_lines(self): + for i in self.unparsable_cases: + self.assertRaises(ValueError, + lambda data: _filter_non_json_lines(data), + data=i + ) diff --git a/test/units/plugins/action/test_action.py b/test/units/plugins/action/test_action.py index d9b2d33864..091b50fb88 100644 --- a/test/units/plugins/action/test_action.py +++ b/test/units/plugins/action/test_action.py @@ -556,42 +556,3 @@ class TestActionBase(unittest.TestCase): play_context.make_become_cmd.assert_called_once_with("ECHO SAME", executable=None) finally: C.BECOME_ALLOW_SAME_USER = become_allow_same_user - - -# Note: Using nose's generator test cases here so we can't inherit from -# unittest.TestCase -class TestFilterNonJsonLines(object): - parsable_cases = ( - (u'{"hello": "world"}', u'{"hello": "world"}'), - (u'{"hello": "world"}\n', u'{"hello": "world"}'), - (u'{"hello": "world"} ', u'{"hello": "world"} '), - (u'{"hello": "world"} \n', u'{"hello": "world"} '), - (u'Message of the Day\n{"hello": "world"}', u'{"hello": "world"}'), - (u'{"hello": "world"}\nEpilogue', u'{"hello": "world"}'), - (u'Several\nStrings\nbefore\n{"hello": "world"}\nAnd\nAfter\n', u'{"hello": "world"}'), - (u'{"hello": "world",\n"olá": "mundo"}', u'{"hello": "world",\n"olá": "mundo"}'), - (u'\nPrecedent\n{"hello": "world",\n"olá": "mundo"}\nAntecedent', u'{"hello": "world",\n"olá": "mundo"}'), - ) - - unparsable_cases = ( - u'No json here', - u'"olá": "mundo"', - u'{"No json": "ending"', - u'{"wrong": "ending"]', - u'["wrong": "ending"}', - ) - - def check_filter_non_json_lines(self, stdout_line, parsed): - eq_(parsed, ActionBase._filter_non_json_lines(stdout_line)) - - def test_filter_non_json_lines(self): - for stdout_line, parsed in self.parsable_cases: - yield self.check_filter_non_json_lines, stdout_line, parsed - - @raises(ValueError) - def check_unparsable_filter_non_json_lines(self, stdout_line): - ActionBase._filter_non_json_lines(stdout_line) - - def test_unparsable_filter_non_json_lines(self): - for stdout_line in self.unparsable_cases: - yield self.check_unparsable_filter_non_json_lines, stdout_line |