assertFalse(WordPressMock::get_transient($key)); // Test cache set $this->assertTrue(WordPressMock::set_transient($key, $value, 3600)); // Test cache hit $this->assertEquals($value, WordPressMock::get_transient($key)); } /** * Test cache with complex data structures * * @return void * @since 1.0.0 */ public function testComplexDataCaching(): void { $key = self::CACHE_PREFIX . 'complex_data'; $complexData = [ 'doctors' => [ ['id' => 1, 'name' => 'Dr. Smith'], ['id' => 2, 'name' => 'Dr. Johnson'] ], 'services' => [ ['id' => 1, 'name' => 'Consultation'], ['id' => 2, 'name' => 'Follow-up'] ], 'metadata' => [ 'total_count' => 4, 'last_updated' => time() ] ]; // Set complex data WordPressMock::set_transient($key, $complexData, 3600); // Retrieve and verify $retrieved = WordPressMock::get_transient($key); $this->assertEquals($complexData, $retrieved); $this->assertIsArray($retrieved); $this->assertArrayHasKey('doctors', $retrieved); $this->assertArrayHasKey('services', $retrieved); $this->assertCount(2, $retrieved['doctors']); } /** * Test cache invalidation * * @return void * @since 1.0.0 */ public function testCacheInvalidation(): void { $key = self::CACHE_PREFIX . 'invalidation_test'; $value = 'cached_value'; // Set cache WordPressMock::set_transient($key, $value, 3600); $this->assertEquals($value, WordPressMock::get_transient($key)); // Invalidate cache $this->assertTrue(WordPressMock::delete_transient($key)); // Verify cache is cleared $this->assertFalse(WordPressMock::get_transient($key)); } /** * Test multiple cache keys with pattern-based invalidation * * @return void * @since 1.0.0 */ public function testPatternBasedInvalidation(): void { $keys = [ self::CACHE_PREFIX . 'doctors_list', self::CACHE_PREFIX . 'doctors_blocked', self::CACHE_PREFIX . 'services_list', self::CACHE_PREFIX . 'restrictions_active' ]; // Set multiple cache entries foreach ($keys as $key) { WordPressMock::set_transient($key, "value_for_{$key}", 3600); } // Verify all are cached foreach ($keys as $key) { $this->assertNotFalse(WordPressMock::get_transient($key)); } // Clear doctor-related caches foreach ($keys as $key) { if (strpos($key, 'doctors') !== false) { WordPressMock::delete_transient($key); } } // Verify selective invalidation $this->assertFalse(WordPressMock::get_transient(self::CACHE_PREFIX . 'doctors_list')); $this->assertFalse(WordPressMock::get_transient(self::CACHE_PREFIX . 'doctors_blocked')); $this->assertNotFalse(WordPressMock::get_transient(self::CACHE_PREFIX . 'services_list')); $this->assertNotFalse(WordPressMock::get_transient(self::CACHE_PREFIX . 'restrictions_active')); } /** * Test cache key generation * * @return void * @since 1.0.0 */ public function testCacheKeyGeneration(): void { // Test simple key $key1 = self::CACHE_PREFIX . 'simple'; $this->assertStringStartsWith(self::CACHE_PREFIX, $key1); // Test parametrized key $doctorId = 123; $serviceId = 456; $key2 = self::CACHE_PREFIX . "doctor_{$doctorId}_service_{$serviceId}"; $this->assertEquals(self::CACHE_PREFIX . 'doctor_123_service_456', $key2); // Test hashed key for long parameters $longParams = str_repeat('abcd', 100); // 400 characters $hashedKey = self::CACHE_PREFIX . md5($longParams); $this->assertEquals(40, strlen($hashedKey) - strlen(self::CACHE_PREFIX)); // MD5 is 32 chars + prefix } /** * Test cache statistics and monitoring * * @return void * @since 1.0.0 */ public function testCacheStatistics(): void { $statsKey = self::CACHE_PREFIX . 'stats'; // Initialize stats $stats = [ 'hits' => 0, 'misses' => 0, 'sets' => 0, 'deletes' => 0 ]; WordPressMock::set_transient($statsKey, $stats, 3600); // Simulate cache operations and update stats $testKey = self::CACHE_PREFIX . 'test'; // Cache miss if (WordPressMock::get_transient($testKey) === false) { $stats['misses']++; } // Cache set WordPressMock::set_transient($testKey, 'value', 3600); $stats['sets']++; // Cache hit if (WordPressMock::get_transient($testKey) !== false) { $stats['hits']++; } // Cache delete WordPressMock::delete_transient($testKey); $stats['deletes']++; // Update stats WordPressMock::set_transient($statsKey, $stats, 3600); // Verify stats $finalStats = WordPressMock::get_transient($statsKey); $this->assertEquals(1, $finalStats['hits']); $this->assertEquals(1, $finalStats['misses']); $this->assertEquals(1, $finalStats['sets']); $this->assertEquals(1, $finalStats['deletes']); } /** * Test cache with expiration times * * @return void * @since 1.0.0 */ public function testCacheExpiration(): void { $key = self::CACHE_PREFIX . 'expiration_test'; $value = 'expires_quickly'; // Set cache with short expiration (1 second) WordPressMock::set_transient($key, $value, 1); $this->assertEquals($value, WordPressMock::get_transient($key)); // In real WordPress, this would expire, but our mock doesn't simulate time // So we'll test the interface exists $this->assertTrue(method_exists(WordPressMock::class, 'set_transient')); // Test different expiration periods $periods = [ 'short' => 300, // 5 minutes 'medium' => 3600, // 1 hour 'long' => 86400 // 1 day ]; foreach ($periods as $name => $seconds) { $key = self::CACHE_PREFIX . "expiration_{$name}"; WordPressMock::set_transient($key, "value_{$name}", $seconds); $this->assertEquals("value_{$name}", WordPressMock::get_transient($key)); } } /** * Test cache namespace isolation * * @return void * @since 1.0.0 */ public function testCacheNamespaceIsolation(): void { $sameKey = 'shared_key'; // Different namespaces $namespace1 = self::CACHE_PREFIX . 'namespace1_' . $sameKey; $namespace2 = self::CACHE_PREFIX . 'namespace2_' . $sameKey; // Set different values in different namespaces WordPressMock::set_transient($namespace1, 'value1', 3600); WordPressMock::set_transient($namespace2, 'value2', 3600); // Verify isolation $this->assertEquals('value1', WordPressMock::get_transient($namespace1)); $this->assertEquals('value2', WordPressMock::get_transient($namespace2)); $this->assertNotEquals( WordPressMock::get_transient($namespace1), WordPressMock::get_transient($namespace2) ); } /** * Test cache warming strategies * * @return void * @since 1.0.0 */ public function testCacheWarmingStrategies(): void { // Simulate cache warming for common data $commonKeys = [ self::CACHE_PREFIX . 'active_doctors', self::CACHE_PREFIX . 'available_services', self::CACHE_PREFIX . 'current_restrictions' ]; // Pre-populate cache (cache warming) $warmingData = [ 'active_doctors' => [1, 2, 3, 5, 8], 'available_services' => [1, 2, 4, 6], 'current_restrictions' => ['doctor_3', 'service_5'] ]; foreach ($warmingData as $type => $data) { $key = self::CACHE_PREFIX . $type; WordPressMock::set_transient($key, $data, 3600); } // Verify all warmed data is available foreach ($warmingData as $type => $expectedData) { $key = self::CACHE_PREFIX . $type; $cachedData = WordPressMock::get_transient($key); $this->assertEquals($expectedData, $cachedData); } } /** * Test cache hierarchical invalidation * * @return void * @since 1.0.0 */ public function testHierarchicalInvalidation(): void { // Set up hierarchical cache structure $parentKey = self::CACHE_PREFIX . 'all_restrictions'; $childKeys = [ self::CACHE_PREFIX . 'restrictions_doctor_1', self::CACHE_PREFIX . 'restrictions_doctor_2', self::CACHE_PREFIX . 'restrictions_service_1' ]; // Set parent and children WordPressMock::set_transient($parentKey, 'parent_data', 3600); foreach ($childKeys as $key) { WordPressMock::set_transient($key, "child_data_{$key}", 3600); } // Verify all cached $this->assertNotFalse(WordPressMock::get_transient($parentKey)); foreach ($childKeys as $key) { $this->assertNotFalse(WordPressMock::get_transient($key)); } // Invalidate parent (should cascade to children in real implementation) WordPressMock::delete_transient($parentKey); // In a real cache hierarchy, children would be invalidated too // For testing, we'll simulate this foreach ($childKeys as $key) { WordPressMock::delete_transient($key); } // Verify all invalidated $this->assertFalse(WordPressMock::get_transient($parentKey)); foreach ($childKeys as $key) { $this->assertFalse(WordPressMock::get_transient($key)); } } /** * Test cache size and memory management * * @return void * @since 1.0.0 */ public function testCacheSizeManagement(): void { // Test different data sizes $sizes = [ 'small' => str_repeat('a', 100), // 100 bytes 'medium' => str_repeat('b', 1000), // 1KB 'large' => str_repeat('c', 10000) // 10KB ]; foreach ($sizes as $size => $data) { $key = self::CACHE_PREFIX . "size_{$size}"; WordPressMock::set_transient($key, $data, 3600); $retrieved = WordPressMock::get_transient($key); $this->assertEquals($data, $retrieved); $this->assertEquals(strlen($data), strlen($retrieved)); } // Test cache size monitoring $totalCacheSize = 0; foreach ($sizes as $size => $data) { $totalCacheSize += strlen($data); } $this->assertGreaterThan(0, $totalCacheSize); $this->assertEquals(11100, $totalCacheSize); // 100 + 1000 + 10000 } }