From 7c6d550123487514781fedcca1e8065172560d3c Mon Sep 17 00:00:00 2001 From: Twangboy Date: Wed, 24 May 2023 18:02:38 -0600 Subject: [PATCH] Add tests --- salt/modules/win_wusa.py | 3 +- salt/states/win_appx.py | 11 +- tests/pytests/unit/modules/test_win_appx.py | 216 +++++++++++++++ .../unit/modules/test_win_servermanager.py | 12 +- tests/pytests/unit/modules/test_win_wusa.py | 245 ++++++++++++++++++ tests/pytests/unit/states/test_win_appx.py | 86 ++++++ tests/pytests/unit/utils/test_win_pwsh.py | 106 ++++++++ tests/unit/modules/test_win_wusa.py | 242 ----------------- 8 files changed, 666 insertions(+), 255 deletions(-) create mode 100644 tests/pytests/unit/modules/test_win_appx.py create mode 100644 tests/pytests/unit/modules/test_win_wusa.py create mode 100644 tests/pytests/unit/states/test_win_appx.py create mode 100644 tests/pytests/unit/utils/test_win_pwsh.py delete mode 100644 tests/unit/modules/test_win_wusa.py diff --git a/salt/modules/win_wusa.py b/salt/modules/win_wusa.py index fb8894ed230..f0c06edddb3 100644 --- a/salt/modules/win_wusa.py +++ b/salt/modules/win_wusa.py @@ -20,6 +20,7 @@ log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = "wusa" +__func_alias__ = {"list_": "list"} def __virtual__(): @@ -181,7 +182,7 @@ def uninstall(path, restart=False): return True -def list(): +def list_(): """ Get a list of updates installed on the machine diff --git a/salt/states/win_appx.py b/salt/states/win_appx.py index 206a06c2159..0f3afc5bdbd 100644 --- a/salt/states/win_appx.py +++ b/salt/states/win_appx.py @@ -26,9 +26,7 @@ def __virtual__(): return __virtualname__ -def absent( - name, query, include_store=False, frameworks=False, deprovision_only=False -): +def absent(name, query, include_store=False, frameworks=False, deprovision_only=False): """ Removes Microsoft Store packages from the system. If the package is part of a bundle, the entire bundle will be removed. @@ -96,8 +94,9 @@ def absent( return ret if __opts__["test"]: - ret["changes"]["removed"] = matches - ret["comment"] = "Matching apps will be removed" + comment = ["The following apps will be removed:"] + comment.extend(matches) + ret["comment"] = "\n- ".join(comment) ret["result"] = None return ret @@ -106,7 +105,7 @@ def absent( query, include_store=include_store, frameworks=frameworks, - deprovision_only=deprovision_only + deprovision_only=deprovision_only, ) if status is None: diff --git a/tests/pytests/unit/modules/test_win_appx.py b/tests/pytests/unit/modules/test_win_appx.py new file mode 100644 index 00000000000..f37ca23be8e --- /dev/null +++ b/tests/pytests/unit/modules/test_win_appx.py @@ -0,0 +1,216 @@ +import pytest + +import salt.modules.win_appx as win_appx +from tests.support.mock import MagicMock, patch + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, +] + + +@pytest.fixture +def configure_loader_modules(): + return {win_appx: {}} + + +def test__pkg_list_empty(): + assert win_appx._pkg_list("") is None + + +def test__pkg_list_single(): + raw = { + "Name": "MicrosoftTeams", + "Version": "22042.702.1226.2352", + "PackageFullName": "MicrosoftTeams_22042.702.1226.2352_x64__8wekyb3d8bbwe", + "PackageFamilyName": "MicrosoftTeams_8wekyb3d8bbwe", + } + assert win_appx._pkg_list(raw=raw) == ["MicrosoftTeams"] + + +def test__pkg_list_multiple(): + raw = [ + { + "Name": "MicrosoftTeams", + "Version": "22042.702.1226.2352", + "PackageFullName": "MicrosoftTeams_22042.702.1226.2352_x64__8wekyb3d8bbwe", + "PackageFamilyName": "MicrosoftTeams_8wekyb3d8bbwe", + }, + { + "Name": "Microsoft.BingWeather", + "Version": "4.53.51361.0", + "PackageFullName": "Microsoft.BingWeather_4.53.51361.0_x64__8wekyb3d8bbwe", + "PackageFamilyName": "Microsoft.BingWeather_8wekyb3d8bbwe", + }, + ] + assert win_appx._pkg_list(raw=raw) == ["MicrosoftTeams", "Microsoft.BingWeather"] + + +def test__pkg_list_single_field(): + raw = { + "Name": "MicrosoftTeams", + "Version": "22042.702.1226.2352", + "PackageFullName": "MicrosoftTeams_22042.702.1226.2352_x64__8wekyb3d8bbwe", + "PackageFamilyName": "MicrosoftTeams_8wekyb3d8bbwe", + } + assert win_appx._pkg_list(raw=raw, field="PackageFamilyName") == [ + "MicrosoftTeams_8wekyb3d8bbwe" + ] + + +def test__pkg_list_multiple_field(): + raw = [ + { + "Name": "MicrosoftTeams", + "Version": "22042.702.1226.2352", + "PackageFullName": "MicrosoftTeams_22042.702.1226.2352_x64__8wekyb3d8bbwe", + "PackageFamilyName": "MicrosoftTeams_8wekyb3d8bbwe", + }, + { + "Name": "Microsoft.BingWeather", + "Version": "4.53.51361.0", + "PackageFullName": "Microsoft.BingWeather_4.53.51361.0_x64__8wekyb3d8bbwe", + "PackageFamilyName": "Microsoft.BingWeather_8wekyb3d8bbwe", + }, + ] + assert win_appx._pkg_list(raw=raw, field="PackageFamilyName") == [ + "MicrosoftTeams_8wekyb3d8bbwe", + "Microsoft.BingWeather_8wekyb3d8bbwe", + ] + + +def test_list(): + mock_run_dict = MagicMock() + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict): + win_appx.list_("*test*") + cmd = [ + "Get-AppxPackage -AllUsers -PackageTypeFilter Bundle -Name *test*", + 'Where-Object {$_.name -notlike "Microsoft.WindowsStore*"}', + "Where-Object -Property IsFramework -eq $false", + "Where-Object -Property NonRemovable -eq $false", + "Sort-Object Name", + ] + mock_run_dict.assert_called_once_with(" | ".join(cmd)) + + +def test_list_field_none(): + mock_run_dict = MagicMock() + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict): + win_appx.list_("*test*", field=None) + cmd = [ + "Get-AppxPackage -AllUsers -PackageTypeFilter Bundle -Name *test*", + 'Where-Object {$_.name -notlike "Microsoft.WindowsStore*"}', + "Where-Object -Property IsFramework -eq $false", + "Where-Object -Property NonRemovable -eq $false", + "Sort-Object Name", + "Select Name, Version, PackageFullName, PackageFamilyName, IsBundle, IsFramework", + ] + mock_run_dict.assert_called_once_with(" | ".join(cmd)) + + +def test_list_other_options_flipped(): + mock_run_dict = MagicMock() + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict): + win_appx.list_("*test*", include_store=True, frameworks=True, bundles=False) + cmd = [ + "Get-AppxPackage -AllUsers -Name *test*", + "Where-Object -Property NonRemovable -eq $false", + "Sort-Object Name", + ] + mock_run_dict.assert_called_once_with(" | ".join(cmd)) + + +def test_remove(): + mock_run_dict = MagicMock() + mock_list_return = { + "Name": "Microsoft.BingWeather", + "PackageFullName": "Microsoft.BingWeather_full_name", + "IsBundle": True, + } + mock_list = MagicMock(return_value=mock_list_return) + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object( + win_appx, "list_", mock_list + ): + assert win_appx.remove("*test*") is True + cmd = "Remove-AppxPackage -AllUsers -Package Microsoft.BingWeather_full_name" + mock_run_dict.assert_called_with(cmd) + + +def test_remove_deprovision_only(): + mock_run_dict = MagicMock() + mock_list_return = { + "Name": "Microsoft.BingWeather", + "PackageFullName": "Microsoft.BingWeather_full_name", + "IsBundle": True, + } + mock_list = MagicMock(return_value=mock_list_return) + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object( + win_appx, "list_", mock_list + ): + assert win_appx.remove("*test*", deprovision_only=True) is True + cmd = "Remove-AppxProvisionedPackage -Online -PackageName Microsoft.BingWeather_full_name" + mock_run_dict.assert_called_with(cmd) + + +def test_remove_non_bundle(): + mock_run_dict = MagicMock() + mock_list_return = [ + { + "Name": "Microsoft.BingWeather", + "PackageFullName": "Microsoft.BingWeather_non_bundle", + "IsBundle": False, + }, + { + "Name": "Microsoft.BingWeather", + "PackageFullName": "Microsoft.BingWeather_bundle", + "IsBundle": True, + }, + ] + mock_list = MagicMock(side_effect=mock_list_return) + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object( + win_appx, "list_", mock_list + ): + assert win_appx.remove("*test*", deprovision_only=True) is True + cmd = "Remove-AppxProvisionedPackage -Online -PackageName Microsoft.BingWeather_bundle" + mock_run_dict.assert_called_with(cmd) + + +def test_remove_not_found_empty_dict(): + mock_run_dict = MagicMock() + mock_list_return = {} + mock_list = MagicMock(return_value=mock_list_return) + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object( + win_appx, "list_", mock_list + ): + assert win_appx.remove("*test*", deprovision_only=True) is None + + +def test_remove_not_found_none(): + mock_run_dict = MagicMock() + mock_list_return = None + mock_list = MagicMock(return_value=mock_list_return) + with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object( + win_appx, "list_", mock_list + ): + assert win_appx.remove("*test*", deprovision_only=True) is None + + +def test_list_deprovisioned(): + mock_list_keys = MagicMock(return_value=["Deprovisioned1", "Deprovisioned2"]) + with patch("salt.utils.win_reg.list_keys", mock_list_keys): + expected = ["Deprovisioned1", "Deprovisioned2"] + assert win_appx.list_deprovisioned() == expected + + +def test_list_deprovisioned_query(): + mock_list_keys = MagicMock(return_value=["Deprovisioned1", "Deprovisioned2"]) + with patch("salt.utils.win_reg.list_keys", mock_list_keys): + expected = ["Deprovisioned1"] + assert win_appx.list_deprovisioned(query="*ed1*") == expected + + +def test_install(): + mock_dism = MagicMock(return_value={"retcode": 0}) + with patch.dict(win_appx.__salt__, {"dism.add_provisioned_package": mock_dism}): + assert win_appx.install("C:\\Test.appx") is True + mock_dism.assert_called_once_with("C:\\Test.appx") diff --git a/tests/pytests/unit/modules/test_win_servermanager.py b/tests/pytests/unit/modules/test_win_servermanager.py index ef00470c0d6..6bd91a82ce1 100644 --- a/tests/pytests/unit/modules/test_win_servermanager.py +++ b/tests/pytests/unit/modules/test_win_servermanager.py @@ -46,9 +46,9 @@ def test_install(): } mock_reboot = MagicMock(return_value=True) - with patch.object( - win_servermanager, "_pshell_json", return_value=mock_out - ), patch.dict(win_servermanager.__salt__, {"system.reboot": mock_reboot}): + with patch("salt.utils.win_pwsh.run_dict", return_value=mock_out), patch.dict( + win_servermanager.__salt__, {"system.reboot": mock_reboot} + ): result = win_servermanager.install("XPS-Viewer") assert result == expected @@ -90,9 +90,9 @@ def test_install_restart(): } mock_reboot = MagicMock(return_value=True) - with patch.object( - win_servermanager, "_pshell_json", return_value=mock_out - ), patch.dict(win_servermanager.__salt__, {"system.reboot": mock_reboot}): + with patch("salt.utils.win_pwsh.run_dict", return_value=mock_out), patch.dict( + win_servermanager.__salt__, {"system.reboot": mock_reboot} + ): result = win_servermanager.install("XPS-Viewer", restart=True) mock_reboot.assert_called_once() assert result == expected diff --git a/tests/pytests/unit/modules/test_win_wusa.py b/tests/pytests/unit/modules/test_win_wusa.py new file mode 100644 index 00000000000..926a99f9fec --- /dev/null +++ b/tests/pytests/unit/modules/test_win_wusa.py @@ -0,0 +1,245 @@ +""" +Test the win_wusa execution module +""" + +import pytest + +import salt.modules.win_wusa as win_wusa +from salt.exceptions import CommandExecutionError +from tests.support.mock import MagicMock, patch + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, +] + + +@pytest.fixture +def configure_loader_modules(): + return {win_wusa: {}} + + +def test_is_installed_false(): + """ + test is_installed function when the KB is not installed + """ + mock_retcode = MagicMock(return_value=1) + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + assert win_wusa.is_installed("KB123456") is False + + +def test_is_installed_true(): + """ + test is_installed function when the KB is installed + """ + mock_retcode = MagicMock(return_value=0) + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + assert win_wusa.is_installed("KB123456") is True + + +def test_list(): + """ + test list function + """ + ret = [{"HotFixID": "KB123456"}, {"HotFixID": "KB123457"}] + mock_all = MagicMock(return_value=ret) + with patch("salt.utils.win_pwsh.run_dict", mock_all): + expected = ["KB123456", "KB123457"] + returned = win_wusa.list_() + assert returned == expected + + +def test_install(): + """ + test install function + """ + mock_retcode = MagicMock(return_value=0) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + assert win_wusa.install(path) is True + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + + +def test_install_restart(): + """ + test install function with restart=True + """ + mock_retcode = MagicMock(return_value=0) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + assert win_wusa.install(path, restart=True) is True + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/forcerestart"], ignore_retcode=True + ) + + +def test_install_already_installed(): + """ + test install function when KB already installed + """ + retcode = 2359302 + mock_retcode = MagicMock(return_value=retcode) + path = "C:\\KB123456.msu" + name = "KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + with pytest.raises(CommandExecutionError) as excinfo: + win_wusa.install(path) + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + assert ( + f"{name} is already installed. Additional info follows:\n\n{retcode}" + == excinfo.value.message + ) + + +def test_install_reboot_needed(): + """ + test install function when KB need a reboot + """ + retcode = 3010 + mock_retcode = MagicMock(return_value=retcode) + path = "C:\\KB123456.msu" + name = "KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + with pytest.raises(CommandExecutionError) as excinfo: + win_wusa.install(path) + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + assert ( + f"{name} correctly installed but server reboot is needed to complete installation. Additional info follows:\n\n{retcode}" + == excinfo.value.message + ) + + +def test_install_error_87(): + """ + test install function when error 87 returned + """ + retcode = 87 + mock_retcode = MagicMock(return_value=retcode) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + with pytest.raises(CommandExecutionError) as excinfo: + win_wusa.install(path) + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + assert ( + f"Unknown error. Additional info follows:\n\n{retcode}" == excinfo.value.message + ) + + +def test_install_error_other(): + """ + test install function on other unknown error + """ + mock_retcode = MagicMock(return_value=1234) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + with pytest.raises(CommandExecutionError) as excinfo: + win_wusa.install(path) + mock_retcode.assert_called_once_with( + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + assert "Unknown error: 1234" == excinfo.value.message + + +def test_uninstall_kb(): + """ + test uninstall function passing kb name + """ + mock_retcode = MagicMock(return_value=0) + kb = "KB123456" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + assert win_wusa.uninstall(kb) is True + mock_retcode.assert_called_once_with( + [ + "wusa.exe", + "/uninstall", + "/quiet", + "/kb:{}".format(kb[2:]), + "/norestart", + ], + ignore_retcode=True, + ) + + +def test_uninstall_path(): + """ + test uninstall function passing full path to .msu file + """ + mock_retcode = MagicMock(return_value=0) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ): + assert win_wusa.uninstall(path) is True + mock_retcode.assert_called_once_with( + ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], + ignore_retcode=True, + ) + + +def test_uninstall_path_restart(): + """ + test uninstall function with full path and restart=True + """ + mock_retcode = MagicMock(return_value=0) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ): + assert win_wusa.uninstall(path, restart=True) is True + mock_retcode.assert_called_once_with( + ["wusa.exe", "/uninstall", "/quiet", path, "/forcerestart"], + ignore_retcode=True, + ) + + +def test_uninstall_already_uninstalled(): + """ + test uninstall function when KB already uninstalled + """ + retcode = 2359303 + mock_retcode = MagicMock(return_value=retcode) + kb = "KB123456" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + with pytest.raises(CommandExecutionError) as excinfo: + win_wusa.uninstall(kb) + mock_retcode.assert_called_once_with( + [ + "wusa.exe", + "/uninstall", + "/quiet", + "/kb:{}".format(kb[2:]), + "/norestart", + ], + ignore_retcode=True, + ) + assert ( + f"{kb} not installed. Additional info follows:\n\n{retcode}" + == excinfo.value.message + ) + + +def test_uninstall_path_error_other(): + """ + test uninstall function with unknown error + """ + mock_retcode = MagicMock(return_value=1234) + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ), pytest.raises(CommandExecutionError) as excinfo: + win_wusa.uninstall(path) + mock_retcode.assert_called_once_with( + ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], + ignore_retcode=True, + ) + assert "Unknown error: 1234" == excinfo.value.message diff --git a/tests/pytests/unit/states/test_win_appx.py b/tests/pytests/unit/states/test_win_appx.py new file mode 100644 index 00000000000..a49f45ac0de --- /dev/null +++ b/tests/pytests/unit/states/test_win_appx.py @@ -0,0 +1,86 @@ +import pytest + +import salt.states.win_appx as win_appx +from tests.support.mock import MagicMock, patch + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, +] + + +@pytest.fixture +def configure_loader_modules(): + return {win_appx: {}} + + +def test_absent_missing(): + expected = { + "comment": "No apps found matching query: *candy*", + "changes": {}, + "name": "remove_candy", + "result": True, + } + mock_list = MagicMock(return_value=["package1", "package2"]) + with patch.dict(win_appx.__salt__, {"appx.list": mock_list}): + result = win_appx.absent("remove_candy", "*candy*") + assert result == expected + + +def test_absent_test_true(): + expected = { + "comment": "The following apps will be removed:\n- king.com.CandyCrush", + "changes": {}, + "name": "remove_candy", + "result": None, + } + mock_list = MagicMock(return_value=["package1", "king.com.CandyCrush"]) + with patch.dict(win_appx.__salt__, {"appx.list": mock_list}): + with patch.dict(win_appx.__opts__, {"test": True}): + result = win_appx.absent("remove_candy", "*candy*") + assert result == expected + + +def test_absent_missing_after_test(): + expected = { + "comment": "No apps found matching query: *candy*", + "changes": {}, + "name": "remove_candy", + "result": False, + } + mock_list = MagicMock(return_value=["package1", "king.com.CandyCrush"]) + with patch.dict( + win_appx.__salt__, + { + "appx.list": mock_list, + "appx.remove": MagicMock(return_value=None), + }, + ): + with patch.dict(win_appx.__opts__, {"test": False}): + result = win_appx.absent("remove_candy", "*candy*") + assert result == expected + + +def test_absent(): + expected = { + "comment": "Removed apps matching query: *candy*", + "changes": {"old": ["king.com.CandyCrush"]}, + "name": "remove_candy", + "result": True, + } + mock_list = MagicMock( + side_effect=( + ["package1", "king.com.CandyCrush"], + ["package1"], + ), + ) + with patch.dict( + win_appx.__salt__, + { + "appx.list": mock_list, + "appx.remove": MagicMock(return_value=True), + }, + ): + with patch.dict(win_appx.__opts__, {"test": False}): + result = win_appx.absent("remove_candy", "*candy*") + assert result == expected diff --git a/tests/pytests/unit/utils/test_win_pwsh.py b/tests/pytests/unit/utils/test_win_pwsh.py new file mode 100644 index 00000000000..add11c97f19 --- /dev/null +++ b/tests/pytests/unit/utils/test_win_pwsh.py @@ -0,0 +1,106 @@ +import pytest + +import salt.utils.win_pwsh as win_pwsh +from salt.exceptions import CommandExecutionError +from tests.support.mock import MagicMock, patch + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, +] + + +def test_run_dict(): + """ + Tests the run_dict function + """ + result = win_pwsh.run_dict("Get-Item C:\\Windows") + assert result["Name"] == "Windows" + assert result["FullName"] == "C:\\Windows" + + +def test_run_dict_json_string(): + """ + Tests the run_dict function with json string + """ + ret = { + "pid": 1, + "retcode": 0, + "stderr": "", + "stdout": '[{"HotFixID": "KB123456"}, {"HotFixID": "KB123457"}]', + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + result = win_pwsh.run_dict("Junk-Command") + assert result == [{"HotFixID": "KB123456"}, {"HotFixID": "KB123457"}] + + +def test_run_dict_empty_return(): + """ + Tests the run_dict function with json string + """ + ret = { + "pid": 1, + "retcode": 0, + "stderr": "", + "stdout": "", + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + result = win_pwsh.run_dict("Junk-Command") + assert result == {} + + +def test_run_dict_stderr(): + ret = { + "pid": 1, + "retcode": 1, + "stderr": "This is an error", + "stdout": "", + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + with pytest.raises(CommandExecutionError) as exc_info: + win_pwsh.run_dict("Junk-Command") + assert "This is an error" in exc_info.value.message + + +def test_run_dict_missing_retcode(): + ret = { + "pid": 1, + "stderr": "", + "stdout": "", + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + with pytest.raises(CommandExecutionError) as exc_info: + win_pwsh.run_dict("Junk-Command") + assert "Issue executing PowerShell" in exc_info.value.message + + +def test_run_dict_retcode_not_zero(): + ret = { + "pid": 1, + "retcode": 1, + "stderr": "", + "stdout": "", + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + with pytest.raises(CommandExecutionError) as exc_info: + win_pwsh.run_dict("Junk-Command") + assert "Issue executing PowerShell" in exc_info.value.message + + +def test_run_dict_invalid_json(): + ret = { + "pid": 1, + "retcode": 0, + "stderr": "", + "stdout": "Invalid Json", + } + mock_all = MagicMock(return_value=ret) + with patch.dict(win_pwsh.__salt__, {"cmd.run_all": mock_all}): + with pytest.raises(CommandExecutionError) as exc_info: + win_pwsh.run_dict("Junk-Command") + assert "No JSON results from PowerShell" in exc_info.value.message diff --git a/tests/unit/modules/test_win_wusa.py b/tests/unit/modules/test_win_wusa.py deleted file mode 100644 index 6577ca8b762..00000000000 --- a/tests/unit/modules/test_win_wusa.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -Test the win_wusa execution module -""" - -import pytest - -import salt.modules.win_wusa as win_wusa -from salt.exceptions import CommandExecutionError -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, patch -from tests.support.unit import TestCase - - -@pytest.mark.skip_unless_on_windows -class WinWusaTestCase(TestCase, LoaderModuleMockMixin): - """ - test the functions in the win_wusa execution module - """ - - def setup_loader_modules(self): - return {win_wusa: {}} - - def test_is_installed_false(self): - """ - test is_installed function when the KB is not installed - """ - mock_retcode = MagicMock(return_value=1) - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - self.assertFalse(win_wusa.is_installed("KB123456")) - - def test_is_installed_true(self): - """ - test is_installed function when the KB is installed - """ - mock_retcode = MagicMock(return_value=0) - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - self.assertTrue(win_wusa.is_installed("KB123456")) - - def test_list(self): - """ - test list function - """ - ret = { - "pid": 1, - "retcode": 0, - "stderr": "", - "stdout": '[{"HotFixID": "KB123456"}, {"HotFixID": "KB123457"}]', - } - mock_all = MagicMock(return_value=ret) - with patch.dict(win_wusa.__salt__, {"cmd.run_all": mock_all}): - expected = ["KB123456", "KB123457"] - returned = win_wusa.list() - self.assertListEqual(expected, returned) - - def test_install(self): - """ - test install function - """ - mock_retcode = MagicMock(return_value=0) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - self.assertTrue(win_wusa.install(path)) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True - ) - - def test_install_restart(self): - """ - test install function with restart=True - """ - mock_retcode = MagicMock(return_value=0) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - self.assertTrue(win_wusa.install(path, restart=True)) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/forcerestart"], ignore_retcode=True - ) - - def test_install_already_installed(self): - """ - test install function when KB already installed - """ - retcode = 2359302 - mock_retcode = MagicMock(return_value=retcode) - path = "C:\\KB123456.msu" - name = "KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - with self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.install(path) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True - ) - self.assertEqual( - "{} is already installed. Additional info follows:\n\n{}".format( - name, retcode - ), - excinfo.exception.strerror, - ) - - def test_install_reboot_needed(self): - """ - test install function when KB need a reboot - """ - retcode = 3010 - mock_retcode = MagicMock(return_value=retcode) - path = "C:\\KB123456.msu" - name = "KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - with self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.install(path) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True - ) - self.assertEqual( - "{} correctly installed but server reboot is needed to complete" - " installation. Additional info follows:\n\n{}".format(name, retcode), - excinfo.exception.strerror, - ) - - def test_install_error_87(self): - """ - test install function when error 87 returned - """ - retcode = 87 - mock_retcode = MagicMock(return_value=retcode) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - with self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.install(path) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True - ) - self.assertEqual( - "Unknown error. Additional info follows:\n\n{}".format(retcode), - excinfo.exception.strerror, - ) - - def test_install_error_other(self): - """ - test install function on other unknown error - """ - mock_retcode = MagicMock(return_value=1234) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - with self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.install(path) - mock_retcode.assert_called_once_with( - ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True - ) - self.assertEqual("Unknown error: 1234", excinfo.exception.strerror) - - def test_uninstall_kb(self): - """ - test uninstall function passing kb name - """ - mock_retcode = MagicMock(return_value=0) - kb = "KB123456" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( - "os.path.exists", MagicMock(return_value=False) - ): - self.assertTrue(win_wusa.uninstall(kb)) - mock_retcode.assert_called_once_with( - [ - "wusa.exe", - "/uninstall", - "/quiet", - "/kb:{}".format(kb[2:]), - "/norestart", - ], - ignore_retcode=True, - ) - - def test_uninstall_path(self): - """ - test uninstall function passing full path to .msu file - """ - mock_retcode = MagicMock(return_value=0) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( - "os.path.exists", MagicMock(return_value=True) - ): - self.assertTrue(win_wusa.uninstall(path)) - mock_retcode.assert_called_once_with( - ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], - ignore_retcode=True, - ) - - def test_uninstall_path_restart(self): - """ - test uninstall function with full path and restart=True - """ - mock_retcode = MagicMock(return_value=0) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( - "os.path.exists", MagicMock(return_value=True) - ): - self.assertTrue(win_wusa.uninstall(path, restart=True)) - mock_retcode.assert_called_once_with( - ["wusa.exe", "/uninstall", "/quiet", path, "/forcerestart"], - ignore_retcode=True, - ) - - def test_uninstall_already_uninstalled(self): - """ - test uninstall function when KB already uninstalled - """ - retcode = 2359303 - mock_retcode = MagicMock(return_value=retcode) - kb = "KB123456" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): - with self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.uninstall(kb) - mock_retcode.assert_called_once_with( - [ - "wusa.exe", - "/uninstall", - "/quiet", - "/kb:{}".format(kb[2:]), - "/norestart", - ], - ignore_retcode=True, - ) - self.assertEqual( - "{} not installed. Additional info follows:\n\n{}".format(kb, retcode), - excinfo.exception.strerror, - ) - - def test_uninstall_path_error_other(self): - """ - test uninstall function with unknown error - """ - mock_retcode = MagicMock(return_value=1234) - path = "C:\\KB123456.msu" - with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( - "os.path.exists", MagicMock(return_value=True) - ), self.assertRaises(CommandExecutionError) as excinfo: - win_wusa.uninstall(path) - mock_retcode.assert_called_once_with( - ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], - ignore_retcode=True, - ) - self.assertEqual("Unknown error: 1234", excinfo.exception.strerror)