From b222aa0c48020de168291cd406e92c5f89ced396 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 27 Apr 2025 01:36:30 +0200 Subject: [PATCH] Add a bunch of tests for StatsCaching --- composer.json | 1 + composer.lock | 67 +++++- src/Module/StatsCaching.php | 9 +- tests/CacheLockTestCase.php | 26 +++ tests/LockTestCase.php | 17 +- tests/src/Core/Cache/ArrayCacheTest.php | 8 + .../Core/Cache/ProfilerCacheDecoratorTest.php | 56 +++++ tests/src/Core/Lock/APCuCacheLockTest.php | 21 +- tests/src/Core/Lock/ArrayCacheLockTest.php | 25 ++- .../src/Core/Lock/DatabaseLockDriverTest.php | 3 +- tests/src/Core/Lock/MemcacheCacheLockTest.php | 28 ++- .../src/Core/Lock/MemcachedCacheLockTest.php | 27 ++- tests/src/Core/Lock/RedisCacheLockTest.php | 24 +- tests/src/Core/Lock/SemaphoreLockTest.php | 3 +- tests/src/Module/StatsCachingTest.php | 205 ++++++++++++++++++ 15 files changed, 477 insertions(+), 43 deletions(-) create mode 100644 tests/CacheLockTestCase.php create mode 100644 tests/src/Core/Cache/ProfilerCacheDecoratorTest.php create mode 100644 tests/src/Module/StatsCachingTest.php diff --git a/composer.json b/composer.json index a2c9eea3c9..655f987df9 100644 --- a/composer.json +++ b/composer.json @@ -153,6 +153,7 @@ "dms/phpunit-arraysubset-asserts": "^0.3.1", "mikey179/vfsstream": "^1.6", "mockery/mockery": "^1.3", + "php-mock/php-mock-mockery": "^1.5", "php-mock/php-mock-phpunit": "^2.10", "phpmd/phpmd": "^2.15", "phpstan/phpstan": "^2.0", diff --git a/composer.lock b/composer.lock index e12cc6533c..bae30155dd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b77bf714197f04022a5feb001bf07852", + "content-hash": "32af97f73ec49df2a6cfe98f11bc1d60", "packages": [ { "name": "asika/simple-console", @@ -5441,6 +5441,71 @@ ], "time": "2024-02-10T21:37:25+00:00" }, + { + "name": "php-mock/php-mock-mockery", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/php-mock/php-mock-mockery.git", + "reference": "291994acdc26daf1e3c659cfbe58b01eeb180b7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-mock/php-mock-mockery/zipball/291994acdc26daf1e3c659cfbe58b01eeb180b7f", + "reference": "291994acdc26daf1e3c659cfbe58b01eeb180b7f", + "shasum": "" + }, + "require": { + "mockery/mockery": "^1", + "php": ">=5.6", + "php-mock/php-mock-integration": "^2.2.1 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4|^5|^8" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpmock\\mockery\\": "classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "WTFPL" + ], + "authors": [ + { + "name": "Markus Malkusch", + "email": "markus@malkusch.de", + "homepage": "http://markus.malkusch.de", + "role": "Developer" + } + ], + "description": "Mock built-in PHP functions (e.g. time()) with Mockery. This package relies on PHP's namespace fallback policy. No further extension is needed.", + "homepage": "https://github.com/php-mock/php-mock-mockery", + "keywords": [ + "BDD", + "TDD", + "function", + "mock", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "issues": "https://github.com/php-mock/php-mock-mockery/issues", + "source": "https://github.com/php-mock/php-mock-mockery/tree/1.5.0" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2025-03-08T19:46:20+00:00" + }, { "name": "php-mock/php-mock-phpunit", "version": "2.10.0", diff --git a/src/Module/StatsCaching.php b/src/Module/StatsCaching.php index 6247eca9c1..668d26e021 100644 --- a/src/Module/StatsCaching.php +++ b/src/Module/StatsCaching.php @@ -15,6 +15,7 @@ use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\L10n; use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Core\Lock\Type\CacheLock; +use Friendica\Network\HTTPException\NotFoundException; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; use Friendica\Network\HTTPException; @@ -41,9 +42,12 @@ class StatsCaching extends BaseModule private function isAllowed(array $request): bool { - return empty(!$request['key']) && $request['key'] == $this->config->get('system', 'stats_key'); + return !empty($request['key']) && $request['key'] == $this->config->get('system', 'stats_key'); } + /** + * @throws NotFoundException In case the rquest isn't allowed + */ protected function content(array $request = []): string { if (!$this->isAllowed($request)) { @@ -98,6 +102,7 @@ class StatsCaching extends BaseModule ]; } - $this->jsonExit($data); + $this->response->setType('json', 'application/json; charset=utf-8'); + $this->response->addContent(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); } } diff --git a/tests/CacheLockTestCase.php b/tests/CacheLockTestCase.php new file mode 100644 index 0000000000..1599391ece --- /dev/null +++ b/tests/CacheLockTestCase.php @@ -0,0 +1,26 @@ +getCache()->getStats()), array_keys($this->instance->getCacheStats())); + } +} diff --git a/tests/LockTestCase.php b/tests/LockTestCase.php index 9ce86497b7..1b80e575ef 100644 --- a/tests/LockTestCase.php +++ b/tests/LockTestCase.php @@ -7,22 +7,21 @@ namespace Friendica\Test; +use Friendica\Core\Cache\Capability\ICanCache; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Lock\Capability\ICanLock; -use Friendica\Test\MockedTestCase; +use Friendica\Core\Lock\Type\CacheLock; abstract class LockTestCase extends MockedTestCase { /** - * @var int Start time of the mock (used for time operations) + * Start time of the mock (used for time operations) */ - protected $startTime = 1417011228; + protected int $startTime = 1417011228; + protected ICanLock $instance; - /** - * @var ICanLock - */ - protected $instance; + abstract protected function getInstance(): ICanLock; - abstract protected function getInstance(); protected function setUp(): void { @@ -205,4 +204,6 @@ abstract class LockTestCase extends MockedTestCase self::assertFalse($this->instance->isLocked('wrongLock')); self::assertFalse($this->instance->release('wrongLock')); } + + } diff --git a/tests/src/Core/Cache/ArrayCacheTest.php b/tests/src/Core/Cache/ArrayCacheTest.php index 967cb07bce..50226b0907 100644 --- a/tests/src/Core/Cache/ArrayCacheTest.php +++ b/tests/src/Core/Cache/ArrayCacheTest.php @@ -33,4 +33,12 @@ class ArrayCacheTest extends MemoryCacheTestCase self::markTestSkipped("Array Cache doesn't support TTL"); return true; } + + /** + * @small + */ + public function testGetStats() + { + self::assertEmpty($this->cache->getStats()); + } } diff --git a/tests/src/Core/Cache/ProfilerCacheDecoratorTest.php b/tests/src/Core/Cache/ProfilerCacheDecoratorTest.php new file mode 100644 index 0000000000..3f44bcd0bf --- /dev/null +++ b/tests/src/Core/Cache/ProfilerCacheDecoratorTest.php @@ -0,0 +1,56 @@ +shouldReceive('get')->with('system', 'profiler')->once()->andReturn(false); + $config->shouldReceive('get')->with('rendertime', 'callstack')->once()->andReturn(false); + + $this->cache = new ProfilerCacheDecorator(new ArrayCache('localhost'), new Profiler($config)); + return $this->cache; + } + + protected function tearDown(): void + { + $this->cache->clear(false); + parent::tearDown(); + } + + /** + * @doesNotPerformAssertions + */ + public function testTTL() + { + // Array Cache doesn't support TTL + self::markTestSkipped("Array Cache doesn't support TTL"); + return true; + } + + /** + * @small + */ + public function testGetStats() + { + self::assertEmpty($this->cache->getStats()); + } + + public function testGetName() + { + self::assertStringEndsWith(' (with profiler)', $this->instance->getName()); + } +} diff --git a/tests/src/Core/Lock/APCuCacheLockTest.php b/tests/src/Core/Lock/APCuCacheLockTest.php index 3ee0d09661..42a1fc72d5 100644 --- a/tests/src/Core/Lock/APCuCacheLockTest.php +++ b/tests/src/Core/Lock/APCuCacheLockTest.php @@ -7,26 +7,39 @@ namespace Friendica\Test\src\Core\Lock; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Cache\Type\APCuCache; +use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Core\Lock\Type\CacheLock; -use Friendica\Test\LockTestCase; +use Friendica\Test\CacheLockTestCase; /** * @group APCU */ -class APCuCacheLockTest extends LockTestCase +class APCuCacheLockTest extends CacheLockTestCase { + private APCuCache $cache; + private ICanLock $lock; + protected function setUp(): void { if (!APCuCache::isAvailable()) { static::markTestSkipped('APCu is not available'); } + $this->cache = new APCuCache('localhost'); + $this->lock = new CacheLock($this->cache); + parent::setUp(); } - protected function getInstance() + protected function getInstance(): CacheLock { - return new CacheLock(new APCuCache('localhost')); + return $this->lock; + } + + protected function getCache(): ICanCacheInMemory + { + return $this->cache; } } diff --git a/tests/src/Core/Lock/ArrayCacheLockTest.php b/tests/src/Core/Lock/ArrayCacheLockTest.php index 19ac7925c6..2aac8e8293 100644 --- a/tests/src/Core/Lock/ArrayCacheLockTest.php +++ b/tests/src/Core/Lock/ArrayCacheLockTest.php @@ -7,15 +7,32 @@ namespace Friendica\Test\src\Core\Lock; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Cache\Type\ArrayCache; use Friendica\Core\Lock\Type\CacheLock; -use Friendica\Test\LockTestCase; +use Friendica\Test\CacheLockTestCase; -class ArrayCacheLockTest extends LockTestCase +class ArrayCacheLockTest extends CacheLockTestCase { - protected function getInstance() + private CacheLock $lock; + private ArrayCache $cache; + + protected function setUp(): void { - return new CacheLock(new ArrayCache('localhost')); + $this->cache = new ArrayCache('localhost'); + $this->lock = new CacheLock($this->cache); + + parent::setUp(); + } + + protected function getInstance(): CacheLock + { + return $this->lock; + } + + protected function getCache(): ICanCacheInMemory + { + return $this->cache; } /** diff --git a/tests/src/Core/Lock/DatabaseLockDriverTest.php b/tests/src/Core/Lock/DatabaseLockDriverTest.php index ebc2b0090f..fbfe61762e 100644 --- a/tests/src/Core/Lock/DatabaseLockDriverTest.php +++ b/tests/src/Core/Lock/DatabaseLockDriverTest.php @@ -7,6 +7,7 @@ namespace Friendica\Test\src\Core\Lock; +use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Core\Lock\Type\DatabaseLock; use Friendica\Test\LockTestCase; use Friendica\Test\Util\CreateDatabaseTrait; @@ -26,7 +27,7 @@ class DatabaseLockDriverTest extends LockTestCase parent::setUp(); } - protected function getInstance() + protected function getInstance(): ICanLock { return new DatabaseLock($this->getDbInstance(), $this->pid); } diff --git a/tests/src/Core/Lock/MemcacheCacheLockTest.php b/tests/src/Core/Lock/MemcacheCacheLockTest.php index 2bb0595cff..c1dec663f1 100644 --- a/tests/src/Core/Lock/MemcacheCacheLockTest.php +++ b/tests/src/Core/Lock/MemcacheCacheLockTest.php @@ -8,19 +8,23 @@ namespace Friendica\Test\src\Core\Lock; use Exception; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Cache\Type\MemcacheCache; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Lock\Type\CacheLock; -use Friendica\Test\LockTestCase; +use Friendica\Test\CacheLockTestCase; use Mockery; /** * @requires extension Memcache * @group MEMCACHE */ -class MemcacheCacheLockTest extends LockTestCase +class MemcacheCacheLockTest extends CacheLockTestCase { - protected function getInstance() + private CacheLock $lock; + private MemcacheCache $cache; + + protected function setUp(): void { $configMock = Mockery::mock(IManageConfigValues::class); @@ -36,16 +40,24 @@ class MemcacheCacheLockTest extends LockTestCase ->with('system', 'memcache_port') ->andReturn($port); - $lock = null; - try { - $cache = new MemcacheCache($host, $configMock); - $lock = new CacheLock($cache); + $this->cache = new MemcacheCache($host, $configMock); + $this->lock = new CacheLock($this->cache); } catch (Exception $e) { static::markTestSkipped('Memcache is not available'); } - return $lock; + parent::setUp(); + } + + protected function getInstance(): CacheLock + { + return $this->lock; + } + + protected function getCache(): ICanCacheInMemory + { + return $this->cache; } /** diff --git a/tests/src/Core/Lock/MemcachedCacheLockTest.php b/tests/src/Core/Lock/MemcachedCacheLockTest.php index fb38ec3312..773e664108 100644 --- a/tests/src/Core/Lock/MemcachedCacheLockTest.php +++ b/tests/src/Core/Lock/MemcachedCacheLockTest.php @@ -8,9 +8,11 @@ namespace Friendica\Test\src\Core\Lock; use Exception; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Cache\Type\MemcachedCache; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Lock\Type\CacheLock; +use Friendica\Test\CacheLockTestCase; use Friendica\Test\LockTestCase; use Mockery; use Psr\Log\NullLogger; @@ -19,9 +21,12 @@ use Psr\Log\NullLogger; * @requires extension memcached * @group MEMCACHED */ -class MemcachedCacheLockTest extends LockTestCase +class MemcachedCacheLockTest extends CacheLockTestCase { - protected function getInstance() + private MemcachedCache $cache; + private CacheLock $lock; + + protected function setUp(): void { $configMock = Mockery::mock(IManageConfigValues::class); @@ -35,16 +40,24 @@ class MemcachedCacheLockTest extends LockTestCase $logger = new NullLogger(); - $lock = null; - try { - $cache = new MemcachedCache($host, $configMock, $logger); - $lock = new CacheLock($cache); + $this->cache = new MemcachedCache($host, $configMock, $logger); + $this->lock = new CacheLock($this->cache); } catch (Exception $e) { static::markTestSkipped('Memcached is not available'); } - return $lock; + parent::setUp(); + } + + protected function getInstance(): CacheLock + { + return $this->lock; + } + + protected function getCache(): ICanCacheInMemory + { + return $this->cache; } /** diff --git a/tests/src/Core/Lock/RedisCacheLockTest.php b/tests/src/Core/Lock/RedisCacheLockTest.php index d0237682c3..3cb4fba436 100644 --- a/tests/src/Core/Lock/RedisCacheLockTest.php +++ b/tests/src/Core/Lock/RedisCacheLockTest.php @@ -8,9 +8,11 @@ namespace Friendica\Test\src\Core\Lock; use Exception; +use Friendica\Core\Cache\Capability\ICanCacheInMemory; use Friendica\Core\Cache\Type\RedisCache; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Lock\Type\CacheLock; +use Friendica\Test\CacheLockTestCase; use Friendica\Test\LockTestCase; use Mockery; @@ -18,9 +20,9 @@ use Mockery; * @requires extension redis * @group REDIS */ -class RedisCacheLockTest extends LockTestCase +class RedisCacheLockTest extends CacheLockTestCase { - protected function getInstance() + protected function setUp(): void { $configMock = Mockery::mock(IManageConfigValues::class); @@ -45,15 +47,23 @@ class RedisCacheLockTest extends LockTestCase ->with('system', 'redis_password') ->andReturn(null); - $lock = null; - try { - $cache = new RedisCache($host, $configMock); - $lock = new CacheLock($cache); + $this->cache = new RedisCache($host, $configMock); + $this->lock = new CacheLock($this->cache); } catch (Exception $e) { static::markTestSkipped('Redis is not available. Error: ' . $e->getMessage()); } - return $lock; + parent::setUp(); + } + + protected function getInstance(): CAcheLock + { + return $this->lock; + } + + protected function getCache(): ICanCacheInMemory + { + return $this->cache; } } diff --git a/tests/src/Core/Lock/SemaphoreLockTest.php b/tests/src/Core/Lock/SemaphoreLockTest.php index 06b4e02f46..1ddb2dade7 100644 --- a/tests/src/Core/Lock/SemaphoreLockTest.php +++ b/tests/src/Core/Lock/SemaphoreLockTest.php @@ -12,6 +12,7 @@ use Friendica\App; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Config\Model\ReadOnlyFileConfig; use Friendica\Core\Config\ValueObject\Cache; +use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Core\Lock\Type\SemaphoreLock; use Friendica\Core\System; use Friendica\DI; @@ -40,7 +41,7 @@ class SemaphoreLockTest extends LockTestCase parent::setUp(); } - protected function getInstance() + protected function getInstance(): ICanLock { return new SemaphoreLock(); } diff --git a/tests/src/Module/StatsCachingTest.php b/tests/src/Module/StatsCachingTest.php new file mode 100644 index 0000000000..08d4b8fdb6 --- /dev/null +++ b/tests/src/Module/StatsCachingTest.php @@ -0,0 +1,205 @@ +httpExceptionMock = \Mockery::mock(HTTPException::class); + $this->config = \Mockery::mock(IManageConfigValues::class); + $this->cache = new ArrayCache('localhost'); + $this->lock = new CacheLock($this->cache); + } + + public function testStatsCachingNotAllowed() + { + $this->httpExceptionMock->shouldReceive('content')->andReturn('failed')->once(); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock); + + self::assertEquals('404', $response->getStatusCode()); + self::assertEquals('Page not found', $response->getReasonPhrase()); + self::assertEquals('failed', $response->getBody()); + } + + public function testStatsCachingWitMinimumCache() + { + $request = [ + 'key' => '12345', + ]; + $this->config->shouldReceive('get')->with('system', 'stats_key')->twice()->andReturn('12345'); + PHPMockery::mock("Friendica\\Module", "function_exists")->with('opcache_get_status')->once()->andReturn(false); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock, $request); + + self::assertJson($response->getBody()); + self::assertEquals(['Content-type' => ['application/json; charset=utf-8'], ICanCreateResponses::X_HEADER => ['json']], $response->getHeaders()); + + $json = json_decode($response->getBody(), true); + + self::assertEquals([ + 'type' => 'array', + 'stats' => [], + ], $json['cache']); + self::assertEquals([ + 'type' => 'array', + 'stats' => [], + ], $json['lock']); + } + + public function testStatsCachingWithDatabase() + { + $request = [ + 'key' => '12345', + ]; + $this->config->shouldReceive('get')->with('system', 'stats_key')->twice()->andReturn('12345'); + + $this->cache = new DatabaseCache('localhost', DI::dba()); + $this->lock = new DatabaseLock(DI::dba()); + PHPMockery::mock("Friendica\\Module", "function_exists")->with('opcache_get_status')->once()->andReturn(false); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock, $request); + + self::assertJson($response->getBody()); + self::assertEquals(['Content-type' => ['application/json; charset=utf-8'], ICanCreateResponses::X_HEADER => ['json']], $response->getHeaders()); + + $json = json_decode($response->getBody(), true); + + self::assertEquals(['enabled' => false], $json['opcache']); + self::assertEquals(['type' => 'database'], $json['cache']); + self::assertEquals(['type' => 'database'], $json['lock']); + } + + public function testStatsCachingWithCache() + { + $request = [ + 'key' => '12345', + ]; + $this->config->shouldReceive('get')->with('system', 'stats_key')->twice()->andReturn('12345'); + + $this->cache = new DatabaseCache('localhost', DI::dba()); + $this->lock = new DatabaseLock(DI::dba()); + PHPMockery::mock("Friendica\\Module", "function_exists")->with('opcache_get_status')->once()->andReturn(false); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock, $request); + + self::assertJson($response->getBody()); + self::assertEquals(['Content-type' => ['application/json; charset=utf-8'], ICanCreateResponses::X_HEADER => ['json']], $response->getHeaders()); + + $json = json_decode($response->getBody(), true); + + self::assertEquals(['enabled' => false], $json['opcache']); + self::assertEquals(['type' => 'database'], $json['cache']); + self::assertEquals(['type' => 'database'], $json['lock']); + } + + public function testStatsCachingWithOpcacheAndNull() + { + $request = [ + 'key' => '12345', + ]; + $this->config->shouldReceive('get')->with('system', 'stats_key')->twice()->andReturn('12345'); + + $this->cache = new DatabaseCache('localhost', DI::dba()); + $this->lock = new DatabaseLock(DI::dba()); + PHPMockery::mock("Friendica\\Module", "function_exists")->with('opcache_get_status')->once()->andReturn(true); + PHPMockery::mock("Friendica\\Module", "opcache_get_status")->with(false)->once()->andReturn(false); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock, $request); + + self::assertJson($response->getBody()); + self::assertEquals(['Content-type' => ['application/json; charset=utf-8'], ICanCreateResponses::X_HEADER => ['json']], $response->getHeaders()); + + $json = json_decode($response->getBody(), true); + + print_r($json); + + self::assertEquals([ + 'enabled' => false, + 'hit_rate' => null, + 'used_memory' => null, + 'free_memory' => null, + 'num_cached_scripts' => null, + ], $json['opcache']); + self::assertEquals(['type' => 'database'], $json['cache']); + self::assertEquals(['type' => 'database'], $json['lock']); + } + + public function testStatsCachingWithOpcacheAndValues() + { + $request = [ + 'key' => '12345', + ]; + $this->config->shouldReceive('get')->with('system', 'stats_key')->twice()->andReturn('12345'); + + $this->cache = new DatabaseCache('localhost', DI::dba()); + $this->lock = new DatabaseLock(DI::dba()); + PHPMockery::mock("Friendica\\Module", "function_exists")->with('opcache_get_status')->once()->andReturn(true); + PHPMockery::mock("Friendica\\Module", "opcache_get_status")->with(false)->once()->andReturn([ + 'opcache_enabled' => true, + 'opcache_statistics' => [ + 'opcache_hit_rate' => 1, + 'num_cached_scripts' => 2, + ], + 'memory_usage' => [ + 'used_memory' => 3, + 'free_memory' => 4, + ] + ]); + + $response = (new StatsCaching(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), [], $this->config, $this->cache, $this->lock, [])) + ->run($this->httpExceptionMock, $request); + + self::assertJson($response->getBody()); + self::assertEquals(['Content-type' => ['application/json; charset=utf-8'], ICanCreateResponses::X_HEADER => ['json']], $response->getHeaders()); + + $json = json_decode($response->getBody(), true); + + self::assertEquals([ + 'enabled' => true, + 'hit_rate' => 1, + 'used_memory' => 3, + 'free_memory' => 4, + 'num_cached_scripts' => 2, + ], $json['opcache']); + self::assertEquals(['type' => 'database'], $json['cache']); + self::assertEquals(['type' => 'database'], $json['lock']); + } +}