]> git.mxchange.org Git - friendica.git/commitdiff
Move Preload/JIT Configuration logic from Adapter to Core-Configuration
authorPhilipp Holzer <admin+github@philipp.info>
Tue, 2 Jul 2019 22:42:47 +0000 (00:42 +0200)
committerPhilipp Holzer <admin+github@philipp.info>
Sun, 14 Jul 2019 20:09:07 +0000 (22:09 +0200)
src/Core/Config/Adapter/IConfigAdapter.php [deleted file]
src/Core/Config/Adapter/JITConfigAdapter.php [deleted file]
src/Core/Config/Adapter/PreloadConfigAdapter.php [deleted file]
src/Core/Config/Configuration.php
src/Core/Config/JitConfiguration.php [new file with mode: 0644]
src/Core/Config/PreloadConfiguration.php [new file with mode: 0644]
src/Model/Config/Config.php [new file with mode: 0644]
src/Model/Config/DbaConfig.php [new file with mode: 0644]
tests/src/Core/Config/ConfigurationTest.php
tests/src/Core/Config/JitConfigurationTest.php [new file with mode: 0644]
tests/src/Core/Config/PreloadConfigurationTest.php [new file with mode: 0644]

diff --git a/src/Core/Config/Adapter/IConfigAdapter.php b/src/Core/Config/Adapter/IConfigAdapter.php
deleted file mode 100644 (file)
index 892c476..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-namespace Friendica\Core\Config\Adapter;
-
-/**
- *
- * @author Hypolite Petovan <hypolite@mrpetovan.com>
- */
-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 (file)
index d125f7d..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-namespace Friendica\Core\Config\Adapter;
-
-use Friendica\Database\DBA;
-
-/**
- * JustInTime Configuration Adapter
- *
- * Default Config Adapter. Provides the best performance for pages loading few configuration variables.
- *
- * @author Hypolite Petovan <hypolite@mrpetovan.com>
- */
-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 (file)
index c691c88..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-namespace Friendica\Core\Config\Adapter;
-
-use Friendica\Database\DBA;
-
-/**
- * Preload Configuration Adapter
- *
- * Minimizes the number of database queries to retrieve configuration values at the cost of memory.
- *
- * @author Hypolite Petovan <hypolite@mrpetovan.com>
- */
-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;
-       }
-}
index 1489d91de04823104178c0f954f6d04e5249e212..9654fb287f8cc156c0361f396b7006b972f15fe0 100644 (file)
 
 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 (file)
index 0000000..f8260c9
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+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\ConfigCache )
+ * - The Config-DB-Table (per Config-DB-model @see Model\Config\Config )
+ */
+class JitConfiguration extends Configuration
+{
+       /** @var array */
+       private $in_db;
+
+       /**
+        * @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, Model\Config\Config $configModel)
+       {
+               parent::__construct($configCache, $configModel);
+               $this->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 (file)
index 0000000..bf57661
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+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\ConfigCache )
+ * - The Config-DB-Table (per Config-DB-model @see Model\Config\Config )
+ */
+class PreloadConfiguration extends Configuration
+{
+       /** @var bool */
+       private $config_loaded;
+
+       /**
+        * @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, Model\Config\Config $configModel)
+       {
+               parent::__construct($configCache, $configModel);
+               $this->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 (file)
index 0000000..e7dd0a5
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+namespace Friendica\Model\Config;
+
+
+class Config extends DbaConfig
+{
+       /**
+        * 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(string $cat = null)
+       {
+               $return = [];
+
+               if (empty($cat)) {
+                       $configs = $this->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 (file)
index 0000000..bbd62c7
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+namespace Friendica\Model\Config;
+
+use Friendica\Database\Database;
+
+abstract class DbaConfig
+{
+       /** @var Database */
+       protected $dba;
+
+       public function __construct(Database $dba)
+       {
+               $this->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;
+               }
+       }
+}
index fda69896fdcc0f6c505fb24ee9915d32dc5c0742..b9546cea7f392834691257a37243771bba02af1e 100644 (file)
@@ -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();
-
-               $configuration = new Configuration($configCache, $configAdapter);
-               $configuration->load('testing');
-
-               $this->assertEquals('it', $configuration->get('testing', 'test'));
-               $this->assertEquals('it', $configuration->getCache()->get('testing', 'test'));
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache());
+
+               foreach ($load as $loadedCats) {
+                       $this->testedConfig->load($loadedCats);
+               }
+
+               // 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();
-
-               $configuration = new Configuration($configCache, $configAdapter);
-               $configuration->load('testing');
-
-               $this->assertEquals('it', $configuration->get('testing', 'test'));
-               $this->assertEquals('it', $configuration->getCache()->get('testing', 'test'));
-
-               $configuration->load('testing');
-
-               $this->assertEquals('again', $configuration->get('testing', 'test'));
-               $this->assertEquals('again', $configuration->getCache()->get('testing', 'test'));
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache());
+
+               foreach ($data1 as $cat => $data) {
+                       $this->testedConfig->load($cat);
+               }
+
+               // Assert at least loaded cats are loaded
+               foreach ($data1 as $cat => $data) {
+                       $this->assertConfig($cat, $data);
+               }
+
+               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 '!<unset>!' 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();
-
-               $configuration = new Configuration($configCache, $configAdapter);
+               $this->configCache->load(['test' => ['it' => 'now']]);
+
+               $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'));
-       }
-
-       /**
-        * 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'));
+               $this->assertEquals('default', $this->testedConfig->get('test', 'not', 'default'));
+               $this->assertNull($this->testedConfig->getCache()->get('test', 'not'));
        }
 
        /**
-        * 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();
-
-               $configAdapter->shouldReceive('delete')->with('test', 'it')->andReturn(false)->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();
-
-               $configuration = new Configuration($configCache, $configAdapter);
-
-               $this->assertEquals('now', $configuration->get('test', 'it'));
-               $this->assertEquals('now', $configuration->getCache()->get('test', 'it'));
+               $this->configCache->load(['test' => ['it' => 'now', 'quarter' => 'true']]);
+
+               $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();
+
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache());
+
+               // directly set the value to the cache
+               $this->testedConfig->getCache()->set('test', 'it', 'now');
+
+               $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 (file)
index 0000000..79aada4
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+
+namespace Friendica\Test\src\Core\Config;
+
+use Friendica\Core\Config\JitConfiguration;
+
+class JitConfigurationTest extends ConfigurationTest
+{
+       public function getInstance()
+       {
+               return new JitConfiguration($this->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 (file)
index 0000000..8804439
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+
+namespace Friendica\Test\src\Core\Config;
+
+use Friendica\Core\Config\PreloadConfiguration;
+
+class PreloadConfigurationTest extends ConfigurationTest
+{
+       public function getInstance()
+       {
+               return new PreloadConfiguration($this->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();
+       }
+}