From 486f1393429afc17ba57f31690fa2388f8925f82 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 3 Jul 2019 00:42:47 +0200 Subject: [PATCH] Move Preload/JIT Configuration logic from Adapter to Core-Configuration --- src/Core/Config/Adapter/IConfigAdapter.php | 73 --- src/Core/Config/Adapter/JITConfigAdapter.php | 145 ------ .../Config/Adapter/PreloadConfigAdapter.php | 115 ----- src/Core/Config/Configuration.php | 139 +----- src/Core/Config/JitConfiguration.php | 134 ++++++ src/Core/Config/PreloadConfiguration.php | 116 +++++ src/Model/Config/Config.php | 121 +++++ src/Model/Config/DbaConfig.php | 79 ++++ tests/src/Core/Config/ConfigurationTest.php | 428 ++++++++++++------ .../src/Core/Config/JitConfigurationTest.php | 167 +++++++ .../Core/Config/PreloadConfigurationTest.php | 140 ++++++ 11 files changed, 1053 insertions(+), 604 deletions(-) delete mode 100644 src/Core/Config/Adapter/IConfigAdapter.php delete mode 100644 src/Core/Config/Adapter/JITConfigAdapter.php delete mode 100644 src/Core/Config/Adapter/PreloadConfigAdapter.php create mode 100644 src/Core/Config/JitConfiguration.php create mode 100644 src/Core/Config/PreloadConfiguration.php create mode 100644 src/Model/Config/Config.php create mode 100644 src/Model/Config/DbaConfig.php create mode 100644 tests/src/Core/Config/JitConfigurationTest.php create mode 100644 tests/src/Core/Config/PreloadConfigurationTest.php diff --git a/src/Core/Config/Adapter/IConfigAdapter.php b/src/Core/Config/Adapter/IConfigAdapter.php deleted file mode 100644 index 892c476e7c..0000000000 --- a/src/Core/Config/Adapter/IConfigAdapter.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ -interface IConfigAdapter -{ - /** - * Loads all configuration values and returns the loaded category as an array. - * - * @param string $cat The category of the configuration values to load - * - * @return array - */ - public function load($cat = "config"); - - /** - * Get a particular system-wide config variable given the category name - * ($family) and a key. - * - * Note: Boolean variables are defined as 0/1 in the database - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return null|mixed Stored value or null if it does not exist - */ - public function get($cat, $key); - - /** - * Stores a config value ($value) in the category ($family) under the key ($key). - * - * Note: Please do not store booleans - convert to 0/1 integer values! - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to set - * @param mixed $value The value to store - * - * @return bool Operation success - */ - public function set($cat, $key, $value); - - /** - * Removes the configured value from the stored cache - * and removes it from the database. - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to delete - * - * @return bool Operation success - */ - public function delete($cat, $key); - - /** - * Checks, if the current adapter is connected to the backend - * - * @return bool - */ - public function isConnected(); - - /** - * Checks, if a config key ($key) in the category ($cat) is already loaded. - * - * @param string $cat The configuration category - * @param string $key The configuration key - * - * @return bool - */ - public function isLoaded($cat, $key); -} diff --git a/src/Core/Config/Adapter/JITConfigAdapter.php b/src/Core/Config/Adapter/JITConfigAdapter.php deleted file mode 100644 index d125f7d400..0000000000 --- a/src/Core/Config/Adapter/JITConfigAdapter.php +++ /dev/null @@ -1,145 +0,0 @@ - - */ -class JITConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter -{ - private $in_db; - - /** - * {@inheritdoc} - */ - public function load($cat = "config") - { - $return = []; - - if (!$this->isConnected()) { - return $return; - } - - // We don't preload "system" anymore. - // This reduces the number of database reads a lot. - if ($cat === 'system') { - return $return; - } - - $configs = DBA::select('config', ['v', 'k'], ['cat' => $cat]); - while ($config = DBA::fetch($configs)) { - $key = $config['k']; - $value = $this->toConfigValue($config['v']); - - // The value was in the db, so don't check it again (unless you have to) - $this->in_db[$cat][$key] = true; - - // just save it in case it is set - if (isset($value)) { - $return[$key] = $value; - } - } - DBA::close($configs); - - return [$cat => $return]; - } - - /** - * {@inheritdoc} - * - * @param bool $mark if true, mark the selection of the current cat/key pair - */ - public function get($cat, $key, $mark = true) - { - if (!$this->isConnected()) { - return null; - } - - // The value got checked, so mark it to avoid checking it over and over again - if ($mark) { - $this->in_db[$cat][$key] = true; - } - - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); - if (DBA::isResult($config)) { - $value = $this->toConfigValue($config['v']); - - // just return it in case it is set - if (isset($value)) { - return $value; - } - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function set($cat, $key, $value) - { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values in a string variable. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $compare_value = (!is_array($value) ? (string)$value : $value); - $stored_value = $this->get($cat, $key, false); - - if (!isset($this->in_db[$cat])) { - $this->in_db[$cat] = []; - } - if (!isset($this->in_db[$cat][$key])) { - $this->in_db[$cat][$key] = false; - } - - if (isset($stored_value) && ($stored_value === $compare_value) && $this->in_db[$cat][$key]) { - return true; - } - - $dbvalue = $this->toDbValue($value); - - $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); - - $this->in_db[$cat][$key] = $result; - - return $result; - } - - /** - * {@inheritdoc} - */ - public function delete($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - if (isset($this->cache[$cat][$key])) { - unset($this->in_db[$cat][$key]); - } - - $result = DBA::delete('config', ['cat' => $cat, 'k' => $key]); - - return $result; - } - - /** - * {@inheritdoc} - */ - public function isLoaded($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return (isset($this->in_db[$cat][$key])) && $this->in_db[$cat][$key]; - } -} diff --git a/src/Core/Config/Adapter/PreloadConfigAdapter.php b/src/Core/Config/Adapter/PreloadConfigAdapter.php deleted file mode 100644 index c691c88bc5..0000000000 --- a/src/Core/Config/Adapter/PreloadConfigAdapter.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ -class PreloadConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter -{ - private $config_loaded = false; - - /** - * {@inheritdoc} - */ - public function load($cat = 'config') - { - $return = []; - - if (!$this->isConnected()) { - return $return; - } - - if ($this->config_loaded) { - return $return; - } - - $configs = DBA::select('config', ['cat', 'v', 'k']); - while ($config = DBA::fetch($configs)) { - $value = $this->toConfigValue($config['v']); - if (isset($value)) { - $return[$config['cat']][$config['k']] = $value; - } - } - DBA::close($configs); - - $this->config_loaded = true; - - return $return; - } - - /** - * {@inheritdoc} - */ - public function get($cat, $key) - { - if (!$this->isConnected()) { - return null; - } - - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); - if (DBA::isResult($config)) { - $value = $this->toConfigValue($config['v']); - - if (isset($value)) { - return $value; - } - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function set($cat, $key, $value) - { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values as strings. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $compare_value = !is_array($value) ? (string)$value : $value; - $stored_value = $this->get($cat, $key); - - if (isset($stored_value) && $stored_value === $compare_value) { - return true; - } - - $dbvalue = $this->toDbValue($value); - - return DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); - } - - /** - * {@inheritdoc} - */ - public function delete($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return DBA::delete('config', ['cat' => $cat, 'k' => $key]); - } - - /** - * {@inheritdoc} - */ - public function isLoaded($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return $this->config_loaded; - } -} diff --git a/src/Core/Config/Configuration.php b/src/Core/Config/Configuration.php index 1489d91de0..9654fb287f 100644 --- a/src/Core/Config/Configuration.php +++ b/src/Core/Config/Configuration.php @@ -2,151 +2,46 @@ namespace Friendica\Core\Config; +use Friendica\Model; + /** * This class is responsible for all system-wide configuration values in Friendica * There are two types of storage - * - The Config-Files (loaded into the FileCache @see Cache\IConfigCache ) - * - The Config-DB-Table (per Config-DB-adapter @see Adapter\IConfigAdapter ) + * - The Config-Files (loaded into the FileCache @see Cache\ConfigCache ) + * - The Config-DB-Table (per Config-DB-model @see Model\Config\Config ) */ -class Configuration +abstract class Configuration { /** * @var Cache\ConfigCache */ - private $configCache; + protected $configCache; /** - * @var Adapter\IConfigAdapter + * @var Model\Config\Config */ - private $configAdapter; + protected $configModel; /** - * @param Cache\ConfigCache $configCache The configuration cache (based on the config-files) - * @param Adapter\IConfigAdapter $configAdapter The configuration DB-backend + * @param Cache\ConfigCache $configCache The configuration cache (based on the config-files) + * @param Model\Config\Config $configModel The configuration model */ - public function __construct(Cache\ConfigCache $configCache, Adapter\IConfigAdapter $configAdapter) + public function __construct(Cache\ConfigCache $configCache, Model\Config\Config $configModel) { $this->configCache = $configCache; - $this->configAdapter = $configAdapter; - - $this->load(); + $this->configModel = $configModel; } /** - * Returns the Config Cache - * - * @return Cache\ConfigCache + * {@inheritDoc} */ public function getCache() { return $this->configCache; } - /** - * @brief Loads all configuration values of family into a cached storage. - * - * All configuration values of the system are stored in the cache ( @see IConfigCache ) - * - * @param string $cat The category of the configuration value - * - * @return void - */ - public function load($cat = 'config') - { - // If not connected, do nothing - if (!$this->configAdapter->isConnected()) { - return; - } - - // load the whole category out of the DB into the cache - $this->configCache->load($this->configAdapter->load($cat), true); - } - - /** - * @brief Get a particular user's config variable given the category name - * ($cat) and a $key. - * - * Get a particular config value from the given category ($cat) - * and the $key from a cached storage either from the $this->configAdapter - * (@see IConfigAdapter ) or from the $this->configCache (@see IConfigCache ). - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * @param mixed $default_value optional, The value to return if key is not set (default: null) - * @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) - * - * @return mixed Stored value or null if it does not exist - */ - public function get($cat, $key, $default_value = null, $refresh = false) - { - // if the value isn't loaded or refresh is needed, load it to the cache - if ($this->configAdapter->isConnected() && - (!$this->configAdapter->isLoaded($cat, $key) || - $refresh)) { - - $dbvalue = $this->configAdapter->get($cat, $key); - - if (isset($dbvalue)) { - $this->configCache->set($cat, $key, $dbvalue); - unset($dbvalue); - } - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } - - /** - * @brief Sets a configuration value for system config - * - * Stores a config value ($value) in the category ($cat) under the key ($key) - * - * Note: Please do not store booleans - convert to 0/1 integer values! - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to set - * @param mixed $value The value to store - * - * @return bool Operation success - */ - public function set($cat, $key, $value) - { - // set the cache first - $cached = $this->configCache->set($cat, $key, $value); - - // If there is no connected adapter, we're finished - if (!$this->configAdapter->isConnected()) { - return $cached; - } - - $stored = $this->configAdapter->set($cat, $key, $value); - - return $cached && $stored; - } - - /** - * @brief Deletes the given key from the system configuration. - * - * Removes the configured value from the stored cache in $this->configCache - * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ). - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to delete - * - * @return bool - */ - public function delete($cat, $key) - { - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (!$this->configAdapter->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configAdapter->delete($cat, $key); - - return $cacheRemoved || $storeRemoved; - } + abstract public function load(string $cat = 'config'); + abstract public function get(string $cat, string $key, $default_value = null, bool $refresh = false); + abstract public function set(string $cat, string $key, $value); + abstract public function delete(string $cat, string $key); } diff --git a/src/Core/Config/JitConfiguration.php b/src/Core/Config/JitConfiguration.php new file mode 100644 index 0000000000..f8260c9e47 --- /dev/null +++ b/src/Core/Config/JitConfiguration.php @@ -0,0 +1,134 @@ +in_db = []; + + // take the values of the given cache instead of loading them from the model again + $preSet = $configCache->getAll(); + if (!empty($preSet)) { + foreach ($preSet as $cat => $data) { + foreach ($data as $key => $value) { + $this->in_db[$cat][$key] = true; + } + } + } + + $this->load(); + } + + /** + * {@inheritDoc} + * + */ + public function load(string $cat = 'config') + { + // If not connected, do nothing + if (!$this->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load($cat); + + foreach ($config[$cat] as $key => $value) { + $this->in_db[$cat][$key] = true; + } + + // load the whole category out of the DB into the cache + $this->configCache->load($config, true); + } + + /** + * {@inheritDoc} + */ + public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + { + // if the value isn't loaded or refresh is needed, load it to the cache + if ($this->configModel->isConnected() && + (empty($this->in_db[$cat][$key]) || + $refresh)) { + + $dbvalue = $this->configModel->get($cat, $key); + + if (isset($dbvalue)) { + $this->configCache->set($cat, $key, $dbvalue); + unset($dbvalue); + $this->in_db[$cat][$key] = true; + } + } + + // use the config cache for return + $result = $this->configCache->get($cat, $key); + + return (isset($result)) ? $result : $default_value; + } + + /** + * {@inheritDoc} + */ + public function set(string $cat, string $key, $value) + { + // set the cache first + $cached = $this->configCache->set($cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($cat, $key, $value); + + $this->in_db[$cat][$key] = $stored; + + return $cached && $stored; + } + + /** + * @brief Deletes the given key from the system configuration. + * + * Removes the configured value from the stored cache in $this->configCache + * (@param string $cat The category of the configuration value + * + * @param string $key The configuration key to delete + * + * @return bool + * @see ConfigCache ) and removes it from the database (@see IConfigAdapter ). + * + */ + public function delete(string $cat, string $key) + { + $cacheRemoved = $this->configCache->delete($cat, $key); + + if (isset($this->in_db[$cat][$key])) { + unset($this->in_db[$cat][$key]); + } + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Core/Config/PreloadConfiguration.php b/src/Core/Config/PreloadConfiguration.php new file mode 100644 index 0000000000..bf57661a1b --- /dev/null +++ b/src/Core/Config/PreloadConfiguration.php @@ -0,0 +1,116 @@ +config_loaded = false; + + $this->load(); + } + + /** + * {@inheritDoc} + * + * This loads all config values everytime load is called + * + */ + public function load(string $cat = 'config') + { + // Don't load the whole configuration twice + if ($this->config_loaded) { + return; + } + + // If not connected, do nothing + if (!$this->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load(); + $this->config_loaded = true; + + // load the whole category out of the DB into the cache + $this->configCache->load($config, true); + } + + /** + * {@inheritDoc} + */ + public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + { + if ($refresh) { + if ($this->configModel->isConnected()) { + $config = $this->configModel->get($cat, $key); + if (isset($config)) { + $this->configCache->set($cat, $key, $config); + } + } + } + + // use the config cache for return + $result = $this->configCache->get($cat, $key); + + return (isset($result)) ? $result : $default_value; + } + + /** + * {@inheritDoc} + */ + public function set(string $cat, string $key, $value) + { + if (!$this->config_loaded) { + $this->load(); + } + + // set the cache first + $cached = $this->configCache->set($cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($cat, $key, $value); + + return $cached && $stored; + } + + /** + * {@inheritDoc} + */ + public function delete(string $cat, string $key) + { + if ($this->config_loaded) { + $this->load(); + } + + $cacheRemoved = $this->configCache->delete($cat, $key); + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Model/Config/Config.php b/src/Model/Config/Config.php new file mode 100644 index 0000000000..e7dd0a5693 --- /dev/null +++ b/src/Model/Config/Config.php @@ -0,0 +1,121 @@ +dba->select('config', ['cat', 'v', 'k']); + } else { + $configs = $this->dba->select('config', ['cat', 'v', 'k'], ['cat' => $cat]); + } + + while ($config = $this->dba->fetch($configs)) { + + $key = $config['k']; + $value = $this->toConfigValue($config['v']); + + // just save it in case it is set + if (isset($value)) { + $return[$config['cat']][$key] = $value; + } + } + $this->dba->close($configs); + + return $return; + } + + /** + * Get a particular system-wide config variable given the category name + * ($cat) and a key ($key). + * + * Note: Boolean variables are defined as 0/1 in the database + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * + * @return null|mixed Stored value or null if it does not exist + */ + public function get(string $cat, string $key) + { + if (!$this->isConnected()) { + return null; + } + + $config = $this->dba->selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); + if ($this->dba->isResult($config)) { + $value = $this->toConfigValue($config['v']); + + // just return it in case it is set + if (isset($value)) { + return $value; + } + } + + return null; + } + + /** + * Stores a config value ($value) in the category ($cat) under the key ($key). + * + * Note: Please do not store booleans - convert to 0/1 integer values! + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to set + * @param mixed $value The value to store + * + * @return bool Operation success + */ + public function set(string $cat, string $key, $value) + { + if (!$this->isConnected()) { + return false; + } + + // We store our setting values in a string variable. + // So we have to do the conversion here so that the compare below works. + // The exception are array values. + $compare_value = (!is_array($value) ? (string)$value : $value); + $stored_value = $this->get($cat, $key); + + if (isset($stored_value) && ($stored_value === $compare_value)) { + return true; + } + + $dbvalue = $this->toDbValue($value); + + $result = $this->dba->update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); + + return $result; + } + + /** + * Removes the configured value from the stored cache + * and removes it from the database. + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete + * + * @return bool Operation success + */ + public function delete(string $cat, string $key) + { + if (!$this->isConnected()) { + return false; + } + + return $this->dba->delete('config', ['cat' => $cat, 'k' => $key]); + } +} diff --git a/src/Model/Config/DbaConfig.php b/src/Model/Config/DbaConfig.php new file mode 100644 index 0000000000..bbd62c7cbe --- /dev/null +++ b/src/Model/Config/DbaConfig.php @@ -0,0 +1,79 @@ +dba = $dba; + } + + /** + * Checks if the model is currently connected + * + * @return bool + */ + public function isConnected() + { + return $this->dba->isConnected(); + } + + /** + * Formats a DB value to a config value + * - null = The db-value isn't set + * - bool = The db-value is either '0' or '1' + * - array = The db-value is a serialized array + * - string = The db-value is a string + * + * Keep in mind that there aren't any numeric/integer config values in the database + * + * @param null|string $value + * + * @return null|array|string + */ + protected function toConfigValue($value) + { + if (!isset($value)) { + return null; + } + + switch (true) { + // manage array value + case preg_match("|^a:[0-9]+:{.*}$|s", $value): + return unserialize($value); + + default: + return $value; + } + } + + /** + * Formats a config value to a DB value (string) + * + * @param mixed $value + * + * @return string + */ + protected function toDbValue($value) + { + // if not set, save an empty string + if (!isset($value)) { + return ''; + } + + switch (true) { + // manage arrays + case is_array($value): + return serialize($value); + + default: + return (string)$value; + } + } +} diff --git a/tests/src/Core/Config/ConfigurationTest.php b/tests/src/Core/Config/ConfigurationTest.php index fda69896fd..b9546cea7f 100644 --- a/tests/src/Core/Config/ConfigurationTest.php +++ b/tests/src/Core/Config/ConfigurationTest.php @@ -2,13 +2,55 @@ namespace Friendica\Test\src\Core\Config; -use Friendica\Core\Config\Adapter\IConfigAdapter; use Friendica\Core\Config\Cache\ConfigCache; use Friendica\Core\Config\Configuration; +use Friendica\Core\Config\JitConfiguration; +use Friendica\Model\Config\Config as ConfigModel; use Friendica\Test\MockedTest; +use Mockery\MockInterface; +use Mockery; -class ConfigurationTest extends MockedTest +abstract class ConfigurationTest extends MockedTest { + /** @var ConfigModel|MockInterface */ + protected $configModel; + + /** @var ConfigCache */ + protected $configCache; + + /** @var Configuration */ + protected $testedConfig; + + /** + * Assert a config tree + * + * @param string $cat The category to assert + * @param array $data The result data array + */ + protected function assertConfig(string $cat, array $data) + { + $result = $this->testedConfig->getCache()->getAll(); + + $this->assertNotEmpty($result); + $this->assertArrayHasKey($cat, $result); + $this->assertArraySubset($data, $result[$cat]); + } + + + protected function setUp() + { + parent::setUp(); + + // Create the config model + $this->configModel = Mockery::mock(ConfigModel::class); + $this->configCache = new ConfigCache(); + } + + /** + * @return Configuration + */ + public abstract function getInstance(); + public function dataTests() { return [ @@ -23,107 +65,229 @@ class ConfigurationTest extends MockedTest ]; } + public function dataConfigLoad() + { + $data = [ + 'system' => [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'config' => [ + 'key1' => 'value1a', + 'key4' => 'value4', + ], + 'other' => [ + 'key5' => 'value5', + 'key6' => 'value6', + ], + ]; + + return [ + 'system' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + ], + ], + 'other' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'other', + ], + ], + 'config' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'config', + ], + ], + 'all' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + 'config', + 'other' + ], + ], + ]; + } + /** * Test the configuration initialization */ - public function testSetUp() + public function testSetUp(array $data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->once(); + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertInstanceOf(ConfigCache::class, $configuration->getCache()); + // assert config is loaded everytime + $this->assertConfig('config', $data['config']); } /** * Test the configuration load() method */ - public function testCacheLoad() + public function testLoad(array $data, array $possibleCats, array $load) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - // expected loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); - $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); - $configuration->load('testing'); + foreach ($load as $loadedCats) { + $this->testedConfig->load($loadedCats); + } - $this->assertEquals('it', $configuration->get('testing', 'test')); - $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($load as $loadedCats) { + $this->assertConfig($loadedCats, $data[$loadedCats]); + } + } + + public function dataDoubleLoad() + { + return [ + 'config' => [ + 'data1' => [ + 'config' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + ], + 'data2' => [ + 'config' => [ + 'key1' => 'overwritten!', + 'key3' => 'value3', + ], + ], + 'expect' => [ + 'config' => [ + // load should overwrite values everytime! + 'key1' => 'overwritten!', + 'key2' => 'value2', + 'key3' => 'value3', + ], + ], + ], + 'other' => [ + 'data1' => [ + 'config' => [ + 'key12' => 'data4', + 'key45' => 7, + ], + 'other' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + ], + 'data2' => [ + 'other' => [ + 'key1' => 'overwritten!', + 'key3' => 'value3', + ], + 'config' => [ + 'key45' => 45, + 'key52' => true, + ] + ], + 'expect' => [ + 'other' => [ + // load should overwrite values everytime! + 'key1' => 'overwritten!', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'config' => [ + 'key12' => 'data4', + 'key45' => 45, + 'key52' => true, + ], + ], + ], + ]; } /** * Test the configuration load() method with overwrite */ - public function testCacheLoadDouble() + public function testCacheLoadDouble(array $data1, array $data2, array $expect) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(5); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - // expected loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); - $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->twice(); - // expected next loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'again']])->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); - $configuration->load('testing'); + foreach ($data1 as $cat => $data) { + $this->testedConfig->load($cat); + } - $this->assertEquals('it', $configuration->get('testing', 'test')); - $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($data1 as $cat => $data) { + $this->assertConfig($cat, $data); + } - $configuration->load('testing'); - - $this->assertEquals('again', $configuration->get('testing', 'test')); - $this->assertEquals('again', $configuration->getCache()->get('testing', 'test')); + foreach ($data2 as $cat => $data) { + $this->testedConfig->load($cat); + } } /** * Test the configuration get() and set() methods without adapter + * * @dataProvider dataTests */ public function testSetGetWithoutDB($data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(3); + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertTrue($configuration->set('test', 'it', $data)); + $this->assertTrue($this->testedConfig->set('test', 'it', $data)); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); } /** - * Test the configuration get() and set() methods with adapter + * Test the configuration get() and set() methods with a model/db + * * @dataProvider dataTests */ public function testSetGetWithDB($data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); - $configAdapter->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); + $this->configModel->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertTrue($configuration->set('test', 'it', $data)); + $this->assertTrue($this->testedConfig->set('test', 'it', $data)); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); } /** @@ -131,145 +295,111 @@ class ConfigurationTest extends MockedTest */ public function testGetWrongWithoutDB() { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); - - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertNull($configuration->get('test', 'it')); + $this->assertNull($this->testedConfig->get('test', 'it')); /// beware that the cache returns '!!' and not null for a non existing value - $this->assertNull($configuration->getCache()->get('test', 'it')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'it')); // with default value - $this->assertEquals('default', $configuration->get('test', 'it', 'default')); + $this->assertEquals('default', $this->testedConfig->get('test', 'it', 'default')); // with default value and refresh - $this->assertEquals('default', $configuration->get('test', 'it', 'default', true)); + $this->assertEquals('default', $this->testedConfig->get('test', 'it', 'default', true)); } /** * Test the configuration get() method with refresh + * * @dataProvider dataTests */ public function testGetWithRefresh($data) { - $configCache = new ConfigCache(['test' => ['it' => 'now']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->twice(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'not')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'not')->andReturn(null)->once(); + $this->configCache->load(['test' => ['it' => 'now']]); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); // with refresh - $this->assertEquals($data, $configuration->get('test', 'it', null, true)); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it', null, true)); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); // without refresh and wrong value and default - $this->assertEquals('default', $configuration->get('test', 'not', 'default')); - $this->assertNull($configuration->getCache()->get('test', 'not')); + $this->assertEquals('default', $this->testedConfig->get('test', 'not', 'default')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'not')); } /** - * Test the configuration get() method with different isLoaded settings - * @dataProvider dataTests - */ - public function testGetWithoutLoaded($data) - { - $configCache = new ConfigCache(['test' => ['it' => 'now']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn(null)->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); - - $configuration = new Configuration($configCache, $configAdapter); - - // first run is not loaded and no data is found in the DB - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); - - // second run is not loaded, but now data is found in the db (overwrote cache) - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); - - // third run is loaded and therefore cache is used - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); - } - - /** - * Test the configuration delete() method without adapter + * Test the configuration delete() method without a model/db + * * @dataProvider dataTests */ public function testDeleteWithoutDB($data) { - $configCache = new ConfigCache(['test' => ['it' => $data]]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); + $this->configCache->load(['test' => ['it' => $data]]); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - $this->assertTrue($configuration->delete('test', 'it')); - $this->assertNull($configuration->get('test', 'it')); - $this->assertNull($configuration->getCache()->get('test', 'it')); + $this->assertTrue($this->testedConfig->delete('test', 'it')); + $this->assertNull($this->testedConfig->get('test', 'it')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'it')); - $this->assertEmpty($configuration->getCache()->getAll()); + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } /** - * Test the configuration delete() method with adapter + * Test the configuration delete() method with a model/db */ public function testDeleteWithDB() { - $configCache = new ConfigCache(['test' => ['it' => 'now', 'quarter' => 'true']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(6); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); + $this->configCache->load(['test' => ['it' => 'now', 'quarter' => 'true']]); - $configAdapter->shouldReceive('delete')->with('test', 'it')->andReturn(false)->once(); + $this->configModel->shouldReceive('delete') + ->with('test', 'it') + ->andReturn(false) + ->once(); + $this->configModel->shouldReceive('delete') + ->with('test', 'second') + ->andReturn(true) + ->once(); + $this->configModel->shouldReceive('delete') + ->with('test', 'third') + ->andReturn(false) + ->once(); + $this->configModel->shouldReceive('delete') + ->with('test', 'quarter') + ->andReturn(true) + ->once(); - $configAdapter->shouldReceive('delete')->with('test', 'second')->andReturn(true)->once(); - $configAdapter->shouldReceive('delete')->with('test', 'third')->andReturn(false)->once(); - $configAdapter->shouldReceive('delete')->with('test', 'quarter')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); + // directly set the value to the cache + $this->testedConfig->getCache()->set('test', 'it', 'now'); - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); // delete from cache only - $this->assertTrue($configuration->delete('test', 'it')); + $this->assertTrue($this->testedConfig->delete('test', 'it')); // delete from db only - $this->assertTrue($configuration->delete('test', 'second')); + $this->assertTrue($this->testedConfig->delete('test', 'second')); // no delete - $this->assertFalse($configuration->delete('test', 'third')); + $this->assertFalse($this->testedConfig->delete('test', 'third')); // delete both - $this->assertTrue($configuration->delete('test', 'quarter')); + $this->assertTrue($this->testedConfig->delete('test', 'quarter')); - $this->assertEmpty($configuration->getCache()->getAll()); + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } } diff --git a/tests/src/Core/Config/JitConfigurationTest.php b/tests/src/Core/Config/JitConfigurationTest.php new file mode 100644 index 0000000000..79aada48d6 --- /dev/null +++ b/tests/src/Core/Config/JitConfigurationTest.php @@ -0,0 +1,167 @@ +configCache, $this->configModel); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testSetUp(array $data) + { + $this->configModel->shouldReceive('load') + ->with('config') + ->andReturn(['config' => $data['config']]) + ->once(); + + parent::testSetUp($data); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testLoad(array $data, array $possibleCats, array $load) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(count($load) + 1); + + $this->configModel->shouldReceive('load') + ->with('config') + ->andReturn(['config' => $data['config']]) + ->once(); + + foreach ($load as $loadCat) { + $this->configModel->shouldReceive('load') + ->with($loadCat) + ->andReturn([$loadCat => $data[$loadCat]]) + ->once(); + } + + parent::testLoad($data, $possibleCats, $load); + } + + /** + * @dataProvider dataDoubleLoad + */ + public function testCacheLoadDouble(array $data1, array $data2, array $expect) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(count($data1) + count($data2) + 1); + + $this->configModel->shouldReceive('load') + ->with('config') + ->andReturn(['config' => $data1['config']]) + ->once(); + + foreach ($data1 as $cat => $data) { + $this->configModel->shouldReceive('load') + ->with($cat) + ->andReturn([$cat => $data]) + ->once(); + } + + + foreach ($data2 as $cat => $data) { + $this->configModel->shouldReceive('load') + ->with($cat) + ->andReturn([$cat => $data]) + ->once(); + } + + parent::testCacheLoadDouble($data1, $data2, $expect); + + // Assert the expected categories + foreach ($data2 as $cat => $data) { + $this->assertConfig($cat, $expect[$cat]); + } + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithDB($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(3); + + $this->configModel->shouldReceive('load')->with('config')->andReturn(['config' => []])->once(); + + parent::testSetGetWithDB($data); + } + + /** + * @dataProvider dataTests + */ + public function testGetWithRefresh($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(4); + + // constructor loading + $this->configModel->shouldReceive('load') + ->with('config') + ->andReturn(['config' => []]) + ->once(); + + // mocking one get + $this->configModel->shouldReceive('get') + ->with('test', 'it') + ->andReturn($data) + ->once(); + + // mocking second get + $this->configModel->shouldReceive('get') + ->with('test', 'not') + ->andReturn(null) + ->once(); + + parent::testGetWithRefresh($data); + } + + public function testGetWrongWithoutDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(4); + + parent::testGetWrongWithoutDB(); + } + + /** + * @dataProvider dataTests + */ + public function testDeleteWithoutDB($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(4); + + parent::testDeleteWithoutDB($data); + } + + public function testDeleteWithDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(6); + + // constructor loading + $this->configModel->shouldReceive('load') + ->with('config') + ->andReturn(['config' => []]) + ->once(); + + parent::testDeleteWithDB(); + } +} diff --git a/tests/src/Core/Config/PreloadConfigurationTest.php b/tests/src/Core/Config/PreloadConfigurationTest.php new file mode 100644 index 0000000000..88044395b1 --- /dev/null +++ b/tests/src/Core/Config/PreloadConfigurationTest.php @@ -0,0 +1,140 @@ +configCache, $this->configModel); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testSetUp(array $data) + { + $this->configModel->shouldReceive('load') + ->andReturn($data) + ->once(); + + parent::testSetUp($data); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testLoad(array $data, array $possibleCats, array $load) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); + + $this->configModel->shouldReceive('load') + ->andReturn($data) + ->once(); + + parent::testLoad($data, $possibleCats, $load); + + // Assert that every category is loaded everytime + foreach ($data as $cat => $values) { + $this->assertConfig($cat, $values); + } + } + + /** + * @dataProvider dataDoubleLoad + */ + public function testCacheLoadDouble(array $data1, array $data2, array $expect) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); + + $this->configModel->shouldReceive('load') + ->andReturn($data1) + ->once(); + + parent::testCacheLoadDouble($data1, $data2, $expect); + + // Assert that every category is loaded everytime and is NOT overwritten + foreach ($data1 as $cat => $values) { + $this->assertConfig($cat, $values); + } + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithDB($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(2); + + $this->configModel->shouldReceive('load')->andReturn(['config' => []])->once(); + + parent::testSetGetWithDB($data); + } + + /** + * @dataProvider dataTests + */ + public function testGetWithRefresh($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(2); + + // constructor loading + $this->configModel->shouldReceive('load') + ->andReturn(['config' => []]) + ->once(); + + // mocking one get + $this->configModel->shouldReceive('get') + ->with('test', 'it') + ->andReturn($data) + ->once(); + + parent::testGetWithRefresh($data); + } + + + public function testGetWrongWithoutDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(2); + + parent::testGetWrongWithoutDB(); + } + + /** + * @dataProvider dataTests + */ + public function testDeleteWithoutDB($data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(2); + + parent::testDeleteWithoutDB($data); + } + + public function testDeleteWithDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(5); + + // constructor loading + $this->configModel->shouldReceive('load') + ->andReturn(['config' => []]) + ->once(); + + parent::testDeleteWithDB(); + } +}