feat(macdefaults): Improve macOS defaults support

This commit is contained in:
Carlos Álvaro 2024-05-05 20:47:34 +02:00 committed by Daniel Wozniak
parent 22611256aa
commit 19cbf09521
4 changed files with 608 additions and 77 deletions

View file

@ -1,11 +1,21 @@
"""
Set defaults on Mac OS
Set defaults on macOS.
This module uses defaults cli under the hood to read and write defaults on macOS.
So the module is limited to the capabilities of the defaults command.
Read macOS defaults help page for more information on the defaults command.
"""
import logging
import re
import salt.utils.data
import salt.utils.platform
import salt.utils.versions
from salt.exceptions import CommandExecutionError
log = logging.getLogger(__name__)
__virtualname__ = "macdefaults"
@ -13,16 +23,21 @@ __virtualname__ = "macdefaults"
def __virtual__():
"""
Only work on Mac OS
Only works on macOS
"""
if salt.utils.platform.is_darwin():
return __virtualname__
return False
def write(domain, key, value, type="string", user=None):
def write(domain, key, value, vtype=None, user=None, type=None):
"""
Write a default to the system
Write a default to the system.
Limitations:
- There is no multi-level support for arrays and dictionaries.
- Internal values types for arrays and dictionaries cannot be specified.
CLI Example:
@ -33,31 +48,58 @@ def write(domain, key, value, type="string", user=None):
salt '*' macdefaults.write NSGlobalDomain ApplePersistence True type=bool
domain
The name of the domain to write to
The name of the domain to write to.
key
The key of the given domain to write to
The key of the given domain to write to.
value
The value to write to the given key
The value to write to the given key.
type
vtype
The type of value to be written, valid types are string, data, int[eger],
float, bool[ean], date, array, array-add, dict, dict-add
user
The user to write the defaults to
type
Deprecated! Use vtype instead.
type collides with Python's built-in type() function.
This parameter will be removed in 3009.
user
The user to write the defaults to.
"""
if type == "bool" or type == "boolean":
if value is True:
value = "TRUE"
elif value is False:
value = "FALSE"
if type is not None:
salt.utils.versions.warn_until(
3009,
"The 'type' argument in macdefaults.write is deprecated. Use 'vtype' instead.",
)
if vtype is None:
vtype = type
else:
log.warning(
"The 'vtype' argument in macdefaults.write takes precedence over 'type'."
)
cmd = f'defaults write "{domain}" "{key}" -{type} "{value}"'
return __salt__["cmd.run_all"](cmd, runas=user)
if vtype is None:
vtype = "string"
if vtype in ("bool", "boolean"):
value = _convert_to_defaults_boolean(value)
if isinstance(value, dict):
value = list((k, v) for k, v in value.items())
value = salt.utils.data.flatten(value)
elif isinstance(value, (int, float, bool, str)):
value = [value]
elif not isinstance(value, list):
raise ValueError("Value must be a list, dict, int, float, bool, or string")
# Quote values that are not integers or floats
value = map(lambda v: str(v) if isinstance(v, (int, float)) else f'"{v}"', value)
cmd = f'write "{domain}" "{key}" -{vtype} {" ".join(value)}'
return _run_defaults_cmd(cmd, runas=user)
def read(domain, key, user=None):
@ -82,8 +124,21 @@ def read(domain, key, user=None):
The user to read the defaults as
"""
cmd = f'defaults read "{domain}" "{key}"'
return __salt__["cmd.run"](cmd, runas=user)
cmd = f'read "{domain}" "{key}"'
ret = _run_defaults_cmd(cmd, runas=user)
if ret["retcode"] != 0:
if "does not exist" in ret["stderr"]:
return None
raise CommandExecutionError(f"Failed to read default: {ret['stderr']}")
# Type cast the value
try:
vtype = read_type(domain, key, user)
except CommandExecutionError:
vtype = None
return _default_to_python(ret["stdout"].strip(), vtype)
def delete(domain, key, user=None):
@ -108,5 +163,206 @@ def delete(domain, key, user=None):
The user to delete the defaults with
"""
cmd = f'defaults delete "{domain}" "{key}"'
return __salt__["cmd.run_all"](cmd, runas=user, output_loglevel="debug")
cmd = f'delete "{domain}" "{key}"'
return _run_defaults_cmd(cmd, runas=user)
def read_type(domain, key, user=None):
"""
Read the type of the given type.
If the given key is not found, then return None.
CLI Example:
.. code-block:: bash
salt '*' macdefaults.read-type com.apple.CrashReporter DialogType
salt '*' macdefaults.read_type NSGlobalDomain ApplePersistence
domain
The name of the domain to read from.
key
The key of the given domain to read the type of.
user
The user to read the defaults as.
"""
cmd = f'read-type "{domain}" "{key}"'
ret = _run_defaults_cmd(cmd, runas=user)
if ret["retcode"] != 0:
if "does not exist" in ret["stderr"]:
return None
raise CommandExecutionError(f"Failed to read type: {ret['stderr']}")
return re.sub(r"^Type is ", "", ret["stdout"].strip())
def _default_to_python(value, vtype=None):
"""
Cast a value returned by defaults in vytpe to Python type.
CLI Example:
.. code-block:: bash
salt '*' macdefaults.cast_value_to_type "1" int
salt '*' macdefaults.cast_value_to_type "1.0" float
salt '*' macdefaults.cast_value_to_type "TRUE" bool
value
The value to cast.
vtype
The type to cast the value to.
"""
if vtype in ["integer", "int"]:
return int(value)
if vtype == "float":
return float(value)
if vtype in ["boolean", "bool"]:
return value in ["1", "TRUE", "YES"]
if vtype == "array":
return _parse_defaults_array(value)
if vtype in ["dict", "dictionary"]:
return _parse_defaults_dict(value)
return value
def _parse_defaults_array(value):
"""
Parse an array from a string returned by `defaults read`
and returns the array content as a list.
value
A multiline string with the array content, including the surrounding parenthesis.
"""
lines = value.splitlines()
if not re.match(r"\s*\(", lines[0]) or not re.match(r"\s*\)", lines[-1]):
raise ValueError("Invalid array format")
lines = lines[1:-1]
# Remove leading and trailing spaces
lines = list(map(lambda line: line.strip(), lines))
# Remove trailing commas
lines = list(map(lambda line: re.sub(r",?$", "", line), lines))
# Remove quotes
lines = list(map(lambda line: line.strip('"'), lines))
# Convert to numbers if possible
lines = list(map(_convert_to_number_if_possible, lines))
return lines
def _parse_defaults_dict(value):
"""
Parse a dictionary from a string returned by `defaults read`
value (str):
A multiline string with the dictionary content, including the surrounding curly braces.
Returns:
dict: The dictionary content as a Python dictionary.
"""
lines = value.splitlines()
if not re.match(r"\s*\{", lines[0]) or not re.match(r"\s*\}", lines[-1]):
raise ValueError("Invalid dictionary format")
contents = {}
lines = list(map(lambda line: line.strip(), lines[1:-1]))
for line in lines:
key, value = re.split(r"\s*=\s*", line.strip())
if re.match(r"\s*(\(|\{)", value):
raise ValueError("Nested arrays and dictionaries are not supported")
value = re.sub(r";?$", "", value)
contents[key] = _convert_to_number_if_possible(value.strip('"'))
return contents
def _convert_to_number_if_possible(value):
"""
Convert a string to a number if possible.
value
The string to convert.
"""
try:
return int(value)
except ValueError:
try:
return float(value)
except ValueError:
return value
def _convert_to_defaults_boolean(value):
"""
Convert a boolean to a string that can be used with the defaults command.
value
The boolean value to convert.
"""
if value is True:
return "TRUE"
if value is False:
return "FALSE"
BOOLEAN_ALLOWED_VALUES = ["TRUE", "YES", "FALSE", "NO"]
if value not in BOOLEAN_ALLOWED_VALUES:
msg = "Value must be a boolean or a string of "
msg += ", ".join(BOOLEAN_ALLOWED_VALUES)
raise ValueError(msg)
return value
def _run_defaults_cmd(action, runas=None):
"""
Run a 'defaults' command.
action
The action to perform with all of its parameters.
Example: 'write com.apple.CrashReporter DialogType "Server"'
runas
The user to run the command as.
"""
ret = __salt__["cmd.run_all"](f"defaults {action}", runas=runas)
# Remove timestamp from stderr if found
if ret["retcode"] != 0:
ret["stderr"] = _remove_timestamp(ret["stderr"])
return ret
def _remove_timestamp(text):
"""
Remove the timestamp from the output of the defaults command.
text
The text to remove the timestamp from.
"""
pattern = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{3})?\s+defaults\[\d+\:\d+\]"
if re.match(pattern, text):
text_lines = text.strip().splitlines()
return "\n".join(text_lines[1:])
return text

View file

@ -5,6 +5,7 @@ Writing/reading defaults from a macOS minion
"""
import logging
import re
import salt.utils.platform
@ -15,11 +16,11 @@ __virtualname__ = "macdefaults"
def __virtual__():
"""
Only work on Mac OS
Only work on macOS
"""
if salt.utils.platform.is_darwin():
return __virtualname__
return (False, "Only supported on Mac OS")
return (False, "Only supported on macOS")
def write(name, domain, value, vtype="string", user=None):
@ -46,30 +47,16 @@ def write(name, domain, value, vtype="string", user=None):
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
def safe_cast(val, to_type, default=None):
try:
return to_type(val)
except ValueError:
return default
current_value = __salt__["macdefaults.read"](domain, name, user)
value = _cast_value(value, vtype)
if (vtype in ["bool", "boolean"]) and (
(value in [True, "TRUE", "YES"] and current_value == "1")
or (value in [False, "FALSE", "NO"] and current_value == "0")
):
ret["comment"] += f"{domain} {name} is already set to {value}"
elif vtype in ["int", "integer"] and safe_cast(current_value, int) == safe_cast(
value, int
):
ret["comment"] += f"{domain} {name} is already set to {value}"
elif current_value == value:
if _compare_values(value, current_value, strict=re.match(r"-add$", vtype) is None):
ret["comment"] += f"{domain} {name} is already set to {value}"
else:
out = __salt__["macdefaults.write"](domain, name, value, vtype, user)
if out["retcode"] != 0:
ret["result"] = False
ret["comment"] = "Failed to write default. {}".format(out["stdout"])
ret["comment"] = f"Failed to write default. {out['stdout']}"
else:
ret["changes"]["written"] = f"{domain} {name} is set to {value}"
@ -101,3 +88,49 @@ def absent(name, domain, user=None):
ret["changes"]["absent"] = f"{domain} {name} is now absent"
return ret
def _compare_values(new, current, strict=True):
"""
Compare two values
new
The new value to compare
current
The current value to compare
strict
If True, the values must be exactly the same, if False, the new value
must be in the current value
"""
if strict:
return new == current
return new in current
def _cast_value(value, vtype):
def safe_cast(val, to_type, default=None):
try:
return to_type(val)
except ValueError:
return default
if vtype in ("bool", "boolean"):
if value not in [True, "TRUE", "YES", False, "FALSE", "NO"]:
raise ValueError(f"Invalid value for boolean: {value}")
return value in [True, "TRUE", "YES"]
if vtype in ("int", "integer"):
return safe_cast(value, int)
if vtype == "float":
return safe_cast(value, float)
if vtype in ("dict", "dict-add"):
return safe_cast(value, dict)
if vtype in ["array", "array-add"]:
return safe_cast(value, list)
return value

View file

@ -1,7 +1,7 @@
import pytest
import salt.modules.macdefaults as macdefaults
from tests.support.mock import MagicMock, patch
from tests.support.mock import MagicMock, call, patch
@pytest.fixture
@ -9,15 +9,45 @@ def configure_loader_modules():
return {macdefaults: {}}
def test_run_defaults_cmd():
"""
Test caling _run_defaults_cmd
"""
mock = MagicMock(return_value={"retcode": 0, "stdout": "Server", "stderr": ""})
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
result = macdefaults._run_defaults_cmd(
'read "com.apple.CrashReporter" "DialogType"'
)
mock.assert_called_once_with(
'defaults read "com.apple.CrashReporter" "DialogType"', runas=None
)
assert result == {"retcode": 0, "stdout": "Server", "stderr": ""}
def test_run_defaults_cmd_with_user():
"""
Test caling _run_defaults_cmd
"""
mock = MagicMock(return_value={"retcode": 0, "stdout": "Server", "stderr": ""})
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
result = macdefaults._run_defaults_cmd(
'read "com.apple.CrashReporter" "DialogType"', runas="frank"
)
mock.assert_called_once_with(
'defaults read "com.apple.CrashReporter" "DialogType"', runas="frank"
)
assert result == {"retcode": 0, "stdout": "Server", "stderr": ""}
def test_write_default():
"""
Test writing a default setting
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "DialogType", "Server")
mock.assert_called_once_with(
'defaults write "com.apple.CrashReporter" "DialogType" -string "Server"',
'write "com.apple.CrashReporter" "DialogType" -string "Server"',
runas=None,
)
@ -26,26 +56,108 @@ def test_write_with_user():
"""
Test writing a default setting with a specific user
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write(
"com.apple.CrashReporter", "DialogType", "Server", user="frank"
)
mock.assert_called_once_with(
'defaults write "com.apple.CrashReporter" "DialogType" -string "Server"',
'write "com.apple.CrashReporter" "DialogType" -string "Server"',
runas="frank",
)
def test_write_default_boolean():
def test_write_true_boolean():
"""
Test writing a default setting
Test writing a True boolean setting
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
macdefaults.write("com.apple.CrashReporter", "Crash", True, type="boolean")
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "Crash", True, vtype="boolean")
mock.assert_called_once_with(
'defaults write "com.apple.CrashReporter" "Crash" -boolean "TRUE"',
'write "com.apple.CrashReporter" "Crash" -boolean "TRUE"',
runas=None,
)
def test_write_false_bool():
"""
Test writing a False boolean setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "Crash", False, vtype="bool")
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -bool "FALSE"',
runas=None,
)
def test_write_int():
"""
Test writing an int setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "Crash", 1, vtype="int")
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -int 1',
runas=None,
)
def test_write_integer():
"""
Test writing an integer setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "Crash", 1, vtype="integer")
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -integer 1',
runas=None,
)
def test_write_float():
"""
Test writing a float setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write("com.apple.CrashReporter", "Crash", 0.85, vtype="float")
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -float 0.85',
runas=None,
)
def test_write_array():
"""
Test writing an array setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write(
"com.apple.CrashReporter", "Crash", [0.1, 0.2, 0.4], vtype="array"
)
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -array 0.1 0.2 0.4',
runas=None,
)
def test_write_dictionary():
"""
Test writing a dictionary setting
"""
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.write(
"com.apple.CrashReporter", "Crash", {"foo": "bar", "baz": 0}, vtype="dict"
)
mock.assert_called_once_with(
'write "com.apple.CrashReporter" "Crash" -dict "foo" "bar" "baz" 0',
runas=None,
)
@ -54,49 +166,179 @@ def test_read_default():
"""
Test reading a default setting
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run": mock}):
macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_called_once_with(
'defaults read "com.apple.CrashReporter" "Crash"', runas=None
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "string"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "Server"}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas=None),
call('read-type "com.apple.CrashReporter" "Crash"', runas=None),
]
)
assert result == "Server"
def test_read_default_with_user():
def test_read_with_user():
"""
Test reading a default setting as a specific user
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run": mock}):
macdefaults.read("com.apple.CrashReporter", "Crash", user="frank")
mock.assert_called_once_with(
'defaults read "com.apple.CrashReporter" "Crash"', runas="frank"
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "string"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "Server"}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash", user="frank")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas="frank"),
call('read-type "com.apple.CrashReporter" "Crash"', runas="frank"),
]
)
assert result == "Server"
def test_read_integer():
"""
Test reading an integer setting
"""
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "integer"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "12"}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas=None),
call('read-type "com.apple.CrashReporter" "Crash"', runas=None),
]
)
assert result == 12
def test_read_float():
"""
Test reading a float setting
"""
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "float"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "0.85"}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas=None),
call('read-type "com.apple.CrashReporter" "Crash"', runas=None),
]
)
assert result == 0.85
def test_read_array():
"""
Test reading an array setting
"""
defaults_output = """(
element 1,
element 2,
0.1,
1
)"""
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "array"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": defaults_output}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas=None),
call('read-type "com.apple.CrashReporter" "Crash"', runas=None),
]
)
assert result == ["element 1", "element 2", 0.1, 1]
def test_read_dictionary():
"""
Test reading a dictionary setting
"""
defaults_output = """{
keyCode = 36;
modifierFlags = 786432;
}"""
def custom_run_defaults_cmd(action, runas=None):
if action == 'read-type "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": "dictionary"}
elif action == 'read "com.apple.CrashReporter" "Crash"':
return {"retcode": 0, "stdout": defaults_output}
return {"retcode": 1, "stderr": f"Unknown action: {action}", "stdout": ""}
mock = MagicMock(side_effect=custom_run_defaults_cmd)
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
result = macdefaults.read("com.apple.CrashReporter", "Crash")
mock.assert_has_calls(
[
call('read "com.apple.CrashReporter" "Crash"', runas=None),
call('read-type "com.apple.CrashReporter" "Crash"', runas=None),
]
)
assert result == {"keyCode": 36, "modifierFlags": 786432}
def test_delete_default():
"""
Test delete a default setting
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.delete("com.apple.CrashReporter", "Crash")
mock.assert_called_once_with(
'defaults delete "com.apple.CrashReporter" "Crash"',
output_loglevel="debug",
'delete "com.apple.CrashReporter" "Crash"',
runas=None,
)
def test_delete_default_with_user():
def test_delete_with_user():
"""
Test delete a default setting as a specific user
Test delete a setting as a specific user
"""
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}):
mock = MagicMock(return_value={"retcode": 0})
with patch("salt.modules.macdefaults._run_defaults_cmd", mock):
macdefaults.delete("com.apple.CrashReporter", "Crash", user="frank")
mock.assert_called_once_with(
'defaults delete "com.apple.CrashReporter" "Crash"',
output_loglevel="debug",
'delete "com.apple.CrashReporter" "Crash"',
runas="frank",
)

View file

@ -88,12 +88,12 @@ def test_write_boolean_match():
"""
expected = {
"changes": {},
"comment": "com.apple.something Key is already set to YES",
"comment": "com.apple.something Key is already set to True",
"name": "Key",
"result": True,
}
read_mock = MagicMock(return_value="1")
read_mock = MagicMock(return_value=True)
write_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macdefaults.__salt__,
@ -141,7 +141,7 @@ def test_write_integer_match():
"result": True,
}
read_mock = MagicMock(return_value="1337")
read_mock = MagicMock(return_value=1337)
write_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macdefaults.__salt__,