Files
2meet-data-optimizer/tests/integration/PostmetaCleanerIntegrationTest.php
wpdev d36bb954d1 chore: initial snapshot of 2meet-data-optimizer v0.1.0
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:06:36 +08:00

205 lines
8.6 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Integration test: WPDO_Postmeta_Cleaner — wp_postmeta garbage cleanup (v2.9.0).
*
* Verifies count_garbage() and delete_garbage() against real MariaDB:
* - target=transients → meta_key LIKE '_transient_%' OR LIKE '_transient_timeout_%'
* - target=wp_old_date → meta_key = '_wp_old_date'
* - target=edit_locks → meta_key = '_edit_lock' AND lock_ts < now - 86400 (stale)
* - target=all → union of all three
*
* Requires real MariaDB (WPDO_TEST_DB_PASS env var must be set).
*/
class PostmetaCleanerIntegrationTest extends TestCase {
private const POSTMETA = 'wp_itest_postmeta';
// ── Fixture lifecycle ─────────────────────────────────────────────────────
public static function setUpBeforeClass(): void {
global $wpdb;
require_once WPDO_PLUGIN_DIR . 'includes/class-tmdo-postmeta-cleaner.php';
$wpdb->query( 'DROP TABLE IF EXISTS `' . self::POSTMETA . '`' );
$wpdb->query(
'CREATE TABLE `' . self::POSTMETA . '` (
meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
post_id bigint(20) unsigned NOT NULL DEFAULT 0,
meta_key varchar(255) DEFAULT NULL,
meta_value longtext,
PRIMARY KEY (meta_id),
KEY post_id (post_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::POSTMETA . '`' );
}
protected function setUp(): void {
global $wpdb;
$wpdb->query( 'TRUNCATE TABLE `' . self::POSTMETA . '`' );
}
// ── Helpers ───────────────────────────────────────────────────────────────
private function seed_postmeta( array $rows ): void {
global $wpdb;
foreach ( $rows as $row ) {
$wpdb->insert( self::POSTMETA, $row );
}
}
// ── count_garbage() ───────────────────────────────────────────────────────
public function test_count_garbage_returns_zero_for_empty_table(): void {
$counts = WPDO_Postmeta_Cleaner::count_garbage( 'all' );
$this->assertSame( 0, $counts['transients'] );
$this->assertSame( 0, $counts['wp_old_date'] );
$this->assertSame( 0, $counts['edit_locks'] );
$this->assertSame( 0, $counts['total'] );
}
public function test_count_garbage_counts_transients(): void {
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_transient_hp_models/listing/v1', 'meta_value' => 'a' ),
array( 'post_id' => 1, 'meta_key' => '_transient_timeout_hp_models/listing/v1', 'meta_value' => '9999' ),
array( 'post_id' => 2, 'meta_key' => '_transient_foo', 'meta_value' => 'b' ),
array( 'post_id' => 2, 'meta_key' => 'hp_price', 'meta_value' => '99' ),
) );
$counts = WPDO_Postmeta_Cleaner::count_garbage( 'transients' );
$this->assertSame( 3, $counts['transients'] );
$this->assertSame( 0, $counts['wp_old_date'] );
$this->assertSame( 0, $counts['edit_locks'] );
$this->assertSame( 3, $counts['total'] );
}
public function test_count_garbage_counts_wp_old_date(): void {
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_wp_old_date', 'meta_value' => '2024-01-01' ),
array( 'post_id' => 2, 'meta_key' => '_wp_old_date', 'meta_value' => '2024-02-01' ),
array( 'post_id' => 3, 'meta_key' => 'hp_price', 'meta_value' => '99' ),
) );
$counts = WPDO_Postmeta_Cleaner::count_garbage( 'wp_old_date' );
$this->assertSame( 0, $counts['transients'] );
$this->assertSame( 2, $counts['wp_old_date'] );
$this->assertSame( 0, $counts['edit_locks'] );
$this->assertSame( 2, $counts['total'] );
}
public function test_count_garbage_counts_only_stale_edit_locks(): void {
$now = time();
$one_day_ago = $now - 86400 - 60; // stale by 1 day + 1 min
$one_hour_ago = $now - 3600; // fresh, not stale
$five_min_ago = $now - 300; // very fresh, not stale
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_edit_lock', 'meta_value' => $one_day_ago . ':1' ), // stale ✓
array( 'post_id' => 2, 'meta_key' => '_edit_lock', 'meta_value' => $one_hour_ago . ':2' ), // fresh
array( 'post_id' => 3, 'meta_key' => '_edit_lock', 'meta_value' => $five_min_ago . ':3' ), // fresh
array( 'post_id' => 4, 'meta_key' => '_edit_last', 'meta_value' => '4' ), // not edit_lock
) );
$counts = WPDO_Postmeta_Cleaner::count_garbage( 'edit_locks' );
$this->assertSame( 0, $counts['transients'] );
$this->assertSame( 0, $counts['wp_old_date'] );
$this->assertSame( 1, $counts['edit_locks'], 'Only stale (>24h old) _edit_lock rows count' );
$this->assertSame( 1, $counts['total'] );
}
public function test_count_garbage_target_all_unions_all_three(): void {
$one_day_ago = time() - 86400 - 60;
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_transient_foo', 'meta_value' => 'a' ),
array( 'post_id' => 2, 'meta_key' => '_transient_timeout_foo', 'meta_value' => '99' ),
array( 'post_id' => 3, 'meta_key' => '_wp_old_date', 'meta_value' => '2024-01-01' ),
array( 'post_id' => 4, 'meta_key' => '_edit_lock', 'meta_value' => $one_day_ago . ':1' ),
array( 'post_id' => 5, 'meta_key' => 'hp_price', 'meta_value' => '99' ),
) );
$counts = WPDO_Postmeta_Cleaner::count_garbage( 'all' );
$this->assertSame( 2, $counts['transients'] );
$this->assertSame( 1, $counts['wp_old_date'] );
$this->assertSame( 1, $counts['edit_locks'] );
$this->assertSame( 4, $counts['total'] );
}
public function test_count_garbage_rejects_invalid_target(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Postmeta_Cleaner::count_garbage( 'bogus' );
}
// ── delete_garbage() ──────────────────────────────────────────────────────
public function test_delete_garbage_removes_only_target_rows(): void {
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_transient_foo', 'meta_value' => 'a' ),
array( 'post_id' => 2, 'meta_key' => '_wp_old_date', 'meta_value' => '2024-01-01' ),
array( 'post_id' => 3, 'meta_key' => 'hp_price', 'meta_value' => '99' ),
) );
$deleted = WPDO_Postmeta_Cleaner::delete_garbage( 'transients' );
$this->assertSame( 1, $deleted['transients'] );
$this->assertSame( 0, $deleted['wp_old_date'] );
$this->assertSame( 0, $deleted['edit_locks'] );
$this->assertSame( 1, $deleted['total'] );
global $wpdb;
$remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::POSTMETA . '`' );
$this->assertSame( 2, $remaining, 'Non-transient rows should remain (wp_old_date + hp_price)' );
}
public function test_delete_garbage_target_all_clears_all_three(): void {
$one_day_ago = time() - 86400 - 60;
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_transient_foo', 'meta_value' => 'a' ),
array( 'post_id' => 2, 'meta_key' => '_wp_old_date', 'meta_value' => '2024-01-01' ),
array( 'post_id' => 3, 'meta_key' => '_edit_lock', 'meta_value' => $one_day_ago . ':1' ),
array( 'post_id' => 4, 'meta_key' => 'hp_price', 'meta_value' => '99' ),
) );
$deleted = WPDO_Postmeta_Cleaner::delete_garbage( 'all' );
$this->assertSame( 1, $deleted['transients'] );
$this->assertSame( 1, $deleted['wp_old_date'] );
$this->assertSame( 1, $deleted['edit_locks'] );
$this->assertSame( 3, $deleted['total'] );
global $wpdb;
$remaining = $wpdb->get_results( 'SELECT meta_key FROM `' . self::POSTMETA . '`', ARRAY_A );
$this->assertCount( 1, $remaining );
$this->assertSame( 'hp_price', $remaining[0]['meta_key'] );
}
public function test_delete_garbage_does_not_touch_fresh_edit_lock(): void {
$one_hour_ago = time() - 3600;
$this->seed_postmeta( array(
array( 'post_id' => 1, 'meta_key' => '_edit_lock', 'meta_value' => $one_hour_ago . ':1' ),
) );
$deleted = WPDO_Postmeta_Cleaner::delete_garbage( 'edit_locks' );
$this->assertSame( 0, $deleted['edit_locks'] );
global $wpdb;
$remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::POSTMETA . '`' );
$this->assertSame( 1, $remaining, 'Fresh edit_lock must survive cleanup' );
}
public function test_delete_garbage_rejects_invalid_target(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Postmeta_Cleaner::delete_garbage( 'bogus' );
}
}