Files
2meet-data-optimizer/tests/integration/CommentStressTesterTest.php
T
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

323 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Integration test: WPDO_Comment_Stress_Tester (v2.13.1).
*
* Verifies the comment stress tester contract:
* - State machine: start / get_state / get_progress / cancel
* - count_test_comments() matches email-domain marker
* - cleanup() removes test comments + cascade
* - Input validation throws / errors correctly
* - Run benchmark structure
*
* Note: Realistic mode tests (wp_insert_comment path) use bootstrap stub which
* inserts directly without firing filter/action chain.
*/
class CommentStressTesterTest extends TestCase {
private const POSTS = 'wp_itest_posts';
private const COMMENTS = 'wp_itest_comments';
private const COMMENTMETA = 'wp_itest_commentmeta';
private static int $test_post_id = 0;
public static function setUpBeforeClass(): void {
global $wpdb;
if ( ! class_exists( 'WPDO_Comment_Stress_Tester' ) ) {
require_once WPDO_PLUGIN_DIR . 'includes/class-tmdo-comment-stress-tester.php';
}
$wpdb->posts = self::POSTS;
$wpdb->comments = self::COMMENTS;
$wpdb->commentmeta = self::COMMENTMETA;
$wpdb->query( 'CREATE TABLE IF NOT EXISTS `' . self::POSTS . '` (
ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,
post_title text NOT NULL DEFAULT "",
post_status varchar(20) NOT NULL DEFAULT "publish",
post_type varchar(20) NOT NULL DEFAULT "post",
comment_count bigint(20) NOT NULL DEFAULT 0,
PRIMARY KEY (ID)
) DEFAULT CHARACTER SET utf8mb4' );
$wpdb->query( 'DROP TABLE IF EXISTS `' . self::COMMENTS . '`' );
$wpdb->query(
'CREATE TABLE `' . self::COMMENTS . '` (
comment_ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,
comment_post_ID bigint(20) unsigned NOT NULL DEFAULT 0,
comment_author tinytext NOT NULL,
comment_author_email varchar(100) NOT NULL DEFAULT "",
comment_author_url varchar(200) NOT NULL DEFAULT "",
comment_author_IP varchar(100) NOT NULL DEFAULT "",
comment_date datetime NOT NULL DEFAULT "1970-01-01 00:00:00",
comment_date_gmt datetime NOT NULL DEFAULT "1970-01-01 00:00:00",
comment_content text NOT NULL,
comment_karma int(11) NOT NULL DEFAULT 0,
comment_approved varchar(20) NOT NULL DEFAULT "1",
comment_agent varchar(255) NOT NULL DEFAULT "",
comment_type varchar(20) NOT NULL DEFAULT "comment",
comment_parent bigint(20) unsigned NOT NULL DEFAULT 0,
user_id bigint(20) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (comment_ID),
KEY comment_author_email (comment_author_email(10)),
KEY comment_post_ID (comment_post_ID)
) DEFAULT CHARACTER SET utf8mb4'
);
$wpdb->query( 'CREATE TABLE IF NOT EXISTS `' . self::COMMENTMETA . '` (
meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
comment_id bigint(20) unsigned NOT NULL DEFAULT 0,
meta_key varchar(255) DEFAULT NULL,
meta_value longtext,
PRIMARY KEY (meta_id),
KEY comment_id (comment_id),
KEY meta_key (meta_key(191))
) DEFAULT CHARACTER SET utf8mb4' );
// Seed a single fixture post to satisfy post_exists() checks.
$wpdb->query( 'TRUNCATE TABLE `' . self::POSTS . '`' );
$wpdb->insert( self::POSTS, array(
'post_title' => 'WPDO Comment Stress Fixture Post',
'post_status' => 'publish',
'post_type' => 'post',
) );
self::$test_post_id = (int) $wpdb->insert_id;
}
public static function tearDownAfterClass(): void {
global $wpdb;
foreach ( array( self::COMMENTS, self::COMMENTMETA ) as $tbl ) {
$wpdb->query( 'DROP TABLE IF EXISTS `' . $tbl . '`' );
}
// Don't drop wp_itest_posts — shared fixture across test classes.
}
protected function setUp(): void {
global $wpdb;
$wpdb->query( 'TRUNCATE TABLE `' . self::COMMENTS . '`' );
$wpdb->query( 'TRUNCATE TABLE `' . self::COMMENTMETA . '`' );
// Reset state per test so each starts idle.
unset( $GLOBALS['_wp_options'][ WPDO_Comment_Stress_Tester::OPT_STATE ] );
unset( $GLOBALS['_wp_transients'][ WPDO_Comment_Stress_Tester::CANCEL_FLAG ] );
unset( $GLOBALS['_wp_transients']['wpdo_comment_stress_pump_lock'] );
}
// ── create() (fast-path direct SQL) ──────────────────────────────────────
public function test_create_inserts_comments_for_post(): void {
$result = WPDO_Comment_Stress_Tester::create( self::$test_post_id, 5 );
$this->assertSame( 5, $result['created'] );
$this->assertSame( self::$test_post_id, $result['post_id'] );
$this->assertNotNull( $result['first_id'] );
$this->assertNotNull( $result['last_id'] );
global $wpdb;
$count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::COMMENTS . '`' );
$this->assertSame( 5, $count );
}
public function test_create_uses_stress_email_domain(): void {
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 3 );
global $wpdb;
$prefix_count = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM `" . self::COMMENTS . "` WHERE comment_author_email LIKE %s",
'%@' . WPDO_Comment_Stress_Tester::TEST_EMAIL_DOMAIN
)
);
$this->assertSame( 3, $prefix_count );
}
public function test_create_seeds_commentmeta_keys(): void {
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 3 );
global $wpdb;
$total_meta = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::COMMENTMETA . '`' );
// 3 comments × 1 key (hp_rating) = 3.
$this->assertSame( 3, $total_meta );
}
public function test_create_rejects_bad_post_id(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Comment_Stress_Tester::create( 0, 3 );
}
public function test_create_rejects_zero_count(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 0 );
}
public function test_create_rejects_excessive_count(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 100001 );
}
// ── count_test_comments() ────────────────────────────────────────────────
public function test_count_test_comments_returns_zero_for_empty(): void {
$this->assertSame( 0, WPDO_Comment_Stress_Tester::count_test_comments() );
}
public function test_count_test_comments_counts_only_stress_emails(): void {
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 4 );
global $wpdb;
$wpdb->insert( self::COMMENTS, array(
'comment_post_ID' => self::$test_post_id,
'comment_author' => 'Real',
'comment_author_email' => 'real@example.com',
'comment_content' => 'Real comment',
'comment_approved' => '1',
) );
$this->assertSame( 4, WPDO_Comment_Stress_Tester::count_test_comments() );
}
// ── cleanup() ─────────────────────────────────────────────────────────────
public function test_cleanup_removes_test_comments_and_cascade(): void {
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 5 );
$this->assertSame( 5, WPDO_Comment_Stress_Tester::count_test_comments() );
$result = WPDO_Comment_Stress_Tester::cleanup();
$this->assertSame( 5, $result['deleted_comments'] );
global $wpdb;
$this->assertSame( 0, (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::COMMENTS . '`' ) );
$this->assertSame( 0, (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::COMMENTMETA . '`' ) );
}
public function test_cleanup_preserves_non_stress_comments(): void {
global $wpdb;
$wpdb->insert( self::COMMENTS, array(
'comment_post_ID' => self::$test_post_id,
'comment_author' => 'Real',
'comment_author_email' => 'real@example.com',
'comment_content' => 'Real comment',
'comment_approved' => '1',
) );
WPDO_Comment_Stress_Tester::create( self::$test_post_id, 3 );
$result = WPDO_Comment_Stress_Tester::cleanup();
$this->assertSame( 3, $result['deleted_comments'] );
$remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::COMMENTS . '`' );
$this->assertSame( 1, $remaining );
}
public function test_cleanup_idempotent_on_empty(): void {
$first = WPDO_Comment_Stress_Tester::cleanup();
$second = WPDO_Comment_Stress_Tester::cleanup();
$this->assertSame( 0, $first['deleted_comments'] );
$this->assertSame( 0, $second['deleted_comments'] );
}
// ── State machine ────────────────────────────────────────────────────────
public function test_get_state_returns_empty_when_idle(): void {
$this->assertSame( array(), WPDO_Comment_Stress_Tester::get_state() );
}
public function test_get_progress_returns_idle_when_no_state(): void {
$progress = WPDO_Comment_Stress_Tester::get_progress( false );
$this->assertSame( 'idle', $progress['status'] );
}
public function test_start_persists_state_with_running_status(): void {
$result = WPDO_Comment_Stress_Tester::start( self::$test_post_id, 10, 'fast', 5 );
$this->assertTrue( $result['ok'], 'start should succeed' );
$state = $result['state'];
$this->assertSame( 'running', $state['status'] );
$this->assertSame( self::$test_post_id, $state['post_id'] );
$this->assertSame( 'fast', $state['mode'] );
$this->assertSame( 10, $state['target'] );
$this->assertSame( 5, $state['batch_size'] );
}
public function test_start_rejects_unknown_post(): void {
$result = WPDO_Comment_Stress_Tester::start( 999999, 10 );
$this->assertFalse( $result['ok'] );
$this->assertStringContainsString( 'unknown_post', $result['error'] );
}
public function test_start_rejects_invalid_mode(): void {
$result = WPDO_Comment_Stress_Tester::start( self::$test_post_id, 10, 'turbo' );
$this->assertFalse( $result['ok'] );
$this->assertSame( 'invalid mode', $result['error'] );
}
public function test_start_rejects_concurrent_run(): void {
WPDO_Comment_Stress_Tester::start( self::$test_post_id, 10 );
$result = WPDO_Comment_Stress_Tester::start( self::$test_post_id, 5 );
$this->assertFalse( $result['ok'] );
$this->assertSame( 'already_running', $result['error'] );
}
public function test_run_batch_advances_processed_count(): void {
WPDO_Comment_Stress_Tester::start( self::$test_post_id, 6, 'fast', 3 );
WPDO_Comment_Stress_Tester::run_batch();
$progress = WPDO_Comment_Stress_Tester::get_progress( false );
$this->assertSame( 3, $progress['processed'] );
$this->assertSame( 1, $progress['batches_done'] );
$this->assertSame( 'running', $progress['status'] );
WPDO_Comment_Stress_Tester::run_batch();
$progress = WPDO_Comment_Stress_Tester::get_progress( false );
$this->assertSame( 6, $progress['processed'] );
$this->assertSame( 'completed', $progress['status'] );
}
public function test_cancel_marks_state_as_cancelled(): void {
WPDO_Comment_Stress_Tester::start( self::$test_post_id, 100, 'fast', 50 );
$result = WPDO_Comment_Stress_Tester::cancel();
$this->assertTrue( $result['ok'] );
$this->assertSame( 'cancelled', $result['state']['status'] );
// In-flight batch run after cancel must NOT bump status back to running.
WPDO_Comment_Stress_Tester::run_batch();
$state = WPDO_Comment_Stress_Tester::get_state();
$this->assertSame( 'cancelled', $state['status'] );
}
public function test_cancel_returns_no_active_job_when_idle(): void {
$result = WPDO_Comment_Stress_Tester::cancel();
$this->assertTrue( $result['ok'] );
$this->assertSame( 'no_active_job', $result['message'] ?? '' );
}
public function test_get_progress_includes_pct_and_eta_keys(): void {
WPDO_Comment_Stress_Tester::start( self::$test_post_id, 10, 'fast', 5 );
WPDO_Comment_Stress_Tester::run_batch();
$progress = WPDO_Comment_Stress_Tester::get_progress( false );
$this->assertArrayHasKey( 'pct', $progress );
$this->assertArrayHasKey( 'rate_per_sec', $progress );
$this->assertArrayHasKey( 'elapsed_sec', $progress );
$this->assertArrayHasKey( 'eta_sec', $progress );
$this->assertArrayHasKey( 'test_comment_count', $progress );
$this->assertSame( 50.0, $progress['pct'] );
}
public function test_run_benchmark_returns_structured_payload(): void {
WPDO_Comment_Stress_Tester::start( self::$test_post_id, 4, 'fast', 4 );
WPDO_Comment_Stress_Tester::run_batch();
$state = WPDO_Comment_Stress_Tester::get_state();
$this->assertSame( 'completed', $state['status'] );
$this->assertIsArray( $state['benchmark'] );
$this->assertArrayHasKey( 'write', $state['benchmark'] );
$this->assertArrayHasKey( 'db_sizes', $state['benchmark'] );
$this->assertSame( self::$test_post_id, $state['benchmark']['post_id'] );
}
}