Environment variables to clean up after each test. */ private array $envVarsToClean = []; protected function setUp(): void { parent::setUp(); StorageFactory::reset(); } protected function tearDown(): void { StorageFactory::reset(); foreach ($this->envVarsToClean as $var) { putenv($var); } $this->envVarsToClean = []; parent::tearDown(); } // ── Adapter Constants ──────────────────────────────────────────── #[Test] public function adapter_constants_are_defined(): void { $this->assertSame('inmemory', StorageFactory::ADAPTER_INMEMORY); $this->assertSame('redis', StorageFactory::ADAPTER_REDIS); $this->assertSame('apcu', StorageFactory::ADAPTER_APCU); } // ── get_available_adapters() ───────────────────────────────────── #[Test] public function get_available_adapters_returns_all_three(): void { $adapters = StorageFactory::get_available_adapters(); $this->assertArrayHasKey(StorageFactory::ADAPTER_INMEMORY, $adapters); $this->assertArrayHasKey(StorageFactory::ADAPTER_REDIS, $adapters); $this->assertArrayHasKey(StorageFactory::ADAPTER_APCU, $adapters); $this->assertCount(3, $adapters); } // ── is_adapter_available() ─────────────────────────────────────── #[Test] public function inmemory_adapter_is_always_available(): void { $this->assertTrue(StorageFactory::is_adapter_available(StorageFactory::ADAPTER_INMEMORY)); } #[Test] public function unknown_adapter_is_not_available(): void { $this->assertFalse(StorageFactory::is_adapter_available('unknown')); } #[Test] public function redis_availability_depends_on_extension(): void { $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(false); $this->assertFalse(StorageFactory::is_adapter_available(StorageFactory::ADAPTER_REDIS)); } #[Test] public function apcu_availability_requires_extension_and_enabled(): void { $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(true); $apcuEnabled = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'apcu_enabled' ); $apcuEnabled->expects($this->any())->willReturn(false); $this->assertFalse(StorageFactory::is_adapter_available(StorageFactory::ADAPTER_APCU)); } // ── get_configured_adapter() ───────────────────────────────────── #[Test] public function default_configured_adapter_is_inmemory(): void { $this->assertSame('inmemory', StorageFactory::get_configured_adapter()); } #[Test] public function configured_adapter_reads_from_env_var(): void { $this->setEnv('WP_PROMETHEUS_STORAGE_ADAPTER', 'redis'); $this->assertSame('redis', StorageFactory::get_configured_adapter()); } #[Test] public function configured_adapter_reads_from_option(): void { GlobalFunctionState::$options['wp_prometheus_storage_adapter'] = 'apcu'; $this->assertSame('apcu', StorageFactory::get_configured_adapter()); } #[Test] public function env_var_takes_precedence_over_option(): void { $this->setEnv('WP_PROMETHEUS_STORAGE_ADAPTER', 'redis'); GlobalFunctionState::$options['wp_prometheus_storage_adapter'] = 'apcu'; $this->assertSame('redis', StorageFactory::get_configured_adapter()); } #[Test] public function configured_adapter_lowercases_env_value(): void { $this->setEnv('WP_PROMETHEUS_STORAGE_ADAPTER', 'REDIS'); $this->assertSame('redis', StorageFactory::get_configured_adapter()); } // ── get_redis_config() ─────────────────────────────────────────── #[Test] public function get_redis_config_returns_defaults(): void { $config = StorageFactory::get_redis_config(); $this->assertSame('127.0.0.1', $config['host']); $this->assertSame(6379, $config['port']); $this->assertSame('', $config['password']); $this->assertSame(0, $config['database']); $this->assertSame('WORDPRESS_PROMETHEUS_', $config['prefix']); } #[Test] public function get_redis_config_reads_from_env_vars(): void { $this->setEnv('WP_PROMETHEUS_REDIS_HOST', '10.0.0.1'); $this->setEnv('WP_PROMETHEUS_REDIS_PORT', '6380'); $this->setEnv('WP_PROMETHEUS_REDIS_PASSWORD', 's3cret'); $this->setEnv('WP_PROMETHEUS_REDIS_DATABASE', '2'); $this->setEnv('WP_PROMETHEUS_REDIS_PREFIX', 'MY_PREFIX_'); $config = StorageFactory::get_redis_config(); $this->assertSame('10.0.0.1', $config['host']); $this->assertSame(6380, $config['port']); $this->assertSame('s3cret', $config['password']); $this->assertSame(2, $config['database']); $this->assertSame('MY_PREFIX_', $config['prefix']); } #[Test] public function get_redis_config_reads_from_option(): void { GlobalFunctionState::$options['wp_prometheus_redis_config'] = [ 'host' => '192.168.1.1', 'port' => 6381, 'password' => 'optpass', 'database' => 3, 'prefix' => 'OPT_', ]; $config = StorageFactory::get_redis_config(); $this->assertSame('192.168.1.1', $config['host']); $this->assertSame(6381, $config['port']); $this->assertSame('optpass', $config['password']); $this->assertSame(3, $config['database']); $this->assertSame('OPT_', $config['prefix']); } #[Test] public function get_redis_config_env_takes_precedence(): void { $this->setEnv('WP_PROMETHEUS_REDIS_HOST', 'env-host'); GlobalFunctionState::$options['wp_prometheus_redis_config'] = [ 'host' => 'option-host', ]; $config = StorageFactory::get_redis_config(); $this->assertSame('env-host', $config['host']); } // ── get_apcu_prefix() ──────────────────────────────────────────── #[Test] public function get_apcu_prefix_returns_default(): void { $this->assertSame('wp_prom', StorageFactory::get_apcu_prefix()); } #[Test] public function get_apcu_prefix_reads_from_env(): void { $this->setEnv('WP_PROMETHEUS_APCU_PREFIX', 'custom_prefix'); $this->assertSame('custom_prefix', StorageFactory::get_apcu_prefix()); } #[Test] public function get_apcu_prefix_reads_from_option(): void { GlobalFunctionState::$options['wp_prometheus_apcu_prefix'] = 'opt_prefix'; $this->assertSame('opt_prefix', StorageFactory::get_apcu_prefix()); } // ── save_config() ──────────────────────────────────────────────── #[Test] public function save_config_stores_adapter(): void { StorageFactory::save_config(['adapter' => 'redis']); $this->assertSame( 'redis', GlobalFunctionState::$options['wp_prometheus_storage_adapter'] ); } #[Test] public function save_config_stores_redis_config(): void { StorageFactory::save_config([ 'redis' => [ 'host' => '10.0.0.5', 'port' => 6390, 'password' => 'pass123', 'database' => 1, 'prefix' => 'TEST_', ], ]); $saved = GlobalFunctionState::$options['wp_prometheus_redis_config']; $this->assertSame('10.0.0.5', $saved['host']); $this->assertSame(6390, $saved['port']); $this->assertSame('pass123', $saved['password']); $this->assertSame(1, $saved['database']); // sanitize_key lowercases the prefix $this->assertSame('test_', $saved['prefix']); } #[Test] public function save_config_stores_apcu_prefix(): void { StorageFactory::save_config(['apcu_prefix' => 'my_apcu']); $this->assertSame( 'my_apcu', GlobalFunctionState::$options['wp_prometheus_apcu_prefix'] ); } #[Test] public function save_config_resets_singleton(): void { // Get an adapter (creates singleton). $adapter1 = StorageFactory::get_adapter(); // Save new config (should reset singleton). StorageFactory::save_config(['adapter' => 'inmemory']); // Get adapter again — should be a new instance. $adapter2 = StorageFactory::get_adapter(); $this->assertNotSame($adapter1, $adapter2); } // ── test_connection() ──────────────────────────────────────────── #[Test] public function test_connection_inmemory_always_succeeds(): void { $result = StorageFactory::test_connection(StorageFactory::ADAPTER_INMEMORY); $this->assertTrue($result['success']); $this->assertNotEmpty($result['message']); } #[Test] public function test_connection_unknown_adapter_fails(): void { $result = StorageFactory::test_connection('unknown'); $this->assertFalse($result['success']); $this->assertStringContainsString('Unknown', $result['message']); } #[Test] public function test_connection_redis_fails_without_extension(): void { $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(false); $result = StorageFactory::test_connection(StorageFactory::ADAPTER_REDIS); $this->assertFalse($result['success']); $this->assertStringContainsString('not installed', $result['message']); } #[Test] public function test_connection_apcu_fails_without_extension(): void { $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(false); $result = StorageFactory::test_connection(StorageFactory::ADAPTER_APCU); $this->assertFalse($result['success']); $this->assertStringContainsString('not installed', $result['message']); } // ── get_adapter() / reset() / singleton ────────────────────────── #[Test] public function get_adapter_returns_inmemory_by_default(): void { $adapter = StorageFactory::get_adapter(); $this->assertInstanceOf(InMemory::class, $adapter); } #[Test] public function get_adapter_returns_singleton(): void { $adapter1 = StorageFactory::get_adapter(); $adapter2 = StorageFactory::get_adapter(); $this->assertSame($adapter1, $adapter2); } #[Test] public function reset_clears_singleton_and_error(): void { StorageFactory::get_adapter(); StorageFactory::reset(); // After reset, get_last_error should be empty. $this->assertEmpty(StorageFactory::get_last_error()); // Getting adapter again creates a new instance. $adapter = StorageFactory::get_adapter(); $this->assertInstanceOf(InMemory::class, $adapter); } #[Test] public function get_adapter_falls_back_to_inmemory_when_redis_unavailable(): void { $this->setEnv('WP_PROMETHEUS_STORAGE_ADAPTER', 'redis'); $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(false); $adapter = StorageFactory::get_adapter(); $this->assertInstanceOf(InMemory::class, $adapter); $this->assertNotEmpty(StorageFactory::get_last_error()); } #[Test] public function get_adapter_falls_back_to_inmemory_when_apcu_unavailable(): void { $this->setEnv('WP_PROMETHEUS_STORAGE_ADAPTER', 'apcu'); $extensionLoaded = $this->getFunctionMock( 'Magdev\\WpPrometheus\\Metrics', 'extension_loaded' ); $extensionLoaded->expects($this->any())->willReturn(false); $adapter = StorageFactory::get_adapter(); $this->assertInstanceOf(InMemory::class, $adapter); $this->assertNotEmpty(StorageFactory::get_last_error()); } // ── Helpers ────────────────────────────────────────────────────── private function setEnv(string $name, string $value): void { putenv("$name=$value"); $this->envVarsToClean[] = $name; } }