query( 'CREATE TABLE IF NOT EXISTS `' . self::TERMMETA . '` ( meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT, term_id bigint(20) unsigned NOT NULL DEFAULT 0, meta_key varchar(255) DEFAULT NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY term_id (term_id), KEY meta_key (meta_key(191)) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' ); } protected function setUp(): void { global $wpdb; $wpdb->query( 'TRUNCATE TABLE `' . self::TERMMETA . '`' ); $GLOBALS['_wp_options'] = array(); update_option( WPDO_WC_Term_Count_Filter::OPT_ENABLED, '1' ); } // ── is_target_key ──────────────────────────────────────────────────────── public function test_is_target_key_matches_product_count_prefix(): void { $this->assertTrue( WPDO_WC_Term_Count_Filter::is_target_key( 'product_count_product_cat' ) ); $this->assertTrue( WPDO_WC_Term_Count_Filter::is_target_key( 'product_count_product_tag' ) ); $this->assertTrue( WPDO_WC_Term_Count_Filter::is_target_key( 'product_count_pa_color' ) ); } public function test_is_target_key_rejects_non_product_count(): void { $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( 'hp_sort_order' ) ); $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( 'hp_default' ) ); $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( 'product_count' ) ); // exact, not prefix $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( 'something_product_count_foo' ) ); } public function test_is_target_key_rejects_non_string(): void { $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( null ) ); $this->assertFalse( WPDO_WC_Term_Count_Filter::is_target_key( 123 ) ); } // ── translate_key ──────────────────────────────────────────────────────── public function test_translate_key_namespaces_by_term_id(): void { $key1 = WPDO_WC_Term_Count_Filter::translate_key( 5, 'product_count_product_cat' ); $key2 = WPDO_WC_Term_Count_Filter::translate_key( 6, 'product_count_product_cat' ); $this->assertNotSame( $key1, $key2 ); $this->assertStringContainsString( 'wpdo_wc_termcount_5_', $key1 ); $this->assertStringContainsString( 'wpdo_wc_termcount_6_', $key2 ); } public function test_translate_key_md5_handles_long_taxonomy(): void { $long_key = 'product_count_my_super_long_taxonomy_slug_' . str_repeat( 'x', 200 ); $translated = WPDO_WC_Term_Count_Filter::translate_key( 1, $long_key ); $option_name = '_transient_' . $translated; $this->assertLessThanOrEqual( 191, strlen( $option_name ) ); } // ── on_read ────────────────────────────────────────────────────────────── public function test_on_read_returns_pre_for_non_target_key(): void { $result = WPDO_WC_Term_Count_Filter::on_read( null, 1, 'hp_sort_order', true ); $this->assertNull( $result ); } public function test_on_read_returns_value_when_present(): void { $translated = WPDO_WC_Term_Count_Filter::translate_key( 5, 'product_count_product_cat' ); update_option( '_transient_' . $translated, '42' ); $result = WPDO_WC_Term_Count_Filter::on_read( null, 5, 'product_count_product_cat', true ); $this->assertSame( array( '42' ), $result ); } public function test_on_read_falls_through_on_cache_miss(): void { $result = WPDO_WC_Term_Count_Filter::on_read( null, 5, 'product_count_product_cat', true ); $this->assertNull( $result, 'Cache miss should fall through (return $pre)' ); } // ── on_add / on_update ─────────────────────────────────────────────────── public function test_on_add_writes_to_wp_options_and_short_circuits(): void { $result = WPDO_WC_Term_Count_Filter::on_add( null, 5, 'product_count_product_cat', '42', false ); $this->assertTrue( $result ); $translated = WPDO_WC_Term_Count_Filter::translate_key( 5, 'product_count_product_cat' ); $this->assertSame( '42', get_option( '_transient_' . $translated ) ); } public function test_on_update_writes_to_wp_options(): void { WPDO_WC_Term_Count_Filter::on_update( null, 7, 'product_count_product_tag', '15', '' ); $translated = WPDO_WC_Term_Count_Filter::translate_key( 7, 'product_count_product_tag' ); $this->assertSame( '15', get_option( '_transient_' . $translated ) ); } public function test_on_add_passes_through_non_target(): void { $this->assertNull( WPDO_WC_Term_Count_Filter::on_add( null, 5, 'hp_sort_order', '5', false ) ); } // ── on_delete ──────────────────────────────────────────────────────────── public function test_on_delete_clears_wp_options_entry(): void { WPDO_WC_Term_Count_Filter::on_update( null, 5, 'product_count_product_cat', '42', '' ); $translated = WPDO_WC_Term_Count_Filter::translate_key( 5, 'product_count_product_cat' ); $this->assertSame( '42', get_option( '_transient_' . $translated ) ); WPDO_WC_Term_Count_Filter::on_delete( null, 5, 'product_count_product_cat', '', false ); $this->assertFalse( get_option( '_transient_' . $translated ) ); } // ── Round-trip ─────────────────────────────────────────────────────────── public function test_full_round_trip(): void { WPDO_WC_Term_Count_Filter::on_update( null, 12, 'product_count_product_cat', 'serialized_value', '' ); $value = WPDO_WC_Term_Count_Filter::on_read( null, 12, 'product_count_product_cat', true ); $this->assertSame( array( 'serialized_value' ), $value ); } // ── count/purge legacy ────────────────────────────────────────────────── public function test_count_legacy_termmeta_returns_zero_for_clean(): void { $this->assertSame( 0, WPDO_WC_Term_Count_Filter::count_legacy_termmeta_rows() ); } public function test_count_legacy_termmeta_counts_only_product_count(): void { global $wpdb; $rows = array( array( 'term_id' => 1, 'meta_key' => 'product_count_product_cat', 'meta_value' => '42' ), array( 'term_id' => 2, 'meta_key' => 'product_count_product_tag', 'meta_value' => '15' ), array( 'term_id' => 1, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ), // not target ); foreach ( $rows as $r ) { $wpdb->insert( self::TERMMETA, $r ); } $this->assertSame( 2, WPDO_WC_Term_Count_Filter::count_legacy_termmeta_rows() ); } public function test_purge_legacy_termmeta_deletes_only_product_count(): void { global $wpdb; $wpdb->insert( self::TERMMETA, array( 'term_id' => 1, 'meta_key' => 'product_count_product_cat', 'meta_value' => '42' ) ); $wpdb->insert( self::TERMMETA, array( 'term_id' => 2, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ) ); $deleted = WPDO_WC_Term_Count_Filter::purge_legacy_termmeta_rows(); $this->assertSame( 1, $deleted ); $remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' ); $this->assertSame( 1, $remaining ); } // ── is_enabled ────────────────────────────────────────────────────────── public function test_is_enabled_defaults_true(): void { delete_option( WPDO_WC_Term_Count_Filter::OPT_ENABLED ); $this->assertTrue( WPDO_WC_Term_Count_Filter::is_enabled() ); } public function test_is_enabled_respects_zero_value(): void { update_option( WPDO_WC_Term_Count_Filter::OPT_ENABLED, '0' ); $this->assertFalse( WPDO_WC_Term_Count_Filter::is_enabled() ); } }