termmeta to point at our test table. $wpdb->termmeta = self::TERMMETA; $wpdb->query( 'DROP TABLE IF EXISTS `' . self::TERMMETA . '`' ); $wpdb->query( 'CREATE TABLE `' . 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' ); } public static function tearDownAfterClass(): void { global $wpdb; $wpdb->query( 'DROP TABLE IF EXISTS `' . self::TERMMETA . '`' ); } protected function setUp(): void { global $wpdb; $wpdb->query( 'TRUNCATE TABLE `' . self::TERMMETA . '`' ); } private function seed( array $rows ): void { global $wpdb; foreach ( $rows as $row ) { $wpdb->insert( self::TERMMETA, $row ); } } // ── count_garbage ──────────────────────────────────────────────────────── public function test_count_garbage_returns_zero_for_empty_table(): void { $counts = WPDO_Termmeta_Cleaner::count_garbage( 'all' ); $this->assertSame( 0, $counts['wxr_import'] ); $this->assertSame( 0, $counts['demo_data'] ); $this->assertSame( 0, $counts['transients'] ); $this->assertSame( 0, $counts['total'] ); } public function test_count_garbage_counts_wxr_import(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_wxr_import_user_xyz', 'meta_value' => 'a' ), array( 'term_id' => 2, 'meta_key' => '_wxr_import_post', 'meta_value' => 'b' ), array( 'term_id' => 3, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ), ) ); $counts = WPDO_Termmeta_Cleaner::count_garbage( 'wxr_import' ); $this->assertSame( 2, $counts['wxr_import'] ); $this->assertSame( 0, $counts['demo_data'] ); $this->assertSame( 0, $counts['transients'] ); $this->assertSame( 2, $counts['total'] ); } public function test_count_garbage_counts_demo_data(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_2meet_demo_music', 'meta_value' => '1' ), array( 'term_id' => 2, 'meta_key' => '_2meet_demo_adv', 'meta_value' => '1' ), array( 'term_id' => 3, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ), ) ); $counts = WPDO_Termmeta_Cleaner::count_garbage( 'demo_data' ); $this->assertSame( 0, $counts['wxr_import'] ); $this->assertSame( 2, $counts['demo_data'] ); $this->assertSame( 0, $counts['transients'] ); $this->assertSame( 2, $counts['total'] ); } public function test_count_garbage_counts_transients(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_transient_foo', 'meta_value' => 'a' ), array( 'term_id' => 1, 'meta_key' => '_transient_timeout_foo', 'meta_value' => '9999' ), array( 'term_id' => 2, 'meta_key' => 'hp_default', 'meta_value' => '1' ), ) ); $counts = WPDO_Termmeta_Cleaner::count_garbage( 'transients' ); $this->assertSame( 2, $counts['transients'] ); $this->assertSame( 2, $counts['total'] ); } public function test_count_garbage_all_unions_three_buckets(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_wxr_import_user', 'meta_value' => 'a' ), array( 'term_id' => 2, 'meta_key' => '_2meet_demo_music', 'meta_value' => '1' ), array( 'term_id' => 3, 'meta_key' => '_transient_foo', 'meta_value' => 'b' ), array( 'term_id' => 4, 'meta_key' => 'hp_icon', 'meta_value' => 'star' ), array( 'term_id' => 5, 'meta_key' => 'hp_default', 'meta_value' => '1' ), ) ); $counts = WPDO_Termmeta_Cleaner::count_garbage( 'all' ); $this->assertSame( 1, $counts['wxr_import'] ); $this->assertSame( 1, $counts['demo_data'] ); $this->assertSame( 1, $counts['transients'] ); $this->assertSame( 3, $counts['total'] ); } // ── delete_garbage ──────────────────────────────────────────────────────── public function test_delete_garbage_removes_targeted_rows_only(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_wxr_import_user', 'meta_value' => 'a' ), array( 'term_id' => 2, 'meta_key' => '_2meet_demo_music', 'meta_value' => '1' ), array( 'term_id' => 3, 'meta_key' => '_transient_foo', 'meta_value' => 'b' ), array( 'term_id' => 4, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ), ) ); $deleted = WPDO_Termmeta_Cleaner::delete_garbage( 'all' ); $this->assertSame( 1, $deleted['wxr_import'] ); $this->assertSame( 1, $deleted['demo_data'] ); $this->assertSame( 1, $deleted['transients'] ); $this->assertSame( 3, $deleted['total'] ); global $wpdb; $remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' ); $this->assertSame( 1, $remaining, 'hp_sort_order must survive' ); } public function test_delete_garbage_target_specific_only_removes_one_bucket(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => '_wxr_import_user', 'meta_value' => 'a' ), array( 'term_id' => 2, 'meta_key' => '_2meet_demo_music', 'meta_value' => '1' ), array( 'term_id' => 3, 'meta_key' => '_transient_foo', 'meta_value' => 'b' ), ) ); $deleted = WPDO_Termmeta_Cleaner::delete_garbage( 'wxr_import' ); $this->assertSame( 1, $deleted['wxr_import'] ); $this->assertSame( 0, $deleted['demo_data'] ); $this->assertSame( 0, $deleted['transients'] ); $this->assertSame( 1, $deleted['total'] ); global $wpdb; $remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' ); $this->assertSame( 2, $remaining, '_2meet_demo + _transient must survive when target=wxr_import' ); } public function test_delete_garbage_idempotent_on_clean_table(): void { $this->seed( array( array( 'term_id' => 1, 'meta_key' => 'hp_sort_order', 'meta_value' => '5' ), ) ); $first = WPDO_Termmeta_Cleaner::delete_garbage( 'all' ); $second = WPDO_Termmeta_Cleaner::delete_garbage( 'all' ); $this->assertSame( 0, $first['total'] ); $this->assertSame( 0, $second['total'] ); global $wpdb; $remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' ); $this->assertSame( 1, $remaining ); } public function test_invalid_target_throws(): void { $this->expectException( InvalidArgumentException::class ); WPDO_Termmeta_Cleaner::count_garbage( 'bogus' ); } }