diff --git a/includes/class-tmdo-cache-layer.php b/includes/class-tmdo-cache-layer.php index db21ce9..0db82cc 100644 --- a/includes/class-tmdo-cache-layer.php +++ b/includes/class-tmdo-cache-layer.php @@ -40,7 +40,7 @@ class TMDO_Cache_Layer { } $cold_keys = TMDO_Schema_Registry::instance()->get_cold_meta_keys( $post_type ); - if ( empty( $cold_keys ) ) { + if ( empty( $cold_keys ) || ! self::cold_active( $post_type ) ) { return; } @@ -108,7 +108,7 @@ class TMDO_Cache_Layer { */ public static function warm_post( int $post_id, string $post_type ): void { $cold_keys = TMDO_Schema_Registry::instance()->get_cold_meta_keys( $post_type ); - if ( empty( $cold_keys ) ) { + if ( empty( $cold_keys ) || ! self::cold_active( $post_type ) ) { return; } @@ -120,6 +120,19 @@ class TMDO_Cache_Layer { TMDO_Zone_Cold::get_blob( $post_id, $post_type ); } + /** + * Whether the cold zone holds data for this post type: its module writes + * to the cold table and the table exists. Registered cold fields alone do + * not mean either (zero side effect while the module is idle). + * + * @param string $post_type Post type. + * @return bool + */ + private static function cold_active( string $post_type ): bool { + return TMDO_Feature_Flags::is_write_active( TMDO_Zone_Router::module_name( 'cold', $post_type ) ) + && TMDO_Schema_Manager::table_exists( TMDO_Zone_Cold::table( $post_type ) ); + } + /** * Flush all cold cache for a post type. * diff --git a/tests/unit/CacheLayerTest.php b/tests/unit/CacheLayerTest.php index e76518a..e15a618 100644 --- a/tests/unit/CacheLayerTest.php +++ b/tests/unit/CacheLayerTest.php @@ -11,17 +11,35 @@ class CacheLayerTest extends TestCase { /** Rows returned by get_results() for the bulk-fetch query. */ public static array $db_rows = []; + /** Whether SHOW TABLES reports the cold table. */ + public static bool $table_exists = true; + + /** Queries that read the cold table (get_var / get_results on it). */ + public static int $cold_queries = 0; + protected function setUp(): void { self::$db_rows = []; + self::$table_exists = true; + self::$cold_queries = 0; $GLOBALS['_wp_cache'] = []; // Reset Schema Registry singleton. $ref = new ReflectionClass( WPDO_Schema_Registry::class ); $ref->getProperty( 'instance' )->setValue( null, null ); + // The cold module is write-active unless a test says otherwise. + $this->set_cold_module( 'dual_write' ); + WPDO_Schema_Manager::flush_table_exists_cache(); + $this->setup_wpdb_mock(); } + private function set_cold_module( string $state ): void { + $GLOBALS['_wp_options']['wpdo_features'] = array( 'cold_hp_listing' => $state ); + $prop = new ReflectionProperty( WPDO_Feature_Flags::class, 'cache' ); + $prop->setValue( null, null ); + } + private function setup_wpdb_mock(): void { global $wpdb; @@ -36,7 +54,15 @@ class CacheLayerTest extends TestCase { }, $sql ); } - public function get_var( string $sql ): ?string { return null; } + public function get_var( string $sql ): ?string { + if ( preg_match( "/^SHOW TABLES LIKE '(.+)'$/", $sql, $m ) ) { + return CacheLayerTest::$table_exists ? stripslashes( $m[1] ) : null; + } + if ( str_contains( $sql, 'wpdo_cold_' ) ) { + ++CacheLayerTest::$cold_queries; + } + return null; + } public function get_row( string $sql, $output = OBJECT ) { return null; } public function insert( string $table, array $data, $format = null ): int|false { return 1; } public function update( string $table, array $data, array $where, $f = null, $wf = null ): int|false { return 1; } @@ -44,11 +70,45 @@ class CacheLayerTest extends TestCase { public function query( string $sql ): int|bool { return 1; } public function get_results( string $sql, $output = OBJECT ): array { + if ( str_contains( $sql, 'wpdo_cold_' ) ) { + ++CacheLayerTest::$cold_queries; + } return CacheLayerTest::$db_rows; } }; } + // ── Zero side effect while the cold module is not active ───────────────── + + public function test_prefetch_does_not_query_while_module_idle(): void { + $this->register_cold_field(); + $this->set_cold_module( 'idle' ); + + WPDO_Cache_Layer::prefetch( [ 1, 2 ], 'hp_listing' ); + + $this->assertSame( 0, self::$cold_queries ); + $this->assertEmpty( $GLOBALS['_wp_cache'] ); + } + + public function test_warm_post_does_not_query_while_module_idle(): void { + $this->register_cold_field(); + $this->set_cold_module( 'idle' ); + + WPDO_Cache_Layer::warm_post( 20, 'hp_listing' ); + + $this->assertSame( 0, self::$cold_queries ); + } + + public function test_no_query_when_cold_table_is_missing(): void { + $this->register_cold_field(); + self::$table_exists = false; + + WPDO_Cache_Layer::prefetch( [ 1 ], 'hp_listing' ); + WPDO_Cache_Layer::warm_post( 1, 'hp_listing' ); + + $this->assertSame( 0, self::$cold_queries ); + } + // ── Helper: register cold field ────────────────────────────────────────── private function register_cold_field( string $post_type = 'hp_listing', string $meta_key = 'hp_description' ): void {