diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 07df0637d57f4..1d6d14a503f54 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -2980,9 +2980,6 @@
-
-
-
@@ -3301,11 +3298,6 @@
-
-
-
-
-
@@ -3314,11 +3306,6 @@
-
-
- fastCache[$app][$key] ?? $default]]>
-
-
@@ -4015,9 +4002,6 @@
-
-
-
@@ -4394,9 +4378,6 @@
-
-
-
diff --git a/config/config.sample.php b/config/config.sample.php
index 37871669309e9..ea8766b970b7b 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -1792,6 +1792,15 @@
*/
'cache_chunk_gc_ttl' => 60*60*24,
+/**
+ * Enable caching of the app config values.
+ * If enabled the app config will be cached locally for a short TTL,
+ * reducing database load significatly on larger setups.
+ *
+ * Defaults to ``true``
+ */
+'cache_app_config' => true,
+
/**
* Using Object Store with Nextcloud
*/
diff --git a/core/Command/Encryption/Enable.php b/core/Command/Encryption/Enable.php
index 2c476185692c5..02045fa1a4ba6 100644
--- a/core/Command/Encryption/Enable.php
+++ b/core/Command/Encryption/Enable.php
@@ -42,8 +42,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln('No encryption module is loaded');
return 1;
}
- $defaultModule = $this->config->getAppValue('core', 'default_encryption_module', null);
- if ($defaultModule === null) {
+ $defaultModule = $this->config->getAppValue('core', 'default_encryption_module');
+ if ($defaultModule === '') {
$output->writeln('No default module is set');
return 1;
}
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index ad486a8a8f7c5..b09a165451f09 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -246,3 +246,16 @@ Cypress.Commands.add('userFileExists', (user: string, path: string) => {
return cy.runCommand(`stat --printf="%s" "data/${user}/files/${path}"`, { failOnNonZeroExit: true })
.then((exec) => Number.parseInt(exec.stdout || '0'))
})
+
+Cypress.Commands.add('runOccCommand', (command: string, options?: Partial) => {
+ return cy.runCommand(`php ./occ ${command}`, options)
+ .then((context) =>
+ // OCC cannot clear the APCu cache
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
+ cy.wait(
+ command.startsWith('app:') || command.startsWith('config:')
+ ? 3000 // clear APCu cache
+ : 0,
+ ).then(() => context),
+ )
+})
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index c80ee52eb0dc6..c4a6989df1317 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -195,7 +195,7 @@ public function setAppValue($appName, $key, $value) {
* @deprecated 29.0.0 Use {@see IAppConfig} directly
*/
public function getAppValue($appName, $key, $default = '') {
- return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default);
+ return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default) ?? $default;
}
/**
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index cef612536d620..2fbc7fe38125f 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -14,8 +14,8 @@
use OC\AppFramework\Bootstrap\Coordinator;
use OC\Config\ConfigManager;
use OC\Config\PresetManager;
+use OC\Memcache\Factory as CacheFactory;
use OCP\Config\Lexicon\Entry;
-use OCP\Config\Lexicon\ILexicon;
use OCP\Config\Lexicon\Strictness;
use OCP\Config\ValueType;
use OCP\DB\Exception as DBException;
@@ -24,6 +24,8 @@
use OCP\Exceptions\AppConfigTypeConflictException;
use OCP\Exceptions\AppConfigUnknownKeyException;
use OCP\IAppConfig;
+use OCP\ICache;
+use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
@@ -53,10 +55,12 @@ class AppConfig implements IAppConfig {
private const KEY_MAX_LENGTH = 64;
private const ENCRYPTION_PREFIX = '$AppConfigEncryption$';
private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX)
+ private const LOCAL_CACHE_KEY = 'OC\\AppConfig';
+ private const LOCAL_CACHE_TTL = 3;
- /** @var array> ['app_id' => ['config_key' => 'config_value']] */
+ /** @var array> ['app_id' => ['config_key' => 'config_value']] */
private array $fastCache = []; // cache for normal config keys
- /** @var array> ['app_id' => ['config_key' => 'config_value']] */
+ /** @var array> ['app_id' => ['config_key' => 'config_value']] */
private array $lazyCache = []; // cache for lazy config keys
/** @var array> ['app_id' => ['config_key' => bitflag]] */
private array $valueTypes = []; // type for all config values
@@ -67,6 +71,7 @@ class AppConfig implements IAppConfig {
private bool $ignoreLexiconAliases = false;
/** @var ?array */
private ?array $appVersionsCache = null;
+ private ?ICache $localCache = null;
public function __construct(
protected IDBConnection $connection,
@@ -75,7 +80,13 @@ public function __construct(
private readonly PresetManager $presetManager,
protected LoggerInterface $logger,
protected ICrypto $crypto,
+ readonly CacheFactory $cacheFactory,
) {
+ if ($config->getSystemValueBool('cache_app_config', true) && $cacheFactory->isLocalCacheAvailable()) {
+ $cacheFactory->withServerVersionPrefix(function (ICacheFactory $factory) {
+ $this->localCache = $factory->createLocal();
+ });
+ }
}
/**
@@ -85,7 +96,7 @@ public function __construct(
* @since 7.0.0
*/
public function getApps(): array {
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache));
sort($apps);
@@ -103,7 +114,7 @@ public function getApps(): array {
*/
public function getKeys(string $app): array {
$this->assertParams($app);
- $this->loadConfigAll($app);
+ $this->loadConfig($app, true);
$keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? []));
sort($keys);
@@ -149,19 +160,16 @@ public function searchKeys(string $app, string $prefix = '', bool $lazy = false)
*/
public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
- $this->loadConfig($app, $lazy);
+ $this->loadConfig($app, $lazy ?? true);
$this->matchAndApplyLexiconDefinition($app, $key);
+ $hasLazy = isset($this->lazyCache[$app][$key]);
+ $hasFast = isset($this->fastCache[$app][$key]);
if ($lazy === null) {
- $appCache = $this->getAllValues($app);
- return isset($appCache[$key]);
- }
-
- if ($lazy) {
- return isset($this->lazyCache[$app][$key]);
+ return $hasLazy || $hasFast;
+ } else {
+ return $lazy ? $hasLazy : $hasFast;
}
-
- return isset($this->fastCache[$app][$key]);
}
/**
@@ -175,7 +183,7 @@ public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
*/
public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
- $this->loadConfig(null, $lazy);
+ $this->loadConfig(null, $lazy ?? true);
$this->matchAndApplyLexiconDefinition($app, $key);
if (!isset($this->valueTypes[$app][$key])) {
@@ -227,7 +235,7 @@ public function isLazy(string $app, string $key): bool {
public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array {
$this->assertParams($app, $prefix);
// if we want to filter values, we need to get sensitivity
- $this->loadConfigAll($app);
+ $this->loadConfig($app, true);
// array_merge() will remove numeric keys (here config keys), so addition arrays instead
$values = $this->formatAppValues($app, ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []));
$values = array_filter(
@@ -479,7 +487,7 @@ private function getTypedValue(
return $default;
}
- $this->loadConfig($app, $lazy);
+ $this->loadConfig($app, $lazy ?? true);
/**
* We ignore check if mixed type is requested.
@@ -551,7 +559,7 @@ public function getValueType(string $app, string $key, ?bool $lazy = null): int
}
$this->assertParams($app, $key);
- $this->loadConfig($app, $lazy);
+ $this->loadConfig($app, $lazy ?? true);
if (!isset($this->valueTypes[$app][$key])) {
throw new AppConfigUnknownKeyException('unknown config key');
@@ -788,7 +796,7 @@ private function setTypedValue(
if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type)) {
return false; // returns false as database is not updated
}
- $this->loadConfig(null, $lazy);
+ $this->loadConfig(null, $lazy ?? true);
$sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
$inserted = $refreshCache = false;
@@ -803,7 +811,7 @@ private function setTypedValue(
* no update if key is already known with set lazy status and value is
* not different, unless sensitivity is switched from false to true.
*/
- if ($origValue === $this->getTypedValue($app, $key, $value, $lazy, $type)
+ if ($origValue === $this->getTypedValue($app, $key, $value, $lazy ?? true, $type)
&& (!$sensitive || $this->isSensitive($app, $key, $lazy))) {
return false;
}
@@ -835,7 +843,7 @@ private function setTypedValue(
if (!$inserted) {
$currType = $this->valueTypes[$app][$key] ?? 0;
if ($currType === 0) { // this might happen when switching lazy loading status
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$currType = $this->valueTypes[$app][$key] ?? 0;
}
@@ -856,7 +864,7 @@ private function setTypedValue(
&& ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
try {
$currType = $this->convertTypeToString($currType);
- $type = $this->convertTypeToString($type);
+ $this->convertTypeToString($type);
} catch (AppConfigIncorrectTypeException) {
// can be ignored, this was just needed for a better exception message.
}
@@ -895,6 +903,7 @@ private function setTypedValue(
$this->fastCache[$app][$key] = $value;
}
$this->valueTypes[$app][$key] = $type;
+ $this->clearLocalCache();
return true;
}
@@ -916,7 +925,7 @@ private function setTypedValue(
*/
public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
$this->assertParams($app, $key);
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$this->matchAndApplyLexiconDefinition($app, $key);
$this->isLazy($app, $key); // confirm key exists
@@ -959,7 +968,7 @@ public function updateType(string $app, string $key, int $type = self::VALUE_MIX
*/
public function updateSensitive(string $app, string $key, bool $sensitive): bool {
$this->assertParams($app, $key);
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$this->matchAndApplyLexiconDefinition($app, $key);
try {
@@ -1019,7 +1028,7 @@ public function updateSensitive(string $app, string $key, bool $sensitive): bool
*/
public function updateLazy(string $app, string $key, bool $lazy): bool {
$this->assertParams($app, $key);
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$this->matchAndApplyLexiconDefinition($app, $key);
try {
@@ -1055,7 +1064,7 @@ public function updateLazy(string $app, string $key, bool $lazy): bool {
*/
public function getDetails(string $app, string $key): array {
$this->assertParams($app, $key);
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
$this->matchAndApplyLexiconDefinition($app, $key);
$lazy = $this->isLazy($app, $key);
@@ -1198,6 +1207,7 @@ public function deleteKey(string $app, string $key): void {
unset($this->lazyCache[$app][$key]);
unset($this->fastCache[$app][$key]);
unset($this->valueTypes[$app][$key]);
+ $this->clearLocalCache();
}
/**
@@ -1227,12 +1237,13 @@ public function deleteApp(string $app): void {
public function clearCache(bool $reload = false): void {
$this->lazyLoaded = $this->fastLoaded = false;
$this->lazyCache = $this->fastCache = $this->valueTypes = $this->configLexiconDetails = [];
+ $this->localCache?->remove(self::LOCAL_CACHE_KEY);
if (!$reload) {
return;
}
- $this->loadConfigAll();
+ $this->loadConfig(lazy: true);
}
@@ -1293,35 +1304,49 @@ private function assertParams(string $app = '', string $configKey = '', bool $al
}
}
- private function loadConfigAll(?string $app = null): void {
- $this->loadConfig($app, null);
- }
-
/**
* Load normal config or config set as lazy loaded
*
- * @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config
+ * @param bool $lazy set to TRUE to also load config values set as lazy loaded
*/
- private function loadConfig(?string $app = null, ?bool $lazy = false): void {
+ private function loadConfig(?string $app = null, bool $lazy = false): void {
if ($this->isLoaded($lazy)) {
return;
}
// if lazy is null or true, we debug log
- if (($lazy ?? true) !== false && $app !== null) {
+ if ($lazy === true && $app !== null) {
$exception = new \RuntimeException('The loading of lazy AppConfig values have been triggered by app "' . $app . '"');
$this->logger->debug($exception->getMessage(), ['exception' => $exception, 'app' => $app]);
}
- $qb = $this->connection->getQueryBuilder();
- $qb->from('appconfig');
+ $loadLazyOnly = $lazy && $this->isLoaded();
- // we only need value from lazy when loadConfig does not specify it
- $qb->select('appid', 'configkey', 'configvalue', 'type');
+ /** @var array */
+ $cacheContent = $this->localCache?->get(self::LOCAL_CACHE_KEY) ?? [];
+ $includesLazyValues = !empty($cacheContent) && !empty($cacheContent['lazyCache']);
+ if (!empty($cacheContent) && (!$lazy || $includesLazyValues)) {
+ $this->valueTypes = $cacheContent['valueTypes'];
+ $this->fastCache = $cacheContent['fastCache'];
+ $this->fastLoaded = !empty($this->fastCache);
+ if ($includesLazyValues) {
+ $this->lazyCache = $cacheContent['lazyCache'];
+ $this->lazyLoaded = !empty($this->lazyCache);
+ }
+ return;
+ }
- if ($lazy !== null) {
- $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT)));
+ // Otherwise no cache available and we need to fetch from database
+ $qb = $this->connection->getQueryBuilder();
+ $qb->from('appconfig')
+ ->select('appid', 'configkey', 'configvalue', 'type');
+
+ if ($lazy === false) {
+ $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
} else {
+ if ($loadLazyOnly) {
+ $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)));
+ }
$qb->addSelect('lazy');
}
@@ -1329,56 +1354,34 @@ private function loadConfig(?string $app = null, ?bool $lazy = false): void {
$rows = $result->fetchAll();
foreach ($rows as $row) {
// most of the time, 'lazy' is not in the select because its value is already known
- if (($row['lazy'] ?? ($lazy ?? 0) ? 1 : 0) === 1) {
+ if ($lazy && ((int)$row['lazy']) === 1) {
$this->lazyCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
} else {
$this->fastCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
}
$this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0);
}
- $result->closeCursor();
- $this->setAsLoaded($lazy);
- }
- /**
- * if $lazy is:
- * - false: will returns true if fast config is loaded
- * - true : will returns true if lazy config is loaded
- * - null : will returns true if both config are loaded
- *
- * @param bool $lazy
- *
- * @return bool
- */
- private function isLoaded(?bool $lazy): bool {
- if ($lazy === null) {
- return $this->lazyLoaded && $this->fastLoaded;
- }
+ $result->closeCursor();
+ $this->localCache?->set(
+ self::LOCAL_CACHE_KEY,
+ [
+ 'fastCache' => $this->fastCache,
+ 'lazyCache' => $this->lazyCache,
+ 'valueTypes' => $this->valueTypes,
+ ],
+ self::LOCAL_CACHE_TTL,
+ );
- return $lazy ? $this->lazyLoaded : $this->fastLoaded;
+ $this->fastLoaded = true;
+ $this->lazyLoaded = $lazy;
}
/**
- * if $lazy is:
- * - false: set fast config as loaded
- * - true : set lazy config as loaded
- * - null : set both config as loaded
- *
- * @param bool $lazy
+ * @param bool $lazy - If set to true then also check if lazy values are loaded
*/
- private function setAsLoaded(?bool $lazy): void {
- if ($lazy === null) {
- $this->fastLoaded = true;
- $this->lazyLoaded = true;
-
- return;
- }
-
- if ($lazy) {
- $this->lazyLoaded = true;
- } else {
- $this->fastLoaded = true;
- }
+ private function isLoaded(bool $lazy = false): bool {
+ return $this->fastLoaded && (!$lazy || $this->lazyLoaded);
}
/**
@@ -1386,7 +1389,7 @@ private function setAsLoaded(?bool $lazy): void {
*
* @param string $app app
* @param string $key key
- * @param string $default = null, default value if the key does not exist
+ * @param string $default - Default value if the key does not exist
*
* @return string the value or $default
* @deprecated 29.0.0 use getValue*()
@@ -1394,7 +1397,7 @@ private function setAsLoaded(?bool $lazy): void {
* This function gets a value from the appconfig table. If the key does
* not exist the default value will be returned
*/
- public function getValue($app, $key, $default = null) {
+ public function getValue($app, $key, $default = '') {
$this->loadConfig($app);
$this->matchAndApplyLexiconDefinition($app, $key);
@@ -1421,7 +1424,7 @@ public function setValue($app, $key, $value) {
* or enabled (lazy=lazy-2)
*
* this solution would remove the loading of config values from disabled app
- * unless calling the method {@see loadConfigAll()}
+ * unless calling the method.
*/
return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED);
}
@@ -1733,7 +1736,7 @@ private function matchAndApplyLexiconDefinition(
*
* @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed
* @throws AppConfigUnknownKeyException if strictness implies exception
- * @see ILexicon::getStrictness()
+ * @see \OCP\Config\Lexicon\ILexicon::getStrictness()
*/
private function applyLexiconStrictness(
?Strictness $strictness,
@@ -1772,8 +1775,9 @@ public function getConfigDetailsFromLexicon(string $appId): array {
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
- if ($configEntry->getRename() !== null) {
- $aliases[$configEntry->getRename()] = $configEntry->getKey();
+ $newName = $configEntry->getRename();
+ if ($newName !== null) {
+ $aliases[$newName] = $configEntry->getKey();
}
}
@@ -1819,4 +1823,8 @@ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
}
return $this->appVersionsCache;
}
+
+ private function clearLocalCache(): void {
+ $this->localCache?->remove(self::LOCAL_CACHE_KEY);
+ }
}
diff --git a/lib/private/Installer.php b/lib/private/Installer.php
index 91d20a129ae55..fd737d286ad81 100644
--- a/lib/private/Installer.php
+++ b/lib/private/Installer.php
@@ -546,7 +546,7 @@ public static function installShippedApps(bool $softErrors = false, ?IOutput $ou
while (false !== ($filename = readdir($dir))) {
if ($filename[0] !== '.' and is_dir($app_dir['path'] . "/$filename")) {
if (file_exists($app_dir['path'] . "/$filename/appinfo/info.xml")) {
- if ($config->getAppValue($filename, 'installed_version', null) === null) {
+ if ($config->getAppValue($filename, 'installed_version') === '') {
$enabled = $appManager->isDefaultEnabled($filename);
if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
&& $config->getAppValue($filename, 'enabled') !== 'no') {
diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php
index b54189937fc12..2a3fabc89f18e 100644
--- a/lib/private/Memcache/Factory.php
+++ b/lib/private/Memcache/Factory.php
@@ -7,72 +7,65 @@
*/
namespace OC\Memcache;
-use Closure;
+use OC\SystemConfig;
use OCP\Cache\CappedMemoryCache;
+use OCP\IAppConfig;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IMemcache;
use OCP\Profiler\IProfiler;
+use OCP\ServerVersion;
use Psr\Log\LoggerInterface;
class Factory implements ICacheFactory {
public const NULL_CACHE = NullCache::class;
- private ?string $globalPrefix = null;
-
- private LoggerInterface $logger;
-
+ protected ?string $globalPrefix = null;
/**
- * @var ?class-string $localCacheClass
+ * @var class-string $localCacheClass
*/
- private ?string $localCacheClass;
+ protected string $localCacheClass;
/**
- * @var ?class-string $distributedCacheClass
+ * @var class-string $distributedCacheClass
*/
- private ?string $distributedCacheClass;
+ protected string $distributedCacheClass;
/**
- * @var ?class-string $lockingCacheClass
+ * @var class-string $lockingCacheClass
*/
- private ?string $lockingCacheClass;
-
- private string $logFile;
-
- private IProfiler $profiler;
+ protected string $lockingCacheClass;
/**
- * @param Closure $globalPrefixClosure
- * @param LoggerInterface $logger
* @param ?class-string $localCacheClass
* @param ?class-string $distributedCacheClass
* @param ?class-string $lockingCacheClass
- * @param string $logFile
*/
public function __construct(
- private Closure $globalPrefixClosure,
- LoggerInterface $logger,
- IProfiler $profiler,
+ protected LoggerInterface $logger,
+ protected IProfiler $profiler,
+ protected ServerVersion $serverVersion,
?string $localCacheClass = null,
?string $distributedCacheClass = null,
?string $lockingCacheClass = null,
- string $logFile = '',
+ protected string $logFile = '',
) {
- $this->logFile = $logFile;
-
if (!$localCacheClass) {
$localCacheClass = self::NULL_CACHE;
}
$localCacheClass = ltrim($localCacheClass, '\\');
+
if (!$distributedCacheClass) {
$distributedCacheClass = $localCacheClass;
}
-
$distributedCacheClass = ltrim($distributedCacheClass, '\\');
$missingCacheMessage = 'Memcache {class} not available for {use} cache';
$missingCacheHint = 'Is the matching PHP module installed and enabled?';
- if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) {
+ if (!class_exists($localCacheClass)
+ || !is_a($localCacheClass, ICache::class, true)
+ || !$localCacheClass::isAvailable()
+ ) {
if (\OC::$CLI && !defined('PHPUNIT_RUN') && $localCacheClass === APCu::class) {
// CLI should not fail if APCu is not available but fallback to NullCache.
// This can be the case if APCu is used without apc.enable_cli=1.
@@ -84,7 +77,11 @@ public function __construct(
]), $missingCacheHint);
}
}
- if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) {
+
+ if (!class_exists($distributedCacheClass)
+ || !is_a($distributedCacheClass, ICache::class, true)
+ || !$distributedCacheClass::isAvailable()
+ ) {
if (\OC::$CLI && !defined('PHPUNIT_RUN') && $distributedCacheClass === APCu::class) {
// CLI should not fail if APCu is not available but fallback to NullCache.
// This can be the case if APCu is used without apc.enable_cli=1.
@@ -96,25 +93,51 @@ public function __construct(
]), $missingCacheHint);
}
}
- if (!($lockingCacheClass && class_exists($lockingCacheClass) && $lockingCacheClass::isAvailable())) {
+
+ if (!$lockingCacheClass
+ || !class_exists($lockingCacheClass)
+ || !is_a($lockingCacheClass, IMemcache::class, true)
+ || !$lockingCacheClass::isAvailable()
+ ) {
// don't fall back since the fallback might not be suitable for storing lock
$lockingCacheClass = self::NULL_CACHE;
}
+ /** @var class-string */
$lockingCacheClass = ltrim($lockingCacheClass, '\\');
$this->localCacheClass = $localCacheClass;
$this->distributedCacheClass = $distributedCacheClass;
$this->lockingCacheClass = $lockingCacheClass;
- $this->profiler = $profiler;
}
- private function getGlobalPrefix(): ?string {
- if (is_null($this->globalPrefix)) {
- $this->globalPrefix = ($this->globalPrefixClosure)();
+ protected function getGlobalPrefix(): string {
+ if ($this->globalPrefix === null) {
+ $config = \OCP\Server::get(SystemConfig::class);
+ $versions = [];
+ if ($config->getValue('installed', false)) {
+ $appConfig = \OCP\Server::get(IAppConfig::class);
+ $versions = $appConfig->getAppInstalledVersions();
+ }
+ $versions['core'] = implode('.', $this->serverVersion->getVersion());
+ $this->globalPrefix = hash('xxh128', implode(',', $versions));
}
return $this->globalPrefix;
}
+ /**
+ * Override the global prefix for a specific closure.
+ * This should only be used internally for bootstrapping purpose!
+ *
+ * @param string $globalPrefix - The prefix to use during the closure execution
+ * @param \Closure $closure - The closure with the cache factory as the first parameter
+ */
+ public function withServerVersionPrefix(\Closure $closure): void {
+ $backupPrefix = $this->globalPrefix;
+ $this->globalPrefix = hash('xxh128', implode('.', $this->serverVersion->getVersion()));
+ $closure($this);
+ $this->globalPrefix = $backupPrefix;
+ }
+
/**
* create a cache instance for storing locks
*
@@ -122,22 +145,17 @@ private function getGlobalPrefix(): ?string {
* @return IMemcache
*/
public function createLocking(string $prefix = ''): IMemcache {
- $globalPrefix = $this->getGlobalPrefix();
- if (is_null($globalPrefix)) {
- return new ArrayCache($prefix);
- }
-
- assert($this->lockingCacheClass !== null);
- $cache = new $this->lockingCacheClass($globalPrefix . '/' . $prefix);
- if ($this->lockingCacheClass === Redis::class && $this->profiler->isEnabled()) {
- // We only support the profiler with Redis
- $cache = new ProfilerWrapperCache($cache, 'Locking');
- $this->profiler->add($cache);
- }
+ $cache = new $this->lockingCacheClass($this->getGlobalPrefix() . '/' . $prefix);
+ if ($this->lockingCacheClass === Redis::class) {
+ if ($this->profiler->isEnabled()) {
+ // We only support the profiler with Redis
+ $cache = new ProfilerWrapperCache($cache, 'Locking');
+ $this->profiler->add($cache);
+ }
- if ($this->lockingCacheClass === Redis::class
- && $this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
- $cache = new LoggerWrapperCache($cache, $this->logFile);
+ if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
+ $cache = new LoggerWrapperCache($cache, $this->logFile);
+ }
}
return $cache;
}
@@ -149,22 +167,17 @@ public function createLocking(string $prefix = ''): IMemcache {
* @return ICache
*/
public function createDistributed(string $prefix = ''): ICache {
- $globalPrefix = $this->getGlobalPrefix();
- if (is_null($globalPrefix)) {
- return new ArrayCache($prefix);
- }
-
- assert($this->distributedCacheClass !== null);
- $cache = new $this->distributedCacheClass($globalPrefix . '/' . $prefix);
- if ($this->distributedCacheClass === Redis::class && $this->profiler->isEnabled()) {
- // We only support the profiler with Redis
- $cache = new ProfilerWrapperCache($cache, 'Distributed');
- $this->profiler->add($cache);
- }
+ $cache = new $this->distributedCacheClass($this->getGlobalPrefix() . '/' . $prefix);
+ if ($this->distributedCacheClass === Redis::class) {
+ if ($this->profiler->isEnabled()) {
+ // We only support the profiler with Redis
+ $cache = new ProfilerWrapperCache($cache, 'Distributed');
+ $this->profiler->add($cache);
+ }
- if ($this->distributedCacheClass === Redis::class && $this->logFile !== ''
- && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
- $cache = new LoggerWrapperCache($cache, $this->logFile);
+ if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
+ $cache = new LoggerWrapperCache($cache, $this->logFile);
+ }
}
return $cache;
}
@@ -176,22 +189,17 @@ public function createDistributed(string $prefix = ''): ICache {
* @return ICache
*/
public function createLocal(string $prefix = ''): ICache {
- $globalPrefix = $this->getGlobalPrefix();
- if (is_null($globalPrefix)) {
- return new ArrayCache($prefix);
- }
-
- assert($this->localCacheClass !== null);
- $cache = new $this->localCacheClass($globalPrefix . '/' . $prefix);
- if ($this->localCacheClass === Redis::class && $this->profiler->isEnabled()) {
- // We only support the profiler with Redis
- $cache = new ProfilerWrapperCache($cache, 'Local');
- $this->profiler->add($cache);
- }
+ $cache = new $this->localCacheClass($this->getGlobalPrefix() . '/' . $prefix);
+ if ($this->localCacheClass === Redis::class) {
+ if ($this->profiler->isEnabled()) {
+ // We only support the profiler with Redis
+ $cache = new ProfilerWrapperCache($cache, 'Local');
+ $this->profiler->add($cache);
+ }
- if ($this->localCacheClass === Redis::class && $this->logFile !== ''
- && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
- $cache = new LoggerWrapperCache($cache, $this->logFile);
+ if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
+ $cache = new LoggerWrapperCache($cache, $this->logFile);
+ }
}
return $cache;
}
@@ -217,4 +225,11 @@ public function createInMemory(int $capacity = 512): ICache {
public function isLocalCacheAvailable(): bool {
return $this->localCacheClass !== self::NULL_CACHE;
}
+
+ public function clearAll(): void {
+ $this->createLocal()->clear();
+ $this->createDistributed()->clear();
+ $this->createLocking()->clear();
+ $this->createInMemory()->clear();
+ }
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 22cd13438b847..49bda19a738bc 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -585,62 +585,37 @@ public function __construct($webRoot, \OC\Config $config) {
$this->registerAlias(IURLGenerator::class, URLGenerator::class);
- $this->registerService(ICache::class, function ($c) {
- return new Cache\File();
- });
-
+ $this->registerAlias(ICache::class, Cache\File::class);
$this->registerService(Factory::class, function (Server $c) {
$profiler = $c->get(IProfiler::class);
- $arrayCacheFactory = new \OC\Memcache\Factory(fn () => '', $c->get(LoggerInterface::class),
- $profiler,
- ArrayCache::class,
- ArrayCache::class,
- ArrayCache::class
- );
+ $logger = $c->get(LoggerInterface::class);
+ $serverVersion = $c->get(ServerVersion::class);
/** @var SystemConfig $config */
$config = $c->get(SystemConfig::class);
- /** @var ServerVersion $serverVersion */
- $serverVersion = $c->get(ServerVersion::class);
-
- if ($config->getValue('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
- $logQuery = $config->getValue('log_query');
- $prefixClosure = function () use ($logQuery, $serverVersion): ?string {
- if (!$logQuery) {
- try {
- $v = \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions(true);
- } catch (\Doctrine\DBAL\Exception $e) {
- // Database service probably unavailable
- // Probably related to https://github.com/nextcloud/server/issues/37424
- return null;
- }
- } else {
- // If the log_query is enabled, we can not get the app versions
- // as that does a query, which will be logged and the logging
- // depends on redis and here we are back again in the same function.
- $v = [
- 'log_query' => 'enabled',
- ];
- }
- $v['core'] = implode(',', $serverVersion->getVersion());
- $version = implode(',', array_keys($v)) . implode(',', $v);
- $instanceId = \OC_Util::getInstanceId();
- $path = \OC::$SERVERROOT;
- return md5($instanceId . '-' . $version . '-' . $path);
- };
- return new \OC\Memcache\Factory($prefixClosure,
- $c->get(LoggerInterface::class),
+ if (!$config->getValue('installed', false) || (defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
+ return new \OC\Memcache\Factory(
+ $logger,
$profiler,
- /** @psalm-taint-escape callable */
- $config->getValue('memcache.local', null),
- /** @psalm-taint-escape callable */
- $config->getValue('memcache.distributed', null),
- /** @psalm-taint-escape callable */
- $config->getValue('memcache.locking', null),
- /** @psalm-taint-escape callable */
- $config->getValue('redis_log_file')
+ $serverVersion,
+ ArrayCache::class,
+ ArrayCache::class,
+ ArrayCache::class
);
}
- return $arrayCacheFactory;
+
+ return new \OC\Memcache\Factory(
+ $logger,
+ $profiler,
+ $serverVersion,
+ /** @psalm-taint-escape callable */
+ $config->getValue('memcache.local', null),
+ /** @psalm-taint-escape callable */
+ $config->getValue('memcache.distributed', null),
+ /** @psalm-taint-escape callable */
+ $config->getValue('memcache.locking', null),
+ /** @psalm-taint-escape callable */
+ $config->getValue('redis_log_file')
+ );
});
$this->registerAlias(ICacheFactory::class, Factory::class);
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index 10b78e2a7ef19..6dbef42594bf9 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -685,7 +685,7 @@ public static function updateApp(string $appId): bool {
//set remote/public handlers
if (array_key_exists('ocsid', $appData)) {
\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
- } elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
+ } elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid') !== '') {
\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
}
foreach ($appData['remote'] as $name => $path) {
diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php
index 3bc64c5e13337..d22606672aae4 100644
--- a/lib/public/IConfig.php
+++ b/lib/public/IConfig.php
@@ -112,8 +112,8 @@ public function getAppKeys($appName);
* Writes a new app wide value
*
* @param string $appName the appName that we want to store the value under
- * @param string|float|int $key the key of the value, under which will be saved
- * @param string $value the value that should be stored
+ * @param string $key the key of the value, under which will be saved
+ * @param string|float|int $value the value that should be stored
* @return void
* @since 6.0.0
* @deprecated 29.0.0 Use {@see IAppConfig} directly
diff --git a/tests/Core/Command/Encryption/EnableTest.php b/tests/Core/Command/Encryption/EnableTest.php
index 32d1a7576f5f5..0e9655c29c778 100644
--- a/tests/Core/Command/Encryption/EnableTest.php
+++ b/tests/Core/Command/Encryption/EnableTest.php
@@ -48,9 +48,9 @@ protected function setUp(): void {
public static function dataEnable(): array {
return [
- ['no', null, [], true, 'Encryption enabled', 'No encryption module is loaded'],
- ['yes', null, [], false, 'Encryption is already enabled', 'No encryption module is loaded'],
- ['no', null, ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'No default module is set'],
+ ['no', '', [], true, 'Encryption enabled', 'No encryption module is loaded'],
+ ['yes', '', [], false, 'Encryption is already enabled', 'No encryption module is loaded'],
+ ['no', '', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'No default module is set'],
['no', 'OC_NO_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'The current default module does not exist: OC_NO_MODULE'],
['no', 'OC_TEST_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'Default module: OC_TEST_MODULE'],
];
@@ -79,7 +79,7 @@ public function testEnable(string $oldStatus, ?string $defaultModule, array $ava
->method('getAppValue')
->willReturnMap([
['core', 'encryption_enabled', 'no', $oldStatus],
- ['core', 'default_encryption_module', null, $defaultModule],
+ ['core', 'default_encryption_module', '', $defaultModule],
]);
}
diff --git a/tests/lib/App/AppManagerTest.php b/tests/lib/App/AppManagerTest.php
index 6637c529a1e53..9a4716aa8f10a 100644
--- a/tests/lib/App/AppManagerTest.php
+++ b/tests/lib/App/AppManagerTest.php
@@ -233,28 +233,25 @@ public function testEnableApp(): void {
$this->manager->disableApp('files_trashbin');
}
$this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('files_trashbin'));
+
$this->manager->enableApp('files_trashbin');
$this->assertEquals('yes', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
}
public function testDisableApp(): void {
$this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppDisableEvent('files_trashbin'));
+
$this->manager->disableApp('files_trashbin');
$this->assertEquals('no', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
}
public function testNotEnableIfNotInstalled(): void {
- try {
- $this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
- $this->assertFalse(true, 'If this line is reached the expected exception is not thrown.');
- } catch (AppPathNotFoundException $e) {
- // Exception is expected
- $this->assertEquals('Could not find path for some_random_name_which_i_hope_is_not_an_app', $e->getMessage());
- }
+ $this->expectException(AppPathNotFoundException::class);
+ $this->expectExceptionMessage('Could not find path for some_random_name_which_i_hope_is_not_an_app');
+ $this->appConfig->expects(self::never())
+ ->method('setValue');
- $this->assertEquals('no', $this->appConfig->getValue(
- 'some_random_name_which_i_hope_is_not_an_app', 'enabled', 'no'
- ));
+ $this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
}
public function testEnableAppForGroups(): void {
@@ -289,7 +286,9 @@ public function testEnableAppForGroups(): void {
->with('test')
->willReturn('apps/test');
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
+ $this->eventDispatcher->expects($this->once())
+ ->method('dispatchTyped')
+ ->with(new AppEnableEvent('test', ['group1', 'group2']));
$manager->enableAppForGroups('test', $groups);
$this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
diff --git a/tests/lib/AppConfigIntegrationTest.php b/tests/lib/AppConfigIntegrationTest.php
new file mode 100644
index 0000000000000..4f821e00a6354
--- /dev/null
+++ b/tests/lib/AppConfigIntegrationTest.php
@@ -0,0 +1,1515 @@
+>>
+ * [appId => [configKey, configValue, valueType, lazy, sensitive]]
+ */
+ private static array $baseStruct
+ = [
+ 'testapp' => [
+ 'enabled' => ['enabled', 'yes'],
+ 'installed_version' => ['installed_version', '1.2.3'],
+ 'depends_on' => ['depends_on', 'someapp'],
+ 'deletethis' => ['deletethis', 'deletethis'],
+ 'key' => ['key', 'value']
+ ],
+ 'searchtest' => [
+ 'search_key1' => ['search_key1', 'key1', IAppConfig::VALUE_STRING],
+ 'search_key2' => ['search_key2', 'key2', IAppConfig::VALUE_STRING],
+ 'search_key3' => ['search_key3', 'key3', IAppConfig::VALUE_STRING],
+ 'searchnot_key4' => ['searchnot_key4', 'key4', IAppConfig::VALUE_STRING],
+ 'search_key5_lazy' => ['search_key5_lazy', 'key5', IAppConfig::VALUE_STRING, true],
+ ],
+ 'someapp' => [
+ 'key' => ['key', 'value'],
+ 'otherkey' => ['otherkey', 'othervalue']
+ ],
+ '123456' => [
+ 'enabled' => ['enabled', 'yes'],
+ 'key' => ['key', 'value']
+ ],
+ 'anotherapp' => [
+ 'enabled' => ['enabled', 'no'],
+ 'installed_version' => ['installed_version', '3.2.1'],
+ 'key' => ['key', 'value']
+ ],
+ 'non-sensitive-app' => [
+ 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, false],
+ 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, false],
+ ],
+ 'sensitive-app' => [
+ 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, true],
+ 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, true],
+ ],
+ 'only-lazy' => [
+ 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true]
+ ],
+ 'typed' => [
+ 'mixed' => ['mixed', 'mix', IAppConfig::VALUE_MIXED],
+ 'string' => ['string', 'value', IAppConfig::VALUE_STRING],
+ 'int' => ['int', '42', IAppConfig::VALUE_INT],
+ 'float' => ['float', '3.14', IAppConfig::VALUE_FLOAT],
+ 'bool' => ['bool', '1', IAppConfig::VALUE_BOOL],
+ 'array' => ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
+ ],
+ 'prefix-app' => [
+ 'key1' => ['key1', 'value'],
+ 'prefix1' => ['prefix1', 'value'],
+ 'prefix-2' => ['prefix-2', 'value'],
+ 'key-2' => ['key-2', 'value'],
+ ]
+ ];
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->connection = Server::get(IDBConnection::class);
+ $this->config = Server::get(IConfig::class);
+ $this->configManager = Server::get(ConfigManager::class);
+ $this->presetManager = Server::get(PresetManager::class);
+ $this->logger = Server::get(LoggerInterface::class);
+ $this->crypto = Server::get(ICrypto::class);
+ $this->cacheFactory = $this->createMock(CacheFactory::class);
+ $this->cacheFactory->method('isLocalCacheAvailable')->willReturn(false);
+
+ // storing current config and emptying the data table
+ $sql = $this->connection->getQueryBuilder();
+ $sql->select('*')
+ ->from('appconfig');
+ $result = $sql->executeQuery();
+ $this->originalConfig = $result->fetchAll();
+ $result->closeCursor();
+
+ $sql = $this->connection->getQueryBuilder();
+ $sql->delete('appconfig');
+ $sql->executeStatement();
+
+ $sql = $this->connection->getQueryBuilder();
+ $sql->insert('appconfig')
+ ->values(
+ [
+ 'appid' => $sql->createParameter('appid'),
+ 'configkey' => $sql->createParameter('configkey'),
+ 'configvalue' => $sql->createParameter('configvalue'),
+ 'type' => $sql->createParameter('type'),
+ 'lazy' => $sql->createParameter('lazy')
+ ]
+ );
+
+ foreach (self::$baseStruct as $appId => $appData) {
+ foreach ($appData as $key => $row) {
+ $value = $row[1];
+ $type = $row[2] ?? IAppConfig::VALUE_MIXED;
+ if (($row[4] ?? false) === true) {
+ $type |= IAppConfig::VALUE_SENSITIVE;
+ $value = self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX') . $this->crypto->encrypt($value);
+ self::$baseStruct[$appId][$key]['encrypted'] = $value;
+ }
+
+ $sql->setParameters(
+ [
+ 'appid' => $appId,
+ 'configkey' => $row[0],
+ 'configvalue' => $value,
+ 'type' => $type,
+ 'lazy' => (($row[3] ?? false) === true) ? 1 : 0
+ ]
+ )->executeStatement();
+ }
+ }
+ }
+
+ protected function tearDown(): void {
+ $sql = $this->connection->getQueryBuilder();
+ $sql->delete('appconfig');
+ $sql->executeStatement();
+
+ $sql = $this->connection->getQueryBuilder();
+ $sql->insert('appconfig')
+ ->values(
+ [
+ 'appid' => $sql->createParameter('appid'),
+ 'configkey' => $sql->createParameter('configkey'),
+ 'configvalue' => $sql->createParameter('configvalue'),
+ 'lazy' => $sql->createParameter('lazy'),
+ 'type' => $sql->createParameter('type'),
+ ]
+ );
+
+ foreach ($this->originalConfig as $key => $configs) {
+ $sql->setParameter('appid', $configs['appid'])
+ ->setParameter('configkey', $configs['configkey'])
+ ->setParameter('configvalue', $configs['configvalue'])
+ ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
+ ->setParameter('type', $configs['type']);
+ $sql->executeStatement();
+ }
+
+ // $this->restoreService(AppConfig::class);
+ parent::tearDown();
+ }
+
+ /**
+ * @param bool $preLoading TRUE will preload the 'fast' cache, which is the normal behavior of usual
+ * IAppConfig
+ *
+ * @return IAppConfig
+ */
+ private function generateAppConfig(bool $preLoading = true): IAppConfig {
+ /** @var AppConfig $config */
+ $config = new AppConfig(
+ $this->connection,
+ $this->config,
+ $this->configManager,
+ $this->presetManager,
+ $this->logger,
+ $this->crypto,
+ $this->cacheFactory,
+ );
+ $msg = ' generateAppConfig() failed to confirm cache status';
+
+ // confirm cache status
+ $status = $config->statusCache();
+ $this->assertSame(false, $status['fastLoaded'], $msg);
+ $this->assertSame(false, $status['lazyLoaded'], $msg);
+ $this->assertSame([], $status['fastCache'], $msg);
+ $this->assertSame([], $status['lazyCache'], $msg);
+ if ($preLoading) {
+ // simple way to initiate the load of non-lazy config values in cache
+ $config->getValueString('core', 'preload', '');
+
+ // confirm cache status
+ $status = $config->statusCache();
+ $this->assertSame(true, $status['fastLoaded'], $msg);
+ $this->assertSame(false, $status['lazyLoaded'], $msg);
+
+ $apps = array_values(array_diff(array_keys(self::$baseStruct), ['only-lazy']));
+ $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache']), $msg);
+ $this->assertSame([], array_keys($status['lazyCache']), $msg);
+ }
+
+ return $config;
+ }
+
+ public function testGetApps(): void {
+ $config = $this->generateAppConfig(false);
+
+ $this->assertEqualsCanonicalizing(array_keys(self::$baseStruct), $config->getApps());
+ }
+
+ public function testGetAppInstalledVersions(): void {
+ $config = $this->generateAppConfig(false);
+
+ $this->assertEquals(
+ ['testapp' => '1.2.3', 'anotherapp' => '3.2.1'],
+ $config->getAppInstalledVersions(false)
+ );
+ $this->assertEquals(
+ ['testapp' => '1.2.3'],
+ $config->getAppInstalledVersions(true)
+ );
+ }
+
+ /**
+ * returns list of app and their keys
+ *
+ * @return array ['appId' => ['key1', 'key2', ]]
+ * @see testGetKeys
+ */
+ public static function providerGetAppKeys(): array {
+ $appKeys = [];
+ foreach (self::$baseStruct as $appId => $appData) {
+ $keys = [];
+ foreach ($appData as $row) {
+ $keys[] = $row[0];
+ }
+ $appKeys[] = [(string)$appId, $keys];
+ }
+
+ return $appKeys;
+ }
+
+ /**
+ * returns list of config keys
+ *
+ * @return array [appId, key, value, type, lazy, sensitive]
+ * @see testIsSensitive
+ * @see testIsLazy
+ * @see testGetKeys
+ */
+ public static function providerGetKeys(): array {
+ $appKeys = [];
+ foreach (self::$baseStruct as $appId => $appData) {
+ foreach ($appData as $row) {
+ $appKeys[] = [
+ (string)$appId, $row[0], $row[1], $row[2] ?? IAppConfig::VALUE_MIXED, $row[3] ?? false,
+ $row[4] ?? false
+ ];
+ }
+ }
+
+ return $appKeys;
+ }
+
+ /**
+ *
+ * @param string $appId
+ * @param array $expectedKeys
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppKeys')]
+ public function testGetKeys(string $appId, array $expectedKeys): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing($expectedKeys, $config->getKeys($appId));
+ }
+
+ public function testGetKeysOnUnknownAppShouldReturnsEmptyArray(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing([], $config->getKeys('unknown-app'));
+ }
+
+ /**
+ *
+ * @param string $appId
+ * @param string $configKey
+ * @param string $value
+ * @param bool $lazy
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
+ public function testHasKey(string $appId, string $configKey, string $value, int $type, bool $lazy): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(true, $config->hasKey($appId, $configKey, $lazy));
+ }
+
+ public function testHasKeyOnNonExistentKeyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(false, $config->hasKey(array_keys(self::$baseStruct)[0], 'inexistant-key'));
+ }
+
+ public function testHasKeyOnUnknownAppReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(false, $config->hasKey('inexistant-app', 'inexistant-key'));
+ }
+
+ public function testHasKeyOnMistypedAsLazyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->hasKey('non-sensitive-app', 'non-lazy-key', true));
+ }
+
+ public function testHasKeyOnMistypeAsNonLazyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->hasKey('non-sensitive-app', 'lazy-key', false));
+ }
+
+ public function testHasKeyOnMistypeAsNonLazyReturnsTrueWithLazyArgumentIsNull(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(true, $config->hasKey('non-sensitive-app', 'lazy-key', null));
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
+ public function testIsSensitive(
+ string $appId, string $configKey, string $configValue, int $type, bool $lazy, bool $sensitive,
+ ): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals($sensitive, $config->isSensitive($appId, $configKey, $lazy));
+ }
+
+ public function testIsSensitiveOnNonExistentKeyThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isSensitive(array_keys(self::$baseStruct)[0], 'inexistant-key');
+ }
+
+ public function testIsSensitiveOnUnknownAppThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isSensitive('unknown-app', 'inexistant-key');
+ }
+
+ public function testIsSensitiveOnSensitiveMistypedAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(true, $config->isSensitive('sensitive-app', 'non-lazy-key', true));
+ }
+
+ public function testIsSensitiveOnNonSensitiveMistypedAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->isSensitive('non-sensitive-app', 'non-lazy-key', true));
+ }
+
+ public function testIsSensitiveOnSensitiveMistypedAsNonLazyThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isSensitive('sensitive-app', 'lazy-key', false);
+ }
+
+ public function testIsSensitiveOnNonSensitiveMistypedAsNonLazyThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isSensitive('non-sensitive-app', 'lazy-key', false);
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
+ public function testIsLazy(string $appId, string $configKey, string $configValue, int $type, bool $lazy,
+ ): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals($lazy, $config->isLazy($appId, $configKey));
+ }
+
+ public function testIsLazyOnNonExistentKeyThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isLazy(array_keys(self::$baseStruct)[0], 'inexistant-key');
+ }
+
+ public function testIsLazyOnUnknownAppThrowsException(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->isLazy('unknown-app', 'inexistant-key');
+ }
+
+ public function testGetAllValues(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'array' => ['test' => 1],
+ 'bool' => true,
+ 'float' => 3.14,
+ 'int' => 42,
+ 'mixed' => 'mix',
+ 'string' => 'value',
+ ],
+ $config->getAllValues('typed')
+ );
+ }
+
+ public function testGetAllValuesWithEmptyApp(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(InvalidArgumentException::class);
+ $config->getAllValues('');
+ }
+
+ /**
+ *
+ * @param string $appId
+ * @param array $keys
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppKeys')]
+ public function testGetAllValuesWithEmptyKey(string $appId, array $keys): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing($keys, array_keys($config->getAllValues($appId, '')));
+ }
+
+ public function testGetAllValuesWithPrefix(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing(['prefix1', 'prefix-2'], array_keys($config->getAllValues('prefix-app', 'prefix')));
+ }
+
+ public function testSearchValues(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing(['testapp' => 'yes', '123456' => 'yes', 'anotherapp' => 'no'], $config->searchValues('enabled'));
+ }
+
+ public function testGetValueString(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame('value', $config->getValueString('typed', 'string', ''));
+ }
+
+ public function testGetValueStringOnUnknownAppReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame('default-1', $config->getValueString('typed-1', 'string', 'default-1'));
+ }
+
+ public function testGetValueStringOnNonExistentKeyReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame('default-2', $config->getValueString('typed', 'string-2', 'default-2'));
+ }
+
+ public function testGetValueStringOnWrongType(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigTypeConflictException::class);
+ $config->getValueString('typed', 'int');
+ }
+
+ public function testGetNonLazyValueStringAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame('value', $config->getValueString('non-sensitive-app', 'non-lazy-key', 'default', lazy: true));
+ }
+
+ public function testGetValueInt(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(42, $config->getValueInt('typed', 'int', 0));
+ }
+
+ public function testGetValueIntOnUnknownAppReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(1, $config->getValueInt('typed-1', 'int', 1));
+ }
+
+ public function testGetValueIntOnNonExistentKeyReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(2, $config->getValueInt('typed', 'int-2', 2));
+ }
+
+ public function testGetValueIntOnWrongType(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigTypeConflictException::class);
+ $config->getValueInt('typed', 'float');
+ }
+
+ public function testGetValueFloat(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(3.14, $config->getValueFloat('typed', 'float', 0));
+ }
+
+ public function testGetValueFloatOnNonUnknownAppReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(1.11, $config->getValueFloat('typed-1', 'float', 1.11));
+ }
+
+ public function testGetValueFloatOnNonExistentKeyReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(2.22, $config->getValueFloat('typed', 'float-2', 2.22));
+ }
+
+ public function testGetValueFloatOnWrongType(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigTypeConflictException::class);
+ $config->getValueFloat('typed', 'bool');
+ }
+
+ public function testGetValueBool(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(true, $config->getValueBool('typed', 'bool'));
+ }
+
+ public function testGetValueBoolOnUnknownAppReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->getValueBool('typed-1', 'bool', false));
+ }
+
+ public function testGetValueBoolOnNonExistentKeyReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->getValueBool('typed', 'bool-2'));
+ }
+
+ public function testGetValueBoolOnWrongType(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigTypeConflictException::class);
+ $config->getValueBool('typed', 'array');
+ }
+
+ public function testGetValueArray(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('typed', 'array', []));
+ }
+
+ public function testGetValueArrayOnUnknownAppReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame([1], $config->getValueArray('typed-1', 'array', [1]));
+ }
+
+ public function testGetValueArrayOnNonExistentKeyReturnsDefault(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame([1, 2], $config->getValueArray('typed', 'array-2', [1, 2]));
+ }
+
+ public function testGetValueArrayOnWrongType(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigTypeConflictException::class);
+ $config->getValueArray('typed', 'string');
+ }
+
+
+ /**
+ * @return array
+ * @see testGetValueType
+ *
+ * @see testGetValueMixed
+ */
+ public static function providerGetValueMixed(): array {
+ return [
+ // key, value, type
+ ['mixed', 'mix', IAppConfig::VALUE_MIXED],
+ ['string', 'value', IAppConfig::VALUE_STRING],
+ ['int', '42', IAppConfig::VALUE_INT],
+ ['float', '3.14', IAppConfig::VALUE_FLOAT],
+ ['bool', '1', IAppConfig::VALUE_BOOL],
+ ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
+ ];
+ }
+
+ /**
+ *
+ * @param string $key
+ * @param string $value
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
+ public function testGetValueMixed(string $key, string $value): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame($value, $config->getValueMixed('typed', $key));
+ }
+
+ /**
+ *
+ * @param string $key
+ * @param string $value
+ * @param int $type
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
+ public function testGetValueType(string $key, string $value, int $type): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame($type, $config->getValueType('typed', $key));
+ }
+
+ public function testGetValueTypeOnUnknownApp(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->getValueType('typed-1', 'string');
+ }
+
+ public function testGetValueTypeOnNonExistentKey(): void {
+ $config = $this->generateAppConfig();
+ $this->expectException(AppConfigUnknownKeyException::class);
+ $config->getValueType('typed', 'string-2');
+ }
+
+ public function testSetValueString(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetValueStringCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $status = $config->statusCache();
+ $this->assertSame('value-1', $status['fastCache']['feed']['string']);
+ }
+
+ public function testSetValueStringDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $config->clearCache();
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetValueStringIsUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $this->assertSame(true, $config->setValueString('feed', 'string', 'value-2'));
+ }
+
+ public function testSetValueStringIsNotUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $this->assertSame(false, $config->setValueString('feed', 'string', 'value-1'));
+ }
+
+ public function testSetValueStringIsUpdatedCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $config->setValueString('feed', 'string', 'value-2');
+ $status = $config->statusCache();
+ $this->assertSame('value-2', $status['fastCache']['feed']['string']);
+ }
+
+ public function testSetValueStringIsUpdatedDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1');
+ $config->setValueString('feed', 'string', 'value-2');
+ $config->clearCache();
+ $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetValueInt(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetValueIntCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $status = $config->statusCache();
+ $this->assertSame('42', $status['fastCache']['feed']['int']);
+ }
+
+ public function testSetValueIntDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $config->clearCache();
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetValueIntIsUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $this->assertSame(true, $config->setValueInt('feed', 'int', 17));
+ }
+
+ public function testSetValueIntIsNotUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $this->assertSame(false, $config->setValueInt('feed', 'int', 42));
+ }
+
+ public function testSetValueIntIsUpdatedCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $config->setValueInt('feed', 'int', 17);
+ $status = $config->statusCache();
+ $this->assertSame('17', $status['fastCache']['feed']['int']);
+ }
+
+ public function testSetValueIntIsUpdatedDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $config->setValueInt('feed', 'int', 17);
+ $config->clearCache();
+ $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetValueFloat(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetValueFloatCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $status = $config->statusCache();
+ $this->assertSame('3.14', $status['fastCache']['feed']['float']);
+ }
+
+ public function testSetValueFloatDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $config->clearCache();
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetValueFloatIsUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $this->assertSame(true, $config->setValueFloat('feed', 'float', 1.23));
+ }
+
+ public function testSetValueFloatIsNotUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $this->assertSame(false, $config->setValueFloat('feed', 'float', 3.14));
+ }
+
+ public function testSetValueFloatIsUpdatedCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $config->setValueFloat('feed', 'float', 1.23);
+ $status = $config->statusCache();
+ $this->assertSame('1.23', $status['fastCache']['feed']['float']);
+ }
+
+ public function testSetValueFloatIsUpdatedDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $config->setValueFloat('feed', 'float', 1.23);
+ $config->clearCache();
+ $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetValueBool(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
+ }
+
+ public function testSetValueBoolCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $status = $config->statusCache();
+ $this->assertSame('1', $status['fastCache']['feed']['bool']);
+ }
+
+ public function testSetValueBoolDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $config->clearCache();
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
+ }
+
+ public function testSetValueBoolIsUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $this->assertSame(true, $config->setValueBool('feed', 'bool', false));
+ }
+
+ public function testSetValueBoolIsNotUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $this->assertSame(false, $config->setValueBool('feed', 'bool', true));
+ }
+
+ public function testSetValueBoolIsUpdatedCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $config->setValueBool('feed', 'bool', false);
+ $status = $config->statusCache();
+ $this->assertSame('0', $status['fastCache']['feed']['bool']);
+ }
+
+ public function testSetValueBoolIsUpdatedDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true);
+ $config->setValueBool('feed', 'bool', false);
+ $config->clearCache();
+ $this->assertSame(false, $config->getValueBool('feed', 'bool', true));
+ }
+
+
+ public function testSetValueArray(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetValueArrayCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $status = $config->statusCache();
+ $this->assertSame('{"test":1}', $status['fastCache']['feed']['array']);
+ }
+
+ public function testSetValueArrayDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $config->clearCache();
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetValueArrayIsUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $this->assertSame(true, $config->setValueArray('feed', 'array', ['test' => 2]));
+ }
+
+ public function testSetValueArrayIsNotUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $this->assertSame(false, $config->setValueArray('feed', 'array', ['test' => 1]));
+ }
+
+ public function testSetValueArrayIsUpdatedCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $config->setValueArray('feed', 'array', ['test' => 2]);
+ $status = $config->statusCache();
+ $this->assertSame('{"test":2}', $status['fastCache']['feed']['array']);
+ }
+
+ public function testSetValueArrayIsUpdatedDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $config->setValueArray('feed', 'array', ['test' => 2]);
+ $config->clearCache();
+ $this->assertSame(['test' => 2], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetLazyValueString(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', true);
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
+ }
+
+ public function testSetLazyValueStringCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', true);
+ $status = $config->statusCache();
+ $this->assertSame('value-1', $status['lazyCache']['feed']['string']);
+ }
+
+ public function testSetLazyValueStringDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', true);
+ $config->clearCache();
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
+ }
+
+ public function testSetLazyValueStringAsNonLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', true);
+ $config->setValueString('feed', 'string', 'value-1', false);
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetNonLazyValueStringAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', false);
+ $config->setValueString('feed', 'string', 'value-1', true);
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
+ }
+
+ public function testSetSensitiveValueString(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetSensitiveValueStringCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $status = $config->statusCache();
+ $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['string']);
+ }
+
+ public function testSetSensitiveValueStringDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $config->clearCache();
+ $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
+ }
+
+ public function testSetNonSensitiveValueStringAsSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: false);
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $this->assertSame(true, $config->isSensitive('feed', 'string'));
+
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
+ }
+
+ public function testSetSensitiveValueStringAsNonSensitiveStaysSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $config->setValueString('feed', 'string', 'value-2', sensitive: false);
+ $this->assertSame(true, $config->isSensitive('feed', 'string'));
+
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
+ }
+
+ public function testSetSensitiveValueStringAsNonSensitiveAreStillUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', 'value-1', sensitive: true);
+ $config->setValueString('feed', 'string', 'value-2', sensitive: false);
+ $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
+
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
+ $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
+ }
+
+ public function testSetLazyValueInt(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, true);
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
+ }
+
+ public function testSetLazyValueIntCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, true);
+ $status = $config->statusCache();
+ $this->assertSame('42', $status['lazyCache']['feed']['int']);
+ }
+
+ public function testSetLazyValueIntDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, true);
+ $config->clearCache();
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
+ }
+
+ public function testSetLazyValueIntAsNonLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, true);
+ $config->setValueInt('feed', 'int', 42, false);
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetNonLazyValueIntAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, false);
+ $config->setValueInt('feed', 'int', 42, true);
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
+ }
+
+ public function testSetSensitiveValueInt(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetSensitiveValueIntCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $status = $config->statusCache();
+ $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['int']);
+ }
+
+ public function testSetSensitiveValueIntDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $config->clearCache();
+ $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetNonSensitiveValueIntAsSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42);
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $this->assertSame(true, $config->isSensitive('feed', 'int'));
+ }
+
+ public function testSetSensitiveValueIntAsNonSensitiveStaysSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $config->setValueInt('feed', 'int', 17);
+ $this->assertSame(true, $config->isSensitive('feed', 'int'));
+ }
+
+ public function testSetSensitiveValueIntAsNonSensitiveAreStillUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueInt('feed', 'int', 42, sensitive: true);
+ $config->setValueInt('feed', 'int', 17);
+ $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
+ }
+
+ public function testSetLazyValueFloat(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, true);
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
+ }
+
+ public function testSetLazyValueFloatCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, true);
+ $status = $config->statusCache();
+ $this->assertSame('3.14', $status['lazyCache']['feed']['float']);
+ }
+
+ public function testSetLazyValueFloatDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, true);
+ $config->clearCache();
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
+ }
+
+ public function testSetLazyValueFloatAsNonLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, true);
+ $config->setValueFloat('feed', 'float', 3.14, false);
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetNonLazyValueFloatAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, false);
+ $config->setValueFloat('feed', 'float', 3.14, true);
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
+ }
+
+ public function testSetSensitiveValueFloat(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetSensitiveValueFloatCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $status = $config->statusCache();
+ $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['float']);
+ }
+
+ public function testSetSensitiveValueFloatDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $config->clearCache();
+ $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetNonSensitiveValueFloatAsSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14);
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $this->assertSame(true, $config->isSensitive('feed', 'float'));
+ }
+
+ public function testSetSensitiveValueFloatAsNonSensitiveStaysSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $config->setValueFloat('feed', 'float', 1.23);
+ $this->assertSame(true, $config->isSensitive('feed', 'float'));
+ }
+
+ public function testSetSensitiveValueFloatAsNonSensitiveAreStillUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
+ $config->setValueFloat('feed', 'float', 1.23);
+ $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
+ }
+
+ public function testSetLazyValueBool(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true, true);
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
+ }
+
+ public function testSetLazyValueBoolCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true, true);
+ $status = $config->statusCache();
+ $this->assertSame('1', $status['lazyCache']['feed']['bool']);
+ }
+
+ public function testSetLazyValueBoolDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true, true);
+ $config->clearCache();
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
+ }
+
+ public function testSetLazyValueBoolAsNonLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true, true);
+ $config->setValueBool('feed', 'bool', true, false);
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
+ }
+
+ public function testSetNonLazyValueBoolAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueBool('feed', 'bool', true, false);
+ $config->setValueBool('feed', 'bool', true, true);
+ $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
+ }
+
+ public function testSetLazyValueArray(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], true);
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
+ }
+
+ public function testSetLazyValueArrayCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], true);
+ $status = $config->statusCache();
+ $this->assertSame('{"test":1}', $status['lazyCache']['feed']['array']);
+ }
+
+ public function testSetLazyValueArrayDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], true);
+ $config->clearCache();
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
+ }
+
+ public function testSetLazyValueArrayAsNonLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], true);
+ $config->setValueArray('feed', 'array', ['test' => 1], false);
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetNonLazyValueArrayAsLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], false);
+ $config->setValueArray('feed', 'array', ['test' => 1], true);
+ $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
+ }
+
+
+ public function testSetSensitiveValueArray(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetSensitiveValueArrayCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $status = $config->statusCache();
+ $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['array']);
+ }
+
+ public function testSetSensitiveValueArrayDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $config->clearCache();
+ $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testSetNonSensitiveValueArrayAsSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1]);
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $this->assertSame(true, $config->isSensitive('feed', 'array'));
+ }
+
+ public function testSetSensitiveValueArrayAsNonSensitiveStaysSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $config->setValueArray('feed', 'array', ['test' => 2]);
+ $this->assertSame(true, $config->isSensitive('feed', 'array'));
+ }
+
+ public function testSetSensitiveValueArrayAsNonSensitiveAreStillUpdated(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
+ $config->setValueArray('feed', 'array', ['test' => 2]);
+ $this->assertEqualsCanonicalizing(['test' => 2], $config->getValueArray('feed', 'array', []));
+ }
+
+ public function testUpdateNotSensitiveToSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->updateSensitive('non-sensitive-app', 'lazy-key', true);
+ $this->assertSame(true, $config->isSensitive('non-sensitive-app', 'lazy-key', true));
+ }
+
+ public function testUpdateSensitiveToNotSensitive(): void {
+ $config = $this->generateAppConfig();
+ $config->updateSensitive('sensitive-app', 'lazy-key', false);
+ $this->assertSame(false, $config->isSensitive('sensitive-app', 'lazy-key', true));
+ }
+
+ public function testUpdateSensitiveToSensitiveReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateSensitive('sensitive-app', 'lazy-key', true));
+ }
+
+ public function testUpdateNotSensitiveToNotSensitiveReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'lazy-key', false));
+ }
+
+ public function testUpdateSensitiveOnUnknownKeyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'unknown-key', true));
+ }
+
+ public function testUpdateNotLazyToLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->updateLazy('non-sensitive-app', 'non-lazy-key', true);
+ $this->assertSame(true, $config->isLazy('non-sensitive-app', 'non-lazy-key'));
+ }
+
+ public function testUpdateLazyToNotLazy(): void {
+ $config = $this->generateAppConfig();
+ $config->updateLazy('non-sensitive-app', 'lazy-key', false);
+ $this->assertSame(false, $config->isLazy('non-sensitive-app', 'lazy-key'));
+ }
+
+ public function testUpdateLazyToLazyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'lazy-key', true));
+ }
+
+ public function testUpdateNotLazyToNotLazyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'non-lazy-key', false));
+ }
+
+ public function testUpdateLazyOnUnknownKeyReturnsFalse(): void {
+ $config = $this->generateAppConfig();
+ $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'unknown-key', true));
+ }
+
+ public function testGetDetails(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'non-sensitive-app',
+ 'key' => 'lazy-key',
+ 'value' => 'value',
+ 'type' => 4,
+ 'lazy' => true,
+ 'typeString' => 'string',
+ 'sensitive' => false,
+ ],
+ $config->getDetails('non-sensitive-app', 'lazy-key')
+ );
+ }
+
+ public function testGetDetailsSensitive(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'sensitive-app',
+ 'key' => 'lazy-key',
+ 'value' => 'value',
+ 'type' => 4,
+ 'lazy' => true,
+ 'typeString' => 'string',
+ 'sensitive' => true,
+ ],
+ $config->getDetails('sensitive-app', 'lazy-key')
+ );
+ }
+
+ public function testGetDetailsInt(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'typed',
+ 'key' => 'int',
+ 'value' => '42',
+ 'type' => 8,
+ 'lazy' => false,
+ 'typeString' => 'integer',
+ 'sensitive' => false
+ ],
+ $config->getDetails('typed', 'int')
+ );
+ }
+
+ public function testGetDetailsFloat(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'typed',
+ 'key' => 'float',
+ 'value' => '3.14',
+ 'type' => 16,
+ 'lazy' => false,
+ 'typeString' => 'float',
+ 'sensitive' => false
+ ],
+ $config->getDetails('typed', 'float')
+ );
+ }
+
+ public function testGetDetailsBool(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'typed',
+ 'key' => 'bool',
+ 'value' => '1',
+ 'type' => 32,
+ 'lazy' => false,
+ 'typeString' => 'boolean',
+ 'sensitive' => false
+ ],
+ $config->getDetails('typed', 'bool')
+ );
+ }
+
+ public function testGetDetailsArray(): void {
+ $config = $this->generateAppConfig();
+ $this->assertEquals(
+ [
+ 'app' => 'typed',
+ 'key' => 'array',
+ 'value' => '{"test": 1}',
+ 'type' => 64,
+ 'lazy' => false,
+ 'typeString' => 'array',
+ 'sensitive' => false
+ ],
+ $config->getDetails('typed', 'array')
+ );
+ }
+
+ public function testDeleteKey(): void {
+ $config = $this->generateAppConfig();
+ $config->deleteKey('anotherapp', 'key');
+ $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
+ }
+
+ public function testDeleteKeyCache(): void {
+ $config = $this->generateAppConfig();
+ $config->deleteKey('anotherapp', 'key');
+ $status = $config->statusCache();
+ $this->assertEqualsCanonicalizing(['enabled' => 'no', 'installed_version' => '3.2.1'], $status['fastCache']['anotherapp']);
+ }
+
+ public function testDeleteKeyDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->deleteKey('anotherapp', 'key');
+ $config->clearCache();
+ $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
+ }
+
+ public function testDeleteApp(): void {
+ $config = $this->generateAppConfig();
+ $config->deleteApp('anotherapp');
+ $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
+ $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
+ }
+
+ public function testDeleteAppCache(): void {
+ $config = $this->generateAppConfig();
+ $status = $config->statusCache();
+ $this->assertSame(true, isset($status['fastCache']['anotherapp']));
+ $config->deleteApp('anotherapp');
+ $status = $config->statusCache();
+ $this->assertSame(false, isset($status['fastCache']['anotherapp']));
+ }
+
+ public function testDeleteAppDatabase(): void {
+ $config = $this->generateAppConfig();
+ $config->deleteApp('anotherapp');
+ $config->clearCache();
+ $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
+ $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
+ }
+
+ public function testClearCache(): void {
+ $config = $this->generateAppConfig();
+ $config->setValueString('feed', 'string', '123454');
+ $config->clearCache();
+ $status = $config->statusCache();
+ $this->assertSame([], $status['fastCache']);
+ }
+
+ public function testSensitiveValuesAreEncrypted(): void {
+ $key = self::getUniqueID('secret');
+
+ $appConfig = $this->generateAppConfig();
+ $secret = md5((string)time());
+ $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
+
+ $this->assertConfigValueNotEquals('testapp', $key, $secret);
+
+ // Can get in same run
+ $actualSecret = $appConfig->getValueString('testapp', $key);
+ $this->assertEquals($secret, $actualSecret);
+
+ // Can get freshly decrypted from DB
+ $newAppConfig = $this->generateAppConfig();
+ $actualSecret = $newAppConfig->getValueString('testapp', $key);
+ $this->assertEquals($secret, $actualSecret);
+ }
+
+ public function testMigratingNonSensitiveValueToSensitiveWithSetValue(): void {
+ $key = self::getUniqueID('secret');
+ $appConfig = $this->generateAppConfig();
+ $secret = sha1((string)time());
+
+ // Unencrypted
+ $appConfig->setValueString('testapp', $key, $secret);
+ $this->assertConfigKey('testapp', $key, $secret);
+
+ // Can get freshly decrypted from DB
+ $newAppConfig = $this->generateAppConfig();
+ $actualSecret = $newAppConfig->getValueString('testapp', $key);
+ $this->assertEquals($secret, $actualSecret);
+
+ // Encrypting on change
+ $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
+ $this->assertConfigValueNotEquals('testapp', $key, $secret);
+
+ // Can get in same run
+ $actualSecret = $appConfig->getValueString('testapp', $key);
+ $this->assertEquals($secret, $actualSecret);
+
+ // Can get freshly decrypted from DB
+ $newAppConfig = $this->generateAppConfig();
+ $actualSecret = $newAppConfig->getValueString('testapp', $key);
+ $this->assertEquals($secret, $actualSecret);
+ }
+
+ public function testUpdateSensitiveValueToNonSensitiveWithUpdateSensitive(): void {
+ $key = self::getUniqueID('secret');
+ $appConfig = $this->generateAppConfig();
+ $secret = sha1((string)time());
+
+ // Encrypted
+ $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
+ $this->assertConfigValueNotEquals('testapp', $key, $secret);
+
+ // Migrate to non-sensitive / non-encrypted
+ $appConfig->updateSensitive('testapp', $key, false);
+ $this->assertConfigKey('testapp', $key, $secret);
+ }
+
+ public function testUpdateNonSensitiveValueToSensitiveWithUpdateSensitive(): void {
+ $key = self::getUniqueID('secret');
+ $appConfig = $this->generateAppConfig();
+ $secret = sha1((string)time());
+
+ // Unencrypted
+ $appConfig->setValueString('testapp', $key, $secret);
+ $this->assertConfigKey('testapp', $key, $secret);
+
+ // Migrate to sensitive / encrypted
+ $appConfig->updateSensitive('testapp', $key, true);
+ $this->assertConfigValueNotEquals('testapp', $key, $secret);
+ }
+
+ public function testSearchKeyNoLazyLoading(): void {
+ $appConfig = $this->generateAppConfig();
+ $appConfig->searchKeys('searchtest', 'search_');
+ $status = $appConfig->statusCache();
+ $this->assertFalse($status['lazyLoaded'], 'searchKeys() loaded lazy config');
+ }
+
+ public function testSearchKeyFast(): void {
+ $appConfig = $this->generateAppConfig();
+ $this->assertEquals(['search_key1', 'search_key2', 'search_key3'], $appConfig->searchKeys('searchtest', 'search_'));
+ }
+
+ public function testSearchKeyLazy(): void {
+ $appConfig = $this->generateAppConfig();
+ $this->assertEquals(['search_key5_lazy'], $appConfig->searchKeys('searchtest', 'search_', true));
+ }
+
+ protected function loadConfigValueFromDatabase(string $app, string $key): string|false {
+ $sql = $this->connection->getQueryBuilder();
+ $sql->select('configvalue')
+ ->from('appconfig')
+ ->where($sql->expr()->eq('appid', $sql->createParameter('appid')))
+ ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
+ ->setParameter('appid', $app)
+ ->setParameter('configkey', $key);
+ $query = $sql->executeQuery();
+ $actual = $query->fetchOne();
+ $query->closeCursor();
+
+ return $actual;
+ }
+
+ protected function assertConfigKey(string $app, string $key, string|false $expected): void {
+ $this->assertEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
+ }
+
+ protected function assertConfigValueNotEquals(string $app, string $key, string|false $expected): void {
+ $this->assertNotEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
+ }
+}
diff --git a/tests/lib/AppConfigTest.php b/tests/lib/AppConfigTest.php
index 0ae917a1d9fdd..f5e385a8405e3 100644
--- a/tests/lib/AppConfigTest.php
+++ b/tests/lib/AppConfigTest.php
@@ -2,1510 +2,203 @@
declare(strict_types=1);
/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace Test;
-use InvalidArgumentException;
use OC\AppConfig;
use OC\Config\ConfigManager;
use OC\Config\PresetManager;
-use OCP\Exceptions\AppConfigTypeConflictException;
-use OCP\Exceptions\AppConfigUnknownKeyException;
-use OCP\IAppConfig;
+use OC\Memcache\Factory as CacheFactory;
+use OCP\DB\IResult;
+use OCP\DB\QueryBuilder\IExpressionBuilder;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\ICache;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
-use OCP\Server;
+use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
/**
* Class AppConfigTest
*
- * @group DB
- *
* @package Test
*/
class AppConfigTest extends TestCase {
- protected IAppConfig $appConfig;
- protected IDBConnection $connection;
- private IConfig $config;
- private ConfigManager $configManager;
- private PresetManager $presetManager;
- private LoggerInterface $logger;
- private ICrypto $crypto;
-
- private array $originalConfig;
-
- /**
- * @var array>>
- * [appId => [configKey, configValue, valueType, lazy, sensitive]]
- */
- private static array $baseStruct
- = [
- 'testapp' => [
- 'enabled' => ['enabled', 'yes'],
- 'installed_version' => ['installed_version', '1.2.3'],
- 'depends_on' => ['depends_on', 'someapp'],
- 'deletethis' => ['deletethis', 'deletethis'],
- 'key' => ['key', 'value']
- ],
- 'searchtest' => [
- 'search_key1' => ['search_key1', 'key1', IAppConfig::VALUE_STRING],
- 'search_key2' => ['search_key2', 'key2', IAppConfig::VALUE_STRING],
- 'search_key3' => ['search_key3', 'key3', IAppConfig::VALUE_STRING],
- 'searchnot_key4' => ['searchnot_key4', 'key4', IAppConfig::VALUE_STRING],
- 'search_key5_lazy' => ['search_key5_lazy', 'key5', IAppConfig::VALUE_STRING, true],
- ],
- 'someapp' => [
- 'key' => ['key', 'value'],
- 'otherkey' => ['otherkey', 'othervalue']
- ],
- '123456' => [
- 'enabled' => ['enabled', 'yes'],
- 'key' => ['key', 'value']
- ],
- 'anotherapp' => [
- 'enabled' => ['enabled', 'no'],
- 'installed_version' => ['installed_version', '3.2.1'],
- 'key' => ['key', 'value']
- ],
- 'non-sensitive-app' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, false],
- 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, false],
- ],
- 'sensitive-app' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, true],
- 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, true],
- ],
- 'only-lazy' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true]
- ],
- 'typed' => [
- 'mixed' => ['mixed', 'mix', IAppConfig::VALUE_MIXED],
- 'string' => ['string', 'value', IAppConfig::VALUE_STRING],
- 'int' => ['int', '42', IAppConfig::VALUE_INT],
- 'float' => ['float', '3.14', IAppConfig::VALUE_FLOAT],
- 'bool' => ['bool', '1', IAppConfig::VALUE_BOOL],
- 'array' => ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
- ],
- 'prefix-app' => [
- 'key1' => ['key1', 'value'],
- 'prefix1' => ['prefix1', 'value'],
- 'prefix-2' => ['prefix-2', 'value'],
- 'key-2' => ['key-2', 'value'],
- ]
- ];
+ private IConfig&MockObject $config;
+ private IDBConnection&MockObject $connection;
+ private ConfigManager&MockObject $configManager;
+ private PresetManager&MockObject $presetManager;
+ private LoggerInterface&MockObject $logger;
+ private ICrypto&MockObject $crypto;
+ private CacheFactory&MockObject $cacheFactory;
+ private ICache&MockObject $localCache;
protected function setUp(): void {
parent::setUp();
- $this->connection = Server::get(IDBConnection::class);
- $this->config = Server::get(IConfig::class);
- $this->configManager = Server::get(ConfigManager::class);
- $this->presetManager = Server::get(PresetManager::class);
- $this->logger = Server::get(LoggerInterface::class);
- $this->crypto = Server::get(ICrypto::class);
-
- // storing current config and emptying the data table
- $sql = $this->connection->getQueryBuilder();
- $sql->select('*')
- ->from('appconfig');
- $result = $sql->executeQuery();
- $this->originalConfig = $result->fetchAll();
- $result->closeCursor();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('appconfig');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('appconfig')
- ->values(
- [
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'type' => $sql->createParameter('type'),
- 'lazy' => $sql->createParameter('lazy')
- ]
- );
-
- foreach (self::$baseStruct as $appId => $appData) {
- foreach ($appData as $key => $row) {
- $value = $row[1];
- $type = $row[2] ?? IAppConfig::VALUE_MIXED;
- if (($row[4] ?? false) === true) {
- $type |= IAppConfig::VALUE_SENSITIVE;
- $value = self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX') . $this->crypto->encrypt($value);
- self::$baseStruct[$appId][$key]['encrypted'] = $value;
- }
-
- $sql->setParameters(
- [
- 'appid' => $appId,
- 'configkey' => $row[0],
- 'configvalue' => $value,
- 'type' => $type,
- 'lazy' => (($row[3] ?? false) === true) ? 1 : 0
- ]
- )->executeStatement();
- }
- }
- }
-
- protected function tearDown(): void {
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('appconfig');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('appconfig')
- ->values(
- [
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'lazy' => $sql->createParameter('lazy'),
- 'type' => $sql->createParameter('type'),
- ]
- );
-
- foreach ($this->originalConfig as $key => $configs) {
- $sql->setParameter('appid', $configs['appid'])
- ->setParameter('configkey', $configs['configkey'])
- ->setParameter('configvalue', $configs['configvalue'])
- ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
- ->setParameter('type', $configs['type']);
- $sql->executeStatement();
+ $this->connection = $this->createMock(IDBConnection::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->configManager = $this->createMock(ConfigManager::class);
+ $this->presetManager = $this->createMock(PresetManager::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->crypto = $this->createMock(ICrypto::class);
+ $this->cacheFactory = $this->createMock(CacheFactory::class);
+ $this->localCache = $this->createMock(ICache::class);
+ }
+
+ protected function getAppConfig($cached = false): AppConfig {
+ $this->config->method('getSystemValueBool')
+ ->with('cache_app_config', $cached)
+ ->willReturn(true);
+ $this->cacheFactory->method('isLocalCacheAvailable')->willReturn($cached);
+ if ($cached) {
+ $this->cacheFactory->method('withServerVersionPrefix')->willReturnCallback(function (\Closure $closure): void {
+ $closure($this->cacheFactory);
+ });
+ $this->cacheFactory->method('createLocal')->willReturn($this->localCache);
}
- // $this->restoreService(AppConfig::class);
- parent::tearDown();
- }
-
- /**
- * @param bool $preLoading TRUE will preload the 'fast' cache, which is the normal behavior of usual
- * IAppConfig
- *
- * @return IAppConfig
- */
- private function generateAppConfig(bool $preLoading = true): IAppConfig {
- /** @var AppConfig $config */
- $config = new AppConfig(
+ return new AppConfig(
$this->connection,
$this->config,
$this->configManager,
$this->presetManager,
$this->logger,
$this->crypto,
+ $this->cacheFactory,
);
- $msg = ' generateAppConfig() failed to confirm cache status';
-
- // confirm cache status
- $status = $config->statusCache();
- $this->assertSame(false, $status['fastLoaded'], $msg);
- $this->assertSame(false, $status['lazyLoaded'], $msg);
- $this->assertSame([], $status['fastCache'], $msg);
- $this->assertSame([], $status['lazyCache'], $msg);
- if ($preLoading) {
- // simple way to initiate the load of non-lazy config values in cache
- $config->getValueString('core', 'preload', '');
-
- // confirm cache status
- $status = $config->statusCache();
- $this->assertSame(true, $status['fastLoaded'], $msg);
- $this->assertSame(false, $status['lazyLoaded'], $msg);
-
- $apps = array_values(array_diff(array_keys(self::$baseStruct), ['only-lazy']));
- $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache']), $msg);
- $this->assertSame([], array_keys($status['lazyCache']), $msg);
- }
-
- return $config;
- }
-
- public function testGetApps(): void {
- $config = $this->generateAppConfig(false);
-
- $this->assertEqualsCanonicalizing(array_keys(self::$baseStruct), $config->getApps());
- }
-
- public function testGetAppInstalledVersions(): void {
- $config = $this->generateAppConfig(false);
-
- $this->assertEquals(
- ['testapp' => '1.2.3', 'anotherapp' => '3.2.1'],
- $config->getAppInstalledVersions(false)
- );
- $this->assertEquals(
- ['testapp' => '1.2.3'],
- $config->getAppInstalledVersions(true)
- );
- }
-
- /**
- * returns list of app and their keys
- *
- * @return array ['appId' => ['key1', 'key2', ]]
- * @see testGetKeys
- */
- public static function providerGetAppKeys(): array {
- $appKeys = [];
- foreach (self::$baseStruct as $appId => $appData) {
- $keys = [];
- foreach ($appData as $row) {
- $keys[] = $row[0];
- }
- $appKeys[] = [(string)$appId, $keys];
- }
-
- return $appKeys;
- }
-
- /**
- * returns list of config keys
- *
- * @return array [appId, key, value, type, lazy, sensitive]
- * @see testIsSensitive
- * @see testIsLazy
- * @see testGetKeys
- */
- public static function providerGetKeys(): array {
- $appKeys = [];
- foreach (self::$baseStruct as $appId => $appData) {
- foreach ($appData as $row) {
- $appKeys[] = [
- (string)$appId, $row[0], $row[1], $row[2] ?? IAppConfig::VALUE_MIXED, $row[3] ?? false,
- $row[4] ?? false
- ];
- }
- }
-
- return $appKeys;
- }
-
- /**
- *
- * @param string $appId
- * @param array $expectedKeys
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppKeys')]
- public function testGetKeys(string $appId, array $expectedKeys): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing($expectedKeys, $config->getKeys($appId));
- }
-
- public function testGetKeysOnUnknownAppShouldReturnsEmptyArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing([], $config->getKeys('unknown-app'));
- }
-
- /**
- *
- * @param string $appId
- * @param string $configKey
- * @param string $value
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
- public function testHasKey(string $appId, string $configKey, string $value, int $type, bool $lazy): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(true, $config->hasKey($appId, $configKey, $lazy));
- }
-
- public function testHasKeyOnNonExistentKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(false, $config->hasKey(array_keys(self::$baseStruct)[0], 'inexistant-key'));
- }
-
- public function testHasKeyOnUnknownAppReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(false, $config->hasKey('inexistant-app', 'inexistant-key'));
- }
-
- public function testHasKeyOnMistypedAsLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->hasKey('non-sensitive-app', 'non-lazy-key', true));
- }
-
- public function testHasKeyOnMistypeAsNonLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->hasKey('non-sensitive-app', 'lazy-key', false));
- }
-
- public function testHasKeyOnMistypeAsNonLazyReturnsTrueWithLazyArgumentIsNull(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->hasKey('non-sensitive-app', 'lazy-key', null));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
- public function testIsSensitive(
- string $appId, string $configKey, string $configValue, int $type, bool $lazy, bool $sensitive,
- ): void {
- $config = $this->generateAppConfig();
- $this->assertEquals($sensitive, $config->isSensitive($appId, $configKey, $lazy));
- }
-
- public function testIsSensitiveOnNonExistentKeyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive(array_keys(self::$baseStruct)[0], 'inexistant-key');
- }
-
- public function testIsSensitiveOnUnknownAppThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('unknown-app', 'inexistant-key');
- }
-
- public function testIsSensitiveOnSensitiveMistypedAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->isSensitive('sensitive-app', 'non-lazy-key', true));
- }
-
- public function testIsSensitiveOnNonSensitiveMistypedAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->isSensitive('non-sensitive-app', 'non-lazy-key', true));
- }
-
- public function testIsSensitiveOnSensitiveMistypedAsNonLazyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('sensitive-app', 'lazy-key', false);
- }
-
- public function testIsSensitiveOnNonSensitiveMistypedAsNonLazyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('non-sensitive-app', 'lazy-key', false);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
- public function testIsLazy(string $appId, string $configKey, string $configValue, int $type, bool $lazy,
- ): void {
- $config = $this->generateAppConfig();
- $this->assertEquals($lazy, $config->isLazy($appId, $configKey));
- }
-
- public function testIsLazyOnNonExistentKeyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isLazy(array_keys(self::$baseStruct)[0], 'inexistant-key');
- }
-
- public function testIsLazyOnUnknownAppThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isLazy('unknown-app', 'inexistant-key');
- }
-
- public function testGetAllValues(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'array' => ['test' => 1],
- 'bool' => true,
- 'float' => 3.14,
- 'int' => 42,
- 'mixed' => 'mix',
- 'string' => 'value',
- ],
- $config->getAllValues('typed')
- );
- }
-
- public function testGetAllValuesWithEmptyApp(): void {
- $config = $this->generateAppConfig();
- $this->expectException(InvalidArgumentException::class);
- $config->getAllValues('');
- }
-
- /**
- *
- * @param string $appId
- * @param array $keys
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppKeys')]
- public function testGetAllValuesWithEmptyKey(string $appId, array $keys): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing($keys, array_keys($config->getAllValues($appId, '')));
- }
-
- public function testGetAllValuesWithPrefix(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['prefix1', 'prefix-2'], array_keys($config->getAllValues('prefix-app', 'prefix')));
- }
-
- public function testSearchValues(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['testapp' => 'yes', '123456' => 'yes', 'anotherapp' => 'no'], $config->searchValues('enabled'));
- }
-
- public function testGetValueString(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('value', $config->getValueString('typed', 'string', ''));
- }
-
- public function testGetValueStringOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('default-1', $config->getValueString('typed-1', 'string', 'default-1'));
- }
-
- public function testGetValueStringOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('default-2', $config->getValueString('typed', 'string-2', 'default-2'));
- }
-
- public function testGetValueStringOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueString('typed', 'int');
- }
-
- public function testGetNonLazyValueStringAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('value', $config->getValueString('non-sensitive-app', 'non-lazy-key', 'default', lazy: true));
- }
-
- public function testGetValueInt(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(42, $config->getValueInt('typed', 'int', 0));
- }
-
- public function testGetValueIntOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(1, $config->getValueInt('typed-1', 'int', 1));
- }
-
- public function testGetValueIntOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(2, $config->getValueInt('typed', 'int-2', 2));
- }
-
- public function testGetValueIntOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueInt('typed', 'float');
- }
-
- public function testGetValueFloat(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(3.14, $config->getValueFloat('typed', 'float', 0));
- }
-
- public function testGetValueFloatOnNonUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(1.11, $config->getValueFloat('typed-1', 'float', 1.11));
- }
-
- public function testGetValueFloatOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(2.22, $config->getValueFloat('typed', 'float-2', 2.22));
- }
-
- public function testGetValueFloatOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueFloat('typed', 'bool');
- }
-
- public function testGetValueBool(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->getValueBool('typed', 'bool'));
- }
-
- public function testGetValueBoolOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->getValueBool('typed-1', 'bool', false));
- }
-
- public function testGetValueBoolOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->getValueBool('typed', 'bool-2'));
- }
-
- public function testGetValueBoolOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueBool('typed', 'array');
- }
-
- public function testGetValueArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('typed', 'array', []));
- }
-
- public function testGetValueArrayOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame([1], $config->getValueArray('typed-1', 'array', [1]));
- }
-
- public function testGetValueArrayOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame([1, 2], $config->getValueArray('typed', 'array-2', [1, 2]));
- }
-
- public function testGetValueArrayOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueArray('typed', 'string');
- }
-
-
- /**
- * @return array
- * @see testGetValueType
- *
- * @see testGetValueMixed
- */
- public static function providerGetValueMixed(): array {
- return [
- // key, value, type
- ['mixed', 'mix', IAppConfig::VALUE_MIXED],
- ['string', 'value', IAppConfig::VALUE_STRING],
- ['int', '42', IAppConfig::VALUE_INT],
- ['float', '3.14', IAppConfig::VALUE_FLOAT],
- ['bool', '1', IAppConfig::VALUE_BOOL],
- ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
- ];
- }
-
- /**
- *
- * @param string $key
- * @param string $value
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
- public function testGetValueMixed(string $key, string $value): void {
- $config = $this->generateAppConfig();
- $this->assertSame($value, $config->getValueMixed('typed', $key));
- }
-
- /**
- *
- * @param string $key
- * @param string $value
- * @param int $type
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
- public function testGetValueType(string $key, string $value, int $type): void {
- $config = $this->generateAppConfig();
- $this->assertSame($type, $config->getValueType('typed', $key));
- }
-
- public function testGetValueTypeOnUnknownApp(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->getValueType('typed-1', 'string');
- }
-
- public function testGetValueTypeOnNonExistentKey(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->getValueType('typed', 'string-2');
- }
-
- public function testSetValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $status = $config->statusCache();
- $this->assertSame('value-1', $status['fastCache']['feed']['string']);
- }
-
- public function testSetValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueStringIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame(true, $config->setValueString('feed', 'string', 'value-2'));
- }
-
- public function testSetValueStringIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame(false, $config->setValueString('feed', 'string', 'value-1'));
- }
-
- public function testSetValueStringIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->setValueString('feed', 'string', 'value-2');
- $status = $config->statusCache();
- $this->assertSame('value-2', $status['fastCache']['feed']['string']);
- }
-
- public function testSetValueStringIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->setValueString('feed', 'string', 'value-2');
- $config->clearCache();
- $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $status = $config->statusCache();
- $this->assertSame('42', $status['fastCache']['feed']['int']);
- }
-
- public function testSetValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueIntIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(true, $config->setValueInt('feed', 'int', 17));
- }
-
- public function testSetValueIntIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(false, $config->setValueInt('feed', 'int', 42));
- }
-
- public function testSetValueIntIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 17);
- $status = $config->statusCache();
- $this->assertSame('17', $status['fastCache']['feed']['int']);
- }
-
- public function testSetValueIntIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 17);
- $config->clearCache();
- $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $status = $config->statusCache();
- $this->assertSame('3.14', $status['fastCache']['feed']['float']);
- }
-
- public function testSetValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueFloatIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(true, $config->setValueFloat('feed', 'float', 1.23));
- }
-
- public function testSetValueFloatIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(false, $config->setValueFloat('feed', 'float', 3.14));
- }
-
- public function testSetValueFloatIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 1.23);
- $status = $config->statusCache();
- $this->assertSame('1.23', $status['fastCache']['feed']['float']);
- }
-
- public function testSetValueFloatIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 1.23);
- $config->clearCache();
- $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueBool(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $status = $config->statusCache();
- $this->assertSame('1', $status['fastCache']['feed']['bool']);
- }
-
- public function testSetValueBoolDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->clearCache();
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(true, $config->setValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(false, $config->setValueBool('feed', 'bool', true));
- }
-
- public function testSetValueBoolIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->setValueBool('feed', 'bool', false);
- $status = $config->statusCache();
- $this->assertSame('0', $status['fastCache']['feed']['bool']);
- }
-
- public function testSetValueBoolIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->setValueBool('feed', 'bool', false);
- $config->clearCache();
- $this->assertSame(false, $config->getValueBool('feed', 'bool', true));
- }
-
-
- public function testSetValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $status = $config->statusCache();
- $this->assertSame('{"test":1}', $status['fastCache']['feed']['array']);
- }
-
- public function testSetValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->clearCache();
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetValueArrayIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(true, $config->setValueArray('feed', 'array', ['test' => 2]));
- }
-
- public function testSetValueArrayIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(false, $config->setValueArray('feed', 'array', ['test' => 1]));
- }
-
- public function testSetValueArrayIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $status = $config->statusCache();
- $this->assertSame('{"test":2}', $status['fastCache']['feed']['array']);
- }
-
- public function testSetValueArrayIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $config->clearCache();
- $this->assertSame(['test' => 2], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetLazyValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetLazyValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $status = $config->statusCache();
- $this->assertSame('value-1', $status['lazyCache']['feed']['string']);
- }
-
- public function testSetLazyValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetLazyValueStringAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $config->setValueString('feed', 'string', 'value-1', false);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetNonLazyValueStringAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', false);
- $config->setValueString('feed', 'string', 'value-1', true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetSensitiveValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetSensitiveValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['string']);
- }
-
- public function testSetSensitiveValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetNonSensitiveValueStringAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: false);
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'string'));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetSensitiveValueStringAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->setValueString('feed', 'string', 'value-2', sensitive: false);
- $this->assertSame(true, $config->isSensitive('feed', 'string'));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetSensitiveValueStringAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->setValueString('feed', 'string', 'value-2', sensitive: false);
- $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetLazyValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetLazyValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $status = $config->statusCache();
- $this->assertSame('42', $status['lazyCache']['feed']['int']);
- }
-
- public function testSetLazyValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetLazyValueIntAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $config->setValueInt('feed', 'int', 42, false);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetNonLazyValueIntAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, false);
- $config->setValueInt('feed', 'int', 42, true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetSensitiveValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetSensitiveValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['int']);
- }
-
- public function testSetSensitiveValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetNonSensitiveValueIntAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'int'));
- }
-
- public function testSetSensitiveValueIntAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->setValueInt('feed', 'int', 17);
- $this->assertSame(true, $config->isSensitive('feed', 'int'));
- }
-
- public function testSetSensitiveValueIntAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->setValueInt('feed', 'int', 17);
- $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetLazyValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetLazyValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $status = $config->statusCache();
- $this->assertSame('3.14', $status['lazyCache']['feed']['float']);
- }
-
- public function testSetLazyValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetLazyValueFloatAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $config->setValueFloat('feed', 'float', 3.14, false);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetNonLazyValueFloatAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, false);
- $config->setValueFloat('feed', 'float', 3.14, true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetSensitiveValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetSensitiveValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['float']);
- }
-
- public function testSetSensitiveValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetNonSensitiveValueFloatAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'float'));
- }
-
- public function testSetSensitiveValueFloatAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->setValueFloat('feed', 'float', 1.23);
- $this->assertSame(true, $config->isSensitive('feed', 'float'));
- }
-
- public function testSetSensitiveValueFloatAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->setValueFloat('feed', 'float', 1.23);
- $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetLazyValueBool(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueBoolCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $status = $config->statusCache();
- $this->assertSame('1', $status['lazyCache']['feed']['bool']);
- }
-
- public function testSetLazyValueBoolDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $config->clearCache();
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueBoolAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $config->setValueBool('feed', 'bool', true, false);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetNonLazyValueBoolAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, false);
- $config->setValueBool('feed', 'bool', true, true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
- public function testSetLazyValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $status = $config->statusCache();
- $this->assertSame('{"test":1}', $status['lazyCache']['feed']['array']);
- }
-
- public function testSetLazyValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $config->clearCache();
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
- public function testSetLazyValueArrayAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $config->setValueArray('feed', 'array', ['test' => 1], false);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetNonLazyValueArrayAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], false);
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
-
- public function testSetSensitiveValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetSensitiveValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['array']);
- }
-
- public function testSetSensitiveValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->clearCache();
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetNonSensitiveValueArrayAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'array'));
- }
-
- public function testSetSensitiveValueArrayAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $this->assertSame(true, $config->isSensitive('feed', 'array'));
- }
-
- public function testSetSensitiveValueArrayAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $this->assertEqualsCanonicalizing(['test' => 2], $config->getValueArray('feed', 'array', []));
- }
-
- public function testUpdateNotSensitiveToSensitive(): void {
- $config = $this->generateAppConfig();
- $config->updateSensitive('non-sensitive-app', 'lazy-key', true);
- $this->assertSame(true, $config->isSensitive('non-sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateSensitiveToNotSensitive(): void {
- $config = $this->generateAppConfig();
- $config->updateSensitive('sensitive-app', 'lazy-key', false);
- $this->assertSame(false, $config->isSensitive('sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateSensitiveToSensitiveReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateNotSensitiveToNotSensitiveReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'lazy-key', false));
- }
-
- public function testUpdateSensitiveOnUnknownKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'unknown-key', true));
- }
-
- public function testUpdateNotLazyToLazy(): void {
- $config = $this->generateAppConfig();
- $config->updateLazy('non-sensitive-app', 'non-lazy-key', true);
- $this->assertSame(true, $config->isLazy('non-sensitive-app', 'non-lazy-key'));
- }
-
- public function testUpdateLazyToNotLazy(): void {
- $config = $this->generateAppConfig();
- $config->updateLazy('non-sensitive-app', 'lazy-key', false);
- $this->assertSame(false, $config->isLazy('non-sensitive-app', 'lazy-key'));
- }
-
- public function testUpdateLazyToLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateNotLazyToNotLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'non-lazy-key', false));
- }
-
- public function testUpdateLazyOnUnknownKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'unknown-key', true));
- }
-
- public function testGetDetails(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'non-sensitive-app',
- 'key' => 'lazy-key',
- 'value' => 'value',
- 'type' => 4,
- 'lazy' => true,
- 'typeString' => 'string',
- 'sensitive' => false,
- ],
- $config->getDetails('non-sensitive-app', 'lazy-key')
- );
- }
-
- public function testGetDetailsSensitive(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'sensitive-app',
- 'key' => 'lazy-key',
- 'value' => 'value',
- 'type' => 4,
- 'lazy' => true,
- 'typeString' => 'string',
- 'sensitive' => true,
- ],
- $config->getDetails('sensitive-app', 'lazy-key')
- );
- }
-
- public function testGetDetailsInt(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'int',
- 'value' => '42',
- 'type' => 8,
- 'lazy' => false,
- 'typeString' => 'integer',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'int')
- );
- }
-
- public function testGetDetailsFloat(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'float',
- 'value' => '3.14',
- 'type' => 16,
- 'lazy' => false,
- 'typeString' => 'float',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'float')
- );
- }
-
- public function testGetDetailsBool(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'bool',
- 'value' => '1',
- 'type' => 32,
- 'lazy' => false,
- 'typeString' => 'boolean',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'bool')
- );
- }
-
- public function testGetDetailsArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'array',
- 'value' => '{"test": 1}',
- 'type' => 64,
- 'lazy' => false,
- 'typeString' => 'array',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'array')
- );
- }
-
- public function testDeleteKey(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- }
-
- public function testDeleteKeyCache(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $status = $config->statusCache();
- $this->assertEqualsCanonicalizing(['enabled' => 'no', 'installed_version' => '3.2.1'], $status['fastCache']['anotherapp']);
- }
-
- public function testDeleteKeyDatabase(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $config->clearCache();
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- }
-
- public function testDeleteApp(): void {
- $config = $this->generateAppConfig();
- $config->deleteApp('anotherapp');
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
- }
-
- public function testDeleteAppCache(): void {
- $config = $this->generateAppConfig();
- $status = $config->statusCache();
- $this->assertSame(true, isset($status['fastCache']['anotherapp']));
- $config->deleteApp('anotherapp');
- $status = $config->statusCache();
- $this->assertSame(false, isset($status['fastCache']['anotherapp']));
- }
-
- public function testDeleteAppDatabase(): void {
- $config = $this->generateAppConfig();
- $config->deleteApp('anotherapp');
- $config->clearCache();
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
- }
-
- public function testClearCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', '123454');
- $config->clearCache();
- $status = $config->statusCache();
- $this->assertSame([], $status['fastCache']);
- }
-
- public function testSensitiveValuesAreEncrypted(): void {
- $key = self::getUniqueID('secret');
-
- $appConfig = $this->generateAppConfig();
- $secret = md5((string)time());
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
-
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Can get in same run
- $actualSecret = $appConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
- }
-
- public function testMigratingNonSensitiveValueToSensitiveWithSetValue(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Unencrypted
- $appConfig->setValueString('testapp', $key, $secret);
- $this->assertConfigKey('testapp', $key, $secret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Encrypting on change
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Can get in same run
- $actualSecret = $appConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
- }
-
- public function testUpdateSensitiveValueToNonSensitiveWithUpdateSensitive(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Encrypted
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Migrate to non-sensitive / non-encrypted
- $appConfig->updateSensitive('testapp', $key, false);
- $this->assertConfigKey('testapp', $key, $secret);
- }
-
- public function testUpdateNonSensitiveValueToSensitiveWithUpdateSensitive(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Unencrypted
- $appConfig->setValueString('testapp', $key, $secret);
- $this->assertConfigKey('testapp', $key, $secret);
-
- // Migrate to sensitive / encrypted
- $appConfig->updateSensitive('testapp', $key, true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
- }
-
- public function testSearchKeyNoLazyLoading(): void {
- $appConfig = $this->generateAppConfig();
- $appConfig->searchKeys('searchtest', 'search_');
- $status = $appConfig->statusCache();
- $this->assertFalse($status['lazyLoaded'], 'searchKeys() loaded lazy config');
- }
-
- public function testSearchKeyFast(): void {
- $appConfig = $this->generateAppConfig();
- $this->assertEquals(['search_key1', 'search_key2', 'search_key3'], $appConfig->searchKeys('searchtest', 'search_'));
- }
-
- public function testSearchKeyLazy(): void {
- $appConfig = $this->generateAppConfig();
- $this->assertEquals(['search_key5_lazy'], $appConfig->searchKeys('searchtest', 'search_', true));
- }
-
- protected function loadConfigValueFromDatabase(string $app, string $key): string|false {
- $sql = $this->connection->getQueryBuilder();
- $sql->select('configvalue')
- ->from('appconfig')
- ->where($sql->expr()->eq('appid', $sql->createParameter('appid')))
- ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
- ->setParameter('appid', $app)
- ->setParameter('configkey', $key);
- $query = $sql->executeQuery();
- $actual = $query->fetchOne();
- $query->closeCursor();
-
- return $actual;
- }
-
- protected function assertConfigKey(string $app, string $key, string|false $expected): void {
- $this->assertEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
}
- protected function assertConfigValueNotEquals(string $app, string $key, string|false $expected): void {
- $this->assertNotEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
+ public function testCachedRead(): void {
+ $this->localCache->expects(self::once())
+ ->method('get')
+ ->with('OC\\AppConfig')
+ ->willReturn([
+ 'fastCache' => [
+ 'appid' => [
+ 'some-key' => 'some-value',
+ 'other-key' => 'other value'
+ ],
+ ],
+ 'valueTypes' => [
+ 'appid' => [
+ 'some-key' => AppConfig::VALUE_STRING,
+ 'other-key' => AppConfig::VALUE_STRING,
+ ],
+ ],
+ ]);
+
+ $this->connection->expects(self::never())->method('getQueryBuilder');
+ $config = $this->getAppConfig(true);
+
+
+ $this->assertSame('some-value', $config->getValueString('appid', 'some-key'));
+ $this->assertSame('other value', $config->getValueString('appid', 'other-key'));
+ $this->assertSame(AppConfig::VALUE_STRING, $config->getValueType('appid', 'some-key', false));
+ }
+
+ public function testCachedLazyRead(): void {
+ $this->localCache->expects(self::once())
+ ->method('get')
+ ->with('OC\\AppConfig')
+ ->willReturn([
+ 'fastCache' => [
+ 'appid' => [
+ 'fast-key' => 'fast value',
+ ],
+ ],
+ 'lazyCache' => [
+ 'appid' => [
+ 'lazy-key' => 'lazy value',
+ ],
+ ],
+ 'valueTypes' => [
+ 'appid' => [
+ 'some-key' => AppConfig::VALUE_STRING,
+ 'lazy-key' => AppConfig::VALUE_STRING,
+ ],
+ ],
+ ]);
+
+ $this->connection->expects(self::never())->method('getQueryBuilder');
+ $config = $this->getAppConfig(true);
+
+
+ $this->assertSame('fast value', $config->getValueString('appid', 'fast-key'));
+ $this->assertSame('lazy value', $config->getValueString('appid', 'lazy-key', '', true));
+ }
+
+ public function testOnlyFastKeyCached(): void {
+ $this->localCache->expects(self::atLeastOnce())
+ ->method('get')
+ ->with('OC\\AppConfig')
+ ->willReturn([
+ 'fastCache' => [
+ 'appid' => [
+ 'fast-key' => 'fast value',
+ ],
+ ],
+ 'valueTypes' => [
+ 'appid' => [
+ 'fast-key' => AppConfig::VALUE_STRING,
+ ],
+ ],
+ ]);
+
+ $result = $this->createMock(IResult::class);
+ $result->method('fetchAll')->willReturn([
+ ['lazy' => 1, 'appid' => 'appid', 'configkey' => 'lazy-key', 'configvalue' => 'lazy value'],
+ ]);
+ $expression = $this->createMock(IExpressionBuilder::class);
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $queryBuilder->method('from')->willReturn($queryBuilder);
+ $queryBuilder->method('expr')->willReturn($expression);
+ $queryBuilder->method('executeQuery')->willReturn($result);
+
+ $this->connection->expects(self::once())->method('getQueryBuilder')->willReturn($queryBuilder);
+ $config = $this->getAppConfig(true);
+
+
+ $this->assertSame('fast value', $config->getValueString('appid', 'fast-key'));
+ $this->assertSame('lazy value', $config->getValueString('appid', 'lazy-key', '', true));
+ }
+
+ public function testWritesAreCached(): void {
+ $this->localCache->expects(self::atLeastOnce())
+ ->method('get')
+ ->with('OC\\AppConfig')
+ ->willReturn([
+ 'fastCache' => [
+ 'appid' => [
+ 'first-key' => 'first value',
+ ],
+ ],
+ 'valueTypes' => [
+ 'appid' => [
+ 'first-key' => AppConfig::VALUE_STRING,
+ ],
+ ],
+ ]);
+
+ $expression = $this->createMock(IExpressionBuilder::class);
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $queryBuilder->expects(self::once())
+ ->method('update')
+ ->with('appconfig', null)
+ ->willReturn($queryBuilder);
+ $queryBuilder->method('set')->willReturn($queryBuilder);
+ $queryBuilder->method('where')->willReturn($queryBuilder);
+ $queryBuilder->method('andWhere')->willReturn($queryBuilder);
+ $queryBuilder->method('expr')->willReturn($expression);
+ $this->connection->expects(self::once())->method('getQueryBuilder')->willReturn($queryBuilder);
+
+ $config = $this->getAppConfig(true);
+
+ $this->assertSame('first value', $config->getValueString('appid', 'first-key'));
+ $config->setValueString('appid', 'first-key', 'new value');
+ $this->assertSame('new value', $config->getValueString('appid', 'first-key'));
}
}
diff --git a/tests/lib/Memcache/FactoryTest.php b/tests/lib/Memcache/FactoryTest.php
index e16e079349f35..31500f31b65d5 100644
--- a/tests/lib/Memcache/FactoryTest.php
+++ b/tests/lib/Memcache/FactoryTest.php
@@ -12,6 +12,7 @@
use OC\Memcache\NullCache;
use OCP\HintException;
use OCP\Profiler\IProfiler;
+use OCP\ServerVersion;
use Psr\Log\LoggerInterface;
class Test_Factory_Available_Cache1 extends NullCache {
@@ -111,7 +112,8 @@ public function testCacheAvailability($localCache, $distributedCache, $lockingCa
$expectedLocalCache, $expectedDistributedCache, $expectedLockingCache): void {
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- $factory = new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache, $lockingCache);
+ $serverVersion = $this->createMock(ServerVersion::class);
+ $factory = new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache, $lockingCache);
$this->assertTrue(is_a($factory->createLocal(), $expectedLocalCache));
$this->assertTrue(is_a($factory->createDistributed(), $expectedDistributedCache));
$this->assertTrue(is_a($factory->createLocking(), $expectedLockingCache));
@@ -123,13 +125,15 @@ public function testCacheNotAvailableException($localCache, $distributedCache):
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache);
+ $serverVersion = $this->createMock(ServerVersion::class);
+ new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache);
}
public function testCreateInMemory(): void {
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- $factory = new Factory(fn () => 'abc', $logger, $profiler, null, null, null);
+ $serverVersion = $this->createMock(ServerVersion::class);
+ $factory = new Factory($logger, $profiler, $serverVersion, null, null, null);
$cache = $factory->createInMemory();
$cache->set('test', 48);