move remaining cache unit tests to pytest

This commit is contained in:
MKLeb 2022-08-25 13:13:48 -04:00
parent 1f78f9bf49
commit 01d18c449d
No known key found for this signature in database
GPG key ID: 089B64EA1A99DDD1
7 changed files with 486 additions and 536 deletions

View file

@ -113,7 +113,8 @@ salt/auth/*:
- pytests.integration.cli.test_salt_auth
salt/cache/*:
- unit.cache.test_cache
- pytests.unit.cache.test_cache
- pytests.unit.cache.test_memcache
- pytests.functional.cache.test_consul
- pytests.functional.cache.test_etcd
- pytests.functional.cache.test_localfs

31
tests/pytests/unit/cache/test_cache.py vendored Normal file
View file

@ -0,0 +1,31 @@
"""
Validate the cache package functions.
"""
import pytest
import salt.cache
import salt.payload
from tests.support.mock import patch
@pytest.fixture
def opts():
return {
"cache": "localfs",
"memcache_expire_seconds": 0,
"memcache_max_items": 0,
"memcache_full_cleanup": False,
"memcache_debug": False,
}
def test_factory_cache(opts):
ret = salt.cache.factory(opts)
assert isinstance(ret, salt.cache.Cache)
def test_factory_memcache(opts):
with patch.dict(opts, {"memcache_expire_seconds": 10}):
ret = salt.cache.factory(opts)
assert isinstance(ret, salt.cache.MemCache)

262
tests/pytests/unit/cache/test_localfs.py vendored Normal file
View file

@ -0,0 +1,262 @@
"""
Validate the functions in the localfs cache
"""
import errno
import shutil
import pytest
import salt.cache.localfs as localfs
import salt.payload
import salt.utils.files
from salt.exceptions import SaltCacheError
from tests.support.mock import MagicMock, patch
@pytest.fixture
def configure_loader_modules():
return {localfs: {}}
@pytest.fixture
def tmp_cache_file(tmp_path):
with patch.dict(localfs.__opts__, {"cachedir": tmp_path}):
localfs.store(bank="bank", key="key", data="payload data", cachedir=tmp_path)
yield tmp_path
def _create_tmp_cache_file(self, tmp_dir):
"""
Helper function that creates a temporary cache file using localfs.store. This
is to used to create DRY unit tests for the localfs cache.
"""
self.addCleanup(shutil.rmtree, tmp_dir)
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
localfs.store(bank="bank", key="key", data="payload data", cachedir=tmp_dir)
# 'store' function tests: 5
def test_handled_exception_cache_dir():
"""
Tests that a SaltCacheError is raised when the base directory doesn't exist and
cannot be created.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(side_effect=Exception)):
with pytest.raises(Exception):
localfs.store(bank="", key="", data="", cachedir="")
def test_unhandled_exception_cache_dir():
"""
Tests that a SaltCacheError is raised when the base directory doesn't exist and
cannot be created.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(1, ""))):
with pytest.raises(SaltCacheError):
localfs.store(bank="", key="", data="", cachedir="")
def test_store_close_mkstemp_file_handle():
"""
Tests that the file descriptor that is opened by os.open during the mkstemp call
in localfs.store is closed before calling salt.utils.files.fopen on the filename.
This test mocks the call to mkstemp, but forces an OSError to be raised when the
close() function is called on a file descriptor that doesn't exist.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(return_value=(12345, "foo"))):
with pytest.raises(OSError):
localfs.store(bank="", key="", data="", cachedir="")
def test_store_error_writing_cache():
"""
Tests that a SaltCacheError is raised when there is a problem writing to the
cache file.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(return_value=("one", "two"))):
with patch("os.close", MagicMock(return_value=None)):
with patch("salt.utils.files.fopen", MagicMock(side_effect=IOError)):
with pytest.raises(SaltCacheError):
localfs.store(bank="", key="", data="", cachedir="")
def test_store_success(tmp_cache_file):
"""
Tests that the store function writes the data to the serializer for storage.
"""
# Read in the contents of the key.p file and assert "payload data" was written
with salt.utils.files.fopen(str(tmp_cache_file / "bank" / "key.p"), "rb") as fh_:
for line in fh_:
assert b"payload data" in line
# 'fetch' function tests: 3
def test_fetch_return_when_cache_file_does_not_exist():
"""
Tests that the fetch function returns an empty dict when the cache key file
doesn't exist.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
assert localfs.fetch(bank="", key="", cachedir="") == {}
def test_fetch_error_reading_cache():
"""
Tests that a SaltCacheError is raised when there is a problem reading the cache
file.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("salt.utils.files.fopen", MagicMock(side_effect=IOError)):
with pytest.raises(SaltCacheError):
localfs.fetch(bank="", key="", cachedir="")
def test_fetch_success(tmp_cache_file):
"""
Tests that the fetch function is able to read the cache file and return its data.
"""
assert "payload data" in localfs.fetch(
bank="bank", key="key", cachedir=tmp_cache_file
)
# # 'updated' function tests: 3
def test_updated_return_when_cache_file_does_not_exist():
"""
Tests that the updated function returns None when the cache key file doesn't
exist.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
assert localfs.updated(bank="", key="", cachedir="") is None
def test_updated_error_when_reading_mtime():
"""
Tests that a SaltCacheError is raised when there is a problem reading the mtime
of the cache file.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("os.path.getmtime", MagicMock(side_effect=IOError)):
with pytest.raises(SaltCacheError):
localfs.fetch(bank="", key="", cachedir="")
def test_updated_success(tmp_cache_file):
"""
Test that the updated function returns the modification time of the cache file
"""
with patch(
"os.path.join", MagicMock(return_value=str(tmp_cache_file / "bank" / "key.p"))
):
assert isinstance(
localfs.updated(bank="bank", key="key", cachedir=tmp_cache_file), int
)
# # 'flush' function tests: 4
def test_flush_key_is_none_and_no_target_dir():
"""
Tests that the flush function returns False when no key is passed in and the
target directory doesn't exist.
"""
with patch("os.path.isdir", MagicMock(return_value=False)):
assert localfs.flush(bank="", key=None, cachedir="") is False
def test_flush_key_provided_and_no_key_file_false():
"""
Tests that the flush function returns False when a key file is provided but
the target key file doesn't exist in the cache bank.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
assert localfs.flush(bank="", key="key", cachedir="") is False
def test_flush_success(tmp_cache_file):
"""
Tests that the flush function returns True when a key file is provided and
the target key exists in the cache bank.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
assert localfs.flush(bank="bank", key="key", cachedir=tmp_cache_file) is True
def test_flush_error_raised():
"""
Tests that a SaltCacheError is raised when there is a problem removing the
key file from the cache bank
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("os.remove", MagicMock(side_effect=OSError)):
with pytest.raises(SaltCacheError):
localfs.flush(bank="", key="key", cachedir="/var/cache/salt")
# # 'list' function tests: 3
def test_list_no_base_dir():
"""
Tests that the ls function returns an empty list if the bank directory
doesn't exist.
"""
with patch("os.path.isdir", MagicMock(return_value=False)):
assert localfs.list_(bank="", cachedir="") == []
def test_list_error_raised_no_bank_directory_access():
"""
Tests that a SaltCacheError is raised when there is a problem accessing the
cache bank directory.
"""
with patch("os.path.isdir", MagicMock(return_value=True)):
with patch("os.listdir", MagicMock(side_effect=OSError)):
with pytest.raises(SaltCacheError):
localfs.list_(bank="", cachedir="")
def test_list_success(tmp_cache_file):
"""
Tests the return of the ls function containing bank entries.
"""
assert localfs.list_(bank="bank", cachedir=tmp_cache_file) == ["key"]
# # 'contains' function tests: 1
def test_contains(tmp_cache_file):
"""
Test the return of the contains function when key=None and when a key
is provided.
"""
assert localfs.contains(bank="bank", key=None, cachedir=tmp_cache_file) is True
assert localfs.contains(bank="bank", key="key", cachedir=tmp_cache_file) is True
def test_mix_of_utf8_and_non_utf8_can_be_round_tripped(tmp_cache_file):
data = {
# Any unicode, which ideally is invalid ascii.
"unicode": "áéí",
# Any bytes so long as they're not valid utf-8
"bytes": b"\xfe\x99\x00\xff",
}
bank = "bank"
key = "key"
localfs.store(bank, key, data, tmp_cache_file)
actual = localfs.fetch(bank, key, tmp_cache_file)
assert data == actual

View file

@ -0,0 +1,191 @@
"""
Validate Cache class methods
"""
import pytest
import salt.cache
import salt.payload
from tests.support.mock import patch
@pytest.fixture
def opts():
return {
"cache": "fake_driver",
"memcache_expire_seconds": 10,
"memcache_max_items": 3,
"memcache_full_cleanup": False,
"memcache_debug": False,
}
@pytest.fixture
def cache(opts):
salt.cache.MemCache.data = {}
return salt.cache.factory(opts)
def test_fetch(cache):
with patch("salt.cache.Cache.fetch", return_value="fake_data") as cache_fetch_mock:
with patch("salt.loader.cache", return_value={}):
# Fetch value, it will be kept in cache.
with patch("time.time", return_value=0):
ret = cache.fetch("bank", "key")
assert ret == "fake_data"
assert salt.cache.MemCache.data == {
"fake_driver": {("bank", "key"): [0, "fake_data"]}
}
cache_fetch_mock.assert_called_once_with("bank", "key")
cache_fetch_mock.reset_mock()
# Fetch again, cached value is used, time updated.
with patch("time.time", return_value=1):
ret = cache.fetch("bank", "key")
assert ret == "fake_data"
assert salt.cache.MemCache.data == {
"fake_driver": {("bank", "key"): [1, "fake_data"]}
}
cache_fetch_mock.assert_not_called()
# Fetch after expire
with patch("time.time", return_value=12):
ret = cache.fetch("bank", "key")
assert ret == "fake_data"
assert salt.cache.MemCache.data == {
"fake_driver": {("bank", "key"): [12, "fake_data"]}
}
cache_fetch_mock.assert_called_once_with("bank", "key")
cache_fetch_mock.reset_mock()
def test_store(cache):
with patch("salt.cache.Cache.store") as cache_store_mock:
with patch("salt.loader.cache", return_value={}):
# Fetch value, it will be kept in cache.
with patch("time.time", return_value=0):
cache.store("bank", "key", "fake_data")
assert salt.cache.MemCache.data == {
"fake_driver": {("bank", "key"): [0, "fake_data"]}
}
cache_store_mock.assert_called_once_with("bank", "key", "fake_data")
cache_store_mock.reset_mock()
# Store another value.
with patch("time.time", return_value=1):
cache.store("bank", "key2", "fake_data2")
assert salt.cache.MemCache.data == {
"fake_driver": {
("bank", "key"): [0, "fake_data"],
("bank", "key2"): [1, "fake_data2"],
}
}
cache_store_mock.assert_called_once_with("bank", "key2", "fake_data2")
def test_flush(cache):
with patch("salt.cache.Cache.flush") as cache_flush_mock:
with patch("salt.cache.Cache.store"):
with patch("salt.loader.cache", return_value={}):
# Flush non-existing bank
cache.flush("bank")
assert salt.cache.MemCache.data == {"fake_driver": {}}
cache_flush_mock.assert_called_once_with("bank", None)
cache_flush_mock.reset_mock()
# Flush non-existing key
cache.flush("bank", "key")
assert salt.cache.MemCache.data == {"fake_driver": {}}
cache_flush_mock.assert_called_once_with("bank", "key")
cache_flush_mock.reset_mock()
# Flush existing key
with patch("time.time", return_value=0):
cache.store("bank", "key", "fake_data")
assert salt.cache.MemCache.data["fake_driver"][("bank", "key")] == [
0,
"fake_data",
]
assert salt.cache.MemCache.data == {
"fake_driver": {("bank", "key"): [0, "fake_data"]}
}
cache.flush("bank", "key")
assert salt.cache.MemCache.data == {"fake_driver": {}}
cache_flush_mock.assert_called_once_with("bank", "key")
cache_flush_mock.reset_mock()
def test_max_items(cache):
with patch("salt.cache.Cache.store"):
with patch("salt.loader.cache", return_value={}):
# Put MAX=3 values
with patch("time.time", return_value=0):
cache.store("bank1", "key1", "fake_data11")
with patch("time.time", return_value=1):
cache.store("bank1", "key2", "fake_data12")
with patch("time.time", return_value=2):
cache.store("bank2", "key1", "fake_data21")
assert salt.cache.MemCache.data["fake_driver"] == {
("bank1", "key1"): [0, "fake_data11"],
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
}
# Put one more and check the oldest was removed
with patch("time.time", return_value=3):
cache.store("bank2", "key2", "fake_data22")
assert salt.cache.MemCache.data["fake_driver"] == {
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
("bank2", "key2"): [3, "fake_data22"],
}
def test_full_cleanup(cache):
with patch("salt.cache.Cache.store"):
with patch("salt.loader.cache", return_value={}):
# Enable full cleanup
cache.cleanup = True
# Put MAX=3 values
with patch("time.time", return_value=0):
cache.store("bank1", "key1", "fake_data11")
with patch("time.time", return_value=1):
cache.store("bank1", "key2", "fake_data12")
with patch("time.time", return_value=2):
cache.store("bank2", "key1", "fake_data21")
assert salt.cache.MemCache.data["fake_driver"] == {
("bank1", "key1"): [0, "fake_data11"],
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
}
# Put one more and check all expired was removed
with patch("time.time", return_value=12):
cache.store("bank2", "key2", "fake_data22")
assert salt.cache.MemCache.data["fake_driver"] == {
("bank2", "key1"): [2, "fake_data21"],
("bank2", "key2"): [12, "fake_data22"],
}
def test_fetch_debug(cache, opts):
with patch("salt.cache.Cache.fetch", return_value="fake_data"):
with patch("salt.loader.cache", return_value={}):
# Recreate cache with debug enabled
opts["memcache_debug"] = True
cache = salt.cache.factory(opts)
# Fetch 2 values (no cache hit)
with patch("time.time", return_value=0):
ret = cache.fetch("bank", "key1")
with patch("time.time", return_value=1):
ret = cache.fetch("bank", "key2")
# Fetch 3 times (cache hit)
with patch("time.time", return_value=2):
ret = cache.fetch("bank", "key2")
with patch("time.time", return_value=3):
ret = cache.fetch("bank", "key1")
with patch("time.time", return_value=4):
ret = cache.fetch("bank", "key1")
# Fetch an expired value (no cache hit)
with patch("time.time", return_value=13):
ret = cache.fetch("bank", "key2")
# Check debug data
assert cache.call == 6
assert cache.hit == 3

View file

View file

@ -1,232 +0,0 @@
"""
unit tests for salt.cache
"""
import salt.cache
import salt.payload
from tests.support.mock import patch
# import integration
from tests.support.unit import TestCase
class CacheFunctionsTest(TestCase):
"""
Validate the cache package functions.
"""
def setUp(self):
self.opts = {
"cache": "localfs",
"memcache_expire_seconds": 0,
"memcache_max_items": 0,
"memcache_full_cleanup": False,
"memcache_debug": False,
}
def test_factory_cache(self):
ret = salt.cache.factory(self.opts)
self.assertIsInstance(ret, salt.cache.Cache)
def test_factory_memcache(self):
self.opts["memcache_expire_seconds"] = 10
ret = salt.cache.factory(self.opts)
self.assertIsInstance(ret, salt.cache.MemCache)
class MemCacheTest(TestCase):
"""
Validate Cache class methods
"""
@patch("salt.payload.Serial")
def setUp(self, serial_mock): # pylint: disable=W0221
salt.cache.MemCache.data = {}
self.opts = {
"cache": "fake_driver",
"memcache_expire_seconds": 10,
"memcache_max_items": 3,
"memcache_full_cleanup": False,
"memcache_debug": False,
}
self.cache = salt.cache.factory(self.opts)
@patch("salt.cache.Cache.fetch", return_value="fake_data")
@patch("salt.loader.cache", return_value={})
def test_fetch(self, loader_mock, cache_fetch_mock):
# Fetch value, it will be kept in cache.
with patch("time.time", return_value=0):
ret = self.cache.fetch("bank", "key")
self.assertEqual(ret, "fake_data")
self.assertDictEqual(
salt.cache.MemCache.data,
{"fake_driver": {("bank", "key"): [0, "fake_data"]}},
)
cache_fetch_mock.assert_called_once_with("bank", "key")
cache_fetch_mock.reset_mock()
# Fetch again, cached value is used, time updated.
with patch("time.time", return_value=1):
ret = self.cache.fetch("bank", "key")
self.assertEqual(ret, "fake_data")
self.assertDictEqual(
salt.cache.MemCache.data,
{"fake_driver": {("bank", "key"): [1, "fake_data"]}},
)
cache_fetch_mock.assert_not_called()
# Fetch after expire
with patch("time.time", return_value=12):
ret = self.cache.fetch("bank", "key")
self.assertEqual(ret, "fake_data")
self.assertDictEqual(
salt.cache.MemCache.data,
{"fake_driver": {("bank", "key"): [12, "fake_data"]}},
)
cache_fetch_mock.assert_called_once_with("bank", "key")
cache_fetch_mock.reset_mock()
@patch("salt.cache.Cache.store")
@patch("salt.loader.cache", return_value={})
def test_store(self, loader_mock, cache_store_mock):
# Fetch value, it will be kept in cache.
with patch("time.time", return_value=0):
self.cache.store("bank", "key", "fake_data")
self.assertDictEqual(
salt.cache.MemCache.data,
{"fake_driver": {("bank", "key"): [0, "fake_data"]}},
)
cache_store_mock.assert_called_once_with("bank", "key", "fake_data")
cache_store_mock.reset_mock()
# Store another value.
with patch("time.time", return_value=1):
self.cache.store("bank", "key2", "fake_data2")
self.assertDictEqual(
salt.cache.MemCache.data,
{
"fake_driver": {
("bank", "key"): [0, "fake_data"],
("bank", "key2"): [1, "fake_data2"],
}
},
)
cache_store_mock.assert_called_once_with("bank", "key2", "fake_data2")
@patch("salt.cache.Cache.store")
@patch("salt.cache.Cache.flush")
@patch("salt.loader.cache", return_value={})
def test_flush(self, loader_mock, cache_flush_mock, cache_store_mock):
# Flush non-existing bank
self.cache.flush("bank")
self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}})
cache_flush_mock.assert_called_once_with("bank", None)
cache_flush_mock.reset_mock()
# Flush non-existing key
self.cache.flush("bank", "key")
self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}})
cache_flush_mock.assert_called_once_with("bank", "key")
cache_flush_mock.reset_mock()
# Flush existing key
with patch("time.time", return_value=0):
self.cache.store("bank", "key", "fake_data")
self.assertEqual(
salt.cache.MemCache.data["fake_driver"][("bank", "key")], [0, "fake_data"]
)
self.assertDictEqual(
salt.cache.MemCache.data,
{"fake_driver": {("bank", "key"): [0, "fake_data"]}},
)
self.cache.flush("bank", "key")
self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}})
cache_flush_mock.assert_called_once_with("bank", "key")
cache_flush_mock.reset_mock()
@patch("salt.cache.Cache.store")
@patch("salt.loader.cache", return_value={})
def test_max_items(self, loader_mock, cache_store_mock):
# Put MAX=3 values
with patch("time.time", return_value=0):
self.cache.store("bank1", "key1", "fake_data11")
with patch("time.time", return_value=1):
self.cache.store("bank1", "key2", "fake_data12")
with patch("time.time", return_value=2):
self.cache.store("bank2", "key1", "fake_data21")
self.assertDictEqual(
salt.cache.MemCache.data["fake_driver"],
{
("bank1", "key1"): [0, "fake_data11"],
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
},
)
# Put one more and check the oldest was removed
with patch("time.time", return_value=3):
self.cache.store("bank2", "key2", "fake_data22")
self.assertDictEqual(
salt.cache.MemCache.data["fake_driver"],
{
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
("bank2", "key2"): [3, "fake_data22"],
},
)
@patch("salt.cache.Cache.store")
@patch("salt.loader.cache", return_value={})
def test_full_cleanup(self, loader_mock, cache_store_mock):
# Enable full cleanup
self.cache.cleanup = True
# Put MAX=3 values
with patch("time.time", return_value=0):
self.cache.store("bank1", "key1", "fake_data11")
with patch("time.time", return_value=1):
self.cache.store("bank1", "key2", "fake_data12")
with patch("time.time", return_value=2):
self.cache.store("bank2", "key1", "fake_data21")
self.assertDictEqual(
salt.cache.MemCache.data["fake_driver"],
{
("bank1", "key1"): [0, "fake_data11"],
("bank1", "key2"): [1, "fake_data12"],
("bank2", "key1"): [2, "fake_data21"],
},
)
# Put one more and check all expired was removed
with patch("time.time", return_value=12):
self.cache.store("bank2", "key2", "fake_data22")
self.assertDictEqual(
salt.cache.MemCache.data["fake_driver"],
{
("bank2", "key1"): [2, "fake_data21"],
("bank2", "key2"): [12, "fake_data22"],
},
)
@patch("salt.cache.Cache.fetch", return_value="fake_data")
@patch("salt.loader.cache", return_value={})
def test_fetch_debug(self, loader_mock, cache_fetch_mock):
# Recreate cache with debug enabled
self.opts["memcache_debug"] = True
self.cache = salt.cache.factory(self.opts)
# Fetch 2 values (no cache hit)
with patch("time.time", return_value=0):
ret = self.cache.fetch("bank", "key1")
with patch("time.time", return_value=1):
ret = self.cache.fetch("bank", "key2")
# Fetch 3 times (cache hit)
with patch("time.time", return_value=2):
ret = self.cache.fetch("bank", "key2")
with patch("time.time", return_value=3):
ret = self.cache.fetch("bank", "key1")
with patch("time.time", return_value=4):
ret = self.cache.fetch("bank", "key1")
# Fetch an expired value (no cache hit)
with patch("time.time", return_value=13):
ret = self.cache.fetch("bank", "key2")
# Check debug data
self.assertEqual(self.cache.call, 6)
self.assertEqual(self.cache.hit, 3)

View file

@ -1,303 +0,0 @@
"""
unit tests for the localfs cache
"""
import errno
import shutil
import tempfile
import salt.cache.localfs as localfs
import salt.payload
import salt.utils.files
from salt.exceptions import SaltCacheError
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch
from tests.support.runtests import RUNTIME_VARS
from tests.support.unit import TestCase
class LocalFSTest(TestCase, LoaderModuleMockMixin):
"""
Validate the functions in the localfs cache
"""
def setup_loader_modules(self):
return {localfs: {}}
def _create_tmp_cache_file(self, tmp_dir):
"""
Helper function that creates a temporary cache file using localfs.store. This
is to used to create DRY unit tests for the localfs cache.
"""
self.addCleanup(shutil.rmtree, tmp_dir)
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
localfs.store(bank="bank", key="key", data="payload data", cachedir=tmp_dir)
# 'store' function tests: 5
def test_handled_exception_cache_dir(self):
"""
Tests that a SaltCacheError is raised when the base directory doesn't exist and
cannot be created.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(side_effect=Exception)):
self.assertRaises(
Exception, localfs.store, bank="", key="", data="", cachedir=""
)
def test_unhandled_exception_cache_dir(self):
"""
Tests that a SaltCacheError is raised when the base directory doesn't exist and
cannot be created.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(1, ""))):
self.assertRaises(
SaltCacheError, localfs.store, bank="", key="", data="", cachedir=""
)
def test_store_close_mkstemp_file_handle(self):
"""
Tests that the file descriptor that is opened by os.open during the mkstemp call
in localfs.store is closed before calling salt.utils.files.fopen on the filename.
This test mocks the call to mkstemp, but forces an OSError to be raised when the
close() function is called on a file descriptor that doesn't exist.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(return_value=(12345, "foo"))):
self.assertRaises(
OSError, localfs.store, bank="", key="", data="", cachedir=""
)
def test_store_error_writing_cache(self):
"""
Tests that a SaltCacheError is raised when there is a problem writing to the
cache file.
"""
with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))):
with patch("tempfile.mkstemp", MagicMock(return_value=("one", "two"))):
with patch("os.close", MagicMock(return_value=None)):
with patch(
"salt.utils.files.fopen", MagicMock(side_effect=IOError)
):
self.assertRaises(
SaltCacheError,
localfs.store,
bank="",
key="",
data="",
cachedir="",
)
def test_store_success(self):
"""
Tests that the store function writes the data to the serializer for storage.
"""
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
# Read in the contents of the key.p file and assert "payload data" was written
with salt.utils.files.fopen(tmp_dir + "/bank/key.p", "rb") as fh_:
for line in fh_:
self.assertIn(b"payload data", line)
# 'fetch' function tests: 3
def test_fetch_return_when_cache_file_does_not_exist(self):
"""
Tests that the fetch function returns an empty dic when the cache key file
doesn't exist.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
self.assertEqual(localfs.fetch(bank="", key="", cachedir=""), {})
def test_fetch_error_reading_cache(self):
"""
Tests that a SaltCacheError is raised when there is a problem reading the cache
file.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("salt.utils.files.fopen", MagicMock(side_effect=IOError)):
self.assertRaises(
SaltCacheError, localfs.fetch, bank="", key="", cachedir=""
)
def test_fetch_success(self):
"""
Tests that the fetch function is able to read the cache file and return its data.
"""
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
# Now fetch the data from the new cache key file
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
self.assertIn(
"payload data",
localfs.fetch(bank="bank", key="key", cachedir=tmp_dir),
)
# 'updated' function tests: 3
def test_updated_return_when_cache_file_does_not_exist(self):
"""
Tests that the updated function returns None when the cache key file doesn't
exist.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
self.assertIsNone(localfs.updated(bank="", key="", cachedir=""))
def test_updated_error_when_reading_mtime(self):
"""
Tests that a SaltCacheError is raised when there is a problem reading the mtime
of the cache file.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("os.path.getmtime", MagicMock(side_effect=IOError)):
self.assertRaises(
SaltCacheError, localfs.updated, bank="", key="", cachedir=""
)
def test_updated_success(self):
"""
Test that the updated function returns the modification time of the cache file
"""
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
with patch("os.path.join", MagicMock(return_value=tmp_dir + "/bank/key.p")):
self.assertIsInstance(
localfs.updated(bank="bank", key="key", cachedir=tmp_dir), int
)
# 'flush' function tests: 4
def test_flush_key_is_none_and_no_target_dir(self):
"""
Tests that the flush function returns False when no key is passed in and the
target directory doesn't exist.
"""
with patch("os.path.isdir", MagicMock(return_value=False)):
self.assertFalse(localfs.flush(bank="", key=None, cachedir=""))
def test_flush_key_provided_and_no_key_file_false(self):
"""
Tests that the flush function returns False when a key file is provided but
the target key file doesn't exist in the cache bank.
"""
with patch("os.path.isfile", MagicMock(return_value=False)):
self.assertFalse(localfs.flush(bank="", key="key", cachedir=""))
def test_flush_success(self):
"""
Tests that the flush function returns True when a key file is provided and
the target key exists in the cache bank.
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
# Now test the return of the flush function
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
self.assertTrue(localfs.flush(bank="bank", key="key", cachedir=tmp_dir))
def test_flush_error_raised(self):
"""
Tests that a SaltCacheError is raised when there is a problem removing the
key file from the cache bank
"""
with patch("os.path.isfile", MagicMock(return_value=True)):
with patch("os.remove", MagicMock(side_effect=OSError)):
self.assertRaises(
SaltCacheError,
localfs.flush,
bank="",
key="key",
cachedir="/var/cache/salt",
)
# 'list' function tests: 3
def test_list_no_base_dir(self):
"""
Tests that the ls function returns an empty list if the bank directory
doesn't exist.
"""
with patch("os.path.isdir", MagicMock(return_value=False)):
self.assertEqual(localfs.list_(bank="", cachedir=""), [])
def test_list_error_raised_no_bank_directory_access(self):
"""
Tests that a SaltCacheError is raised when there is a problem accessing the
cache bank directory.
"""
with patch("os.path.isdir", MagicMock(return_value=True)):
with patch("os.listdir", MagicMock(side_effect=OSError)):
self.assertRaises(SaltCacheError, localfs.list_, bank="", cachedir="")
def test_list_success(self):
"""
Tests the return of the ls function containing bank entries.
"""
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
# Now test the return of the ls function
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
self.assertEqual(localfs.list_(bank="bank", cachedir=tmp_dir), ["key"])
# 'contains' function tests: 1
def test_contains(self):
"""
Test the return of the contains function when key=None and when a key
is provided.
"""
# Create a temporary cache dir
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
# Use the helper function to create the cache file using localfs.store()
self._create_tmp_cache_file(tmp_dir)
# Now test the return of the contains function when key=None
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
self.assertTrue(localfs.contains(bank="bank", key=None, cachedir=tmp_dir))
# Now test the return of the contains function when key='key'
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
self.assertTrue(localfs.contains(bank="bank", key="key", cachedir=tmp_dir))
def test_mix_of_utf8_and_non_utf8_can_be_round_tripped(self):
tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
data = {
# Any unicode, which ideally is invalid ascii.
"unicode": "áéí",
# Any bytes so long as they're not valid utf-8
"bytes": b"\xfe\x99\x00\xff",
}
bank = "bank"
key = "key"
self.addCleanup(shutil.rmtree, tmp_dir)
with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}):
localfs.store(bank, key, data, tmp_dir)
actual = localfs.fetch(bank, key, tmp_dir)
self.assertEqual(data, actual)