mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
move remaining cache unit tests to pytest
This commit is contained in:
parent
1f78f9bf49
commit
01d18c449d
7 changed files with 486 additions and 536 deletions
|
@ -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
31
tests/pytests/unit/cache/test_cache.py
vendored
Normal 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
262
tests/pytests/unit/cache/test_localfs.py
vendored
Normal 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
|
191
tests/pytests/unit/cache/test_memcache.py
vendored
Normal file
191
tests/pytests/unit/cache/test_memcache.py
vendored
Normal 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
|
0
tests/unit/cache/__init__.py
vendored
0
tests/unit/cache/__init__.py
vendored
232
tests/unit/cache/test_cache.py
vendored
232
tests/unit/cache/test_cache.py
vendored
|
@ -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)
|
303
tests/unit/cache/test_localfs.py
vendored
303
tests/unit/cache/test_localfs.py
vendored
|
@ -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)
|
Loading…
Add table
Reference in a new issue