# -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type import re from unittest import TestCase from ansible.playbook.task import Task from ansible.template import Templar from ansible_collections.ansible.utils.plugins.action.fact_diff import ActionModule try: from unittest.mock import MagicMock # pylint:disable=syntax-error except ImportError: from mock import MagicMock # pyright: ignore[reportMissingModuleSource] class TestUpdate_Fact(TestCase): def setUp(self): task = MagicMock(Task) # Ansible > 2.13 looks for check_mode in task task.check_mode = False play_context = MagicMock() # Ansible <= 2.13 looks for check_mode in play_context play_context.check_mode = False connection = MagicMock() fake_loader = {} templar = Templar(loader=fake_loader) self._plugin = ActionModule( task=task, connection=connection, play_context=play_context, loader=fake_loader, templar=templar, shared_loader_obj=None, ) self._plugin._task.action = "fact_diff" self._task_vars = {"inventory_hostname": "mockdevice"} def test_argspec_no_updates(self): """Check passing invalid argspec""" self._plugin._task.args = {"before": True} result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["failed"]) self.assertIn("missing required arguments: after", result["msg"]) def test_same(self): """Ensure two equal string don't create a diff""" before = "Lorem ipsum dolor sit amet" after = before self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_string(self): """Compare two strings""" before = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" after = "Lorem ipsum dolor sit amet, AAA consectetur adipiscing elit" self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["changed"]) self.assertIn("-" + before, result["diff_lines"]) self.assertIn("-" + before, result["diff_text"]) self.assertIn("+" + after, result["diff_lines"]) self.assertIn("+" + after, result["diff_text"]) def test_string_skip_lines(self): """Compare two string, with skip_lines""" before = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" after = "Lorem ipsum dolor sit amet, AAA consectetur adipiscing elit" self._plugin._task.args = { "before": before, "after": after, "plugin": {"vars": {"skip_lines": "^Lorem"}}, } result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_same_list(self): """Compare two lists that are the same""" before = [0, 1, 2, 3] after = before self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_diff_list_skip_lines(self): """Compare two lists, with skip_lines""" before = [0, 1, 2] after = [0, 1, 2, 3] self._plugin._task.args = { "before": before, "after": after, "plugin": {"vars": {"skip_lines": "3"}}, } result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_diff_list(self): """Compare two lists with differences""" before = [0, 1, 2, 3] after = [0, 1, 2, 4] self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["changed"]) self.assertIn("-3", result["diff_lines"]) self.assertIn("-3", result["diff_text"]) self.assertIn("+4", result["diff_lines"]) self.assertIn("+4", result["diff_text"]) def test_same_dict(self): """Compare two dicts that are the same""" before = {"a": {"b": {"c": {"d": [0, 1, 2]}}}} after = before self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_diff_dict_skip_lines(self): """Compare two dicts, with skip_lines""" before = {"a": {"b": {"c": {"d": [0, 1, 2]}}}} after = {"a": {"b": {"c": {"d": [0, 1, 2, 3]}}}} self._plugin._task.args = { "before": before, "after": after, "plugin": {"vars": {"skip_lines": "3"}}, } result = self._plugin.run(task_vars=self._task_vars) self.assertFalse(result["changed"]) self.assertEqual([], result["diff_lines"]) self.assertEqual("", result["diff_text"]) def test_diff_dict(self): """Compare two dicts that are different""" before = {"a": {"b": {"c": {"d": [0, 1, 2, 3]}}}} after = {"a": {"b": {"c": {"d": [0, 1, 2, 4]}}}} self._plugin._task.args = {"before": before, "after": after} result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["changed"]) mlines = [line for line in result["diff_lines"] if re.match(r"^-\s+3$", line)] self.assertEqual(1, len(mlines)) mlines = [line for line in result["diff_lines"] if re.match(r"^\+\s+4$", line)] self.assertEqual(1, len(mlines)) def test_invalid_diff_engine_not_collection(self): """Check passing invalid argspec""" self._plugin._task.args = { "before": True, "after": True, "plugin": {"name": "a"}, } result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["failed"]) self.assertIn( "Plugin name should be provided as a full name including collection", result["msg"], ) def test_invalid_diff_engine_not_valid(self): """Check passing invalid argspec""" self._plugin._task.args = { "before": True, "after": True, "plugin": {"name": "a.b.c"}, } result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["failed"]) self.assertIn("Error loading plugin 'a.b.c'", result["msg"]) def test_invalid_regex(self): """Check with invalid regex""" before = True after = False self._plugin._task.args = { "before": before, "after": after, "plugin": {"vars": {"skip_lines": "+"}}, } result = self._plugin.run(task_vars=self._task_vars) self.assertTrue(result["failed"]) self.assertIn("The regex '+', is not valid", result["msg"]) def test_fail_plugin(self): """Simulate a diff plugin failure""" self._plugin._result = {} result = self._plugin._run_diff(None) self.assertIsNone(result) self.assertTrue(self._plugin._result["failed"]) self.assertIn( "'NoneType' object has no attribute 'diff'", self._plugin._result["msg"], )