# -*- coding: utf-8 -*- ''' unit tests for salt.cache ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs # import integration from tests.support.unit import skipIf, TestCase from tests.support.mock import ( NO_MOCK, NO_MOCK_REASON, patch, ) # Import Salt libs import salt.payload import salt.cache 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) @skipIf(NO_MOCK, NO_MOCK_REASON) 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)