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

316 lines
12 KiB
PHP
Raw Permalink 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_Term_Stress_Tester (v2.13.0).
*
* Verifies the term stress tester contract:
* - State machine: start / get_state / get_progress / cancel
* - count_test_terms() matches slug prefix
* - cleanup() removes test terms + cascade
* - Input validation throws / errors correctly
* - Run benchmark structure
*
* Note: Realistic mode tests (wp_insert_term path) are exercised live in dev10
* smoke tests since the bootstrap wp_insert_term stub is intentionally minimal.
*/
class TermStressTesterTest extends TestCase {
private const TERMS = 'wp_itest_terms';
private const TERM_TAXONOMY = 'wp_itest_term_taxonomy';
private const TERMMETA = 'wp_itest_termmeta';
public static function setUpBeforeClass(): void {
global $wpdb;
if ( ! class_exists( 'WPDO_Term_Stress_Tester' ) ) {
require_once WPDO_PLUGIN_DIR . 'includes/class-tmdo-term-stress-tester.php';
}
$wpdb->terms = self::TERMS;
$wpdb->termmeta = self::TERMMETA;
$wpdb->query( 'DROP TABLE IF EXISTS `' . self::TERMS . '`' );
$wpdb->query(
'CREATE TABLE `' . self::TERMS . '` (
term_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(200) NOT NULL DEFAULT "",
slug varchar(200) NOT NULL DEFAULT "",
term_group bigint(10) NOT NULL DEFAULT 0,
PRIMARY KEY (term_id),
KEY slug (slug(191))
) DEFAULT CHARACTER SET utf8mb4'
);
$wpdb->query( 'DROP TABLE IF EXISTS `' . self::TERM_TAXONOMY . '`' );
$wpdb->query(
'CREATE TABLE `' . self::TERM_TAXONOMY . '` (
term_taxonomy_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
term_id bigint(20) unsigned NOT NULL DEFAULT 0,
taxonomy varchar(32) NOT NULL DEFAULT "",
description longtext,
parent bigint(20) unsigned NOT NULL DEFAULT 0,
count bigint(20) NOT NULL DEFAULT 0,
PRIMARY KEY (term_taxonomy_id),
KEY taxonomy (taxonomy)
) DEFAULT CHARACTER SET utf8mb4'
);
$wpdb->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' );
}
public static function tearDownAfterClass(): void {
global $wpdb;
foreach ( array( self::TERMS, self::TERM_TAXONOMY, self::TERMMETA ) as $tbl ) {
$wpdb->query( 'DROP TABLE IF EXISTS `' . $tbl . '`' );
}
}
protected function setUp(): void {
global $wpdb;
$wpdb->query( 'TRUNCATE TABLE `' . self::TERMS . '`' );
$wpdb->query( 'TRUNCATE TABLE `' . self::TERM_TAXONOMY . '`' );
$wpdb->query( 'TRUNCATE TABLE `' . self::TERMMETA . '`' );
// Reset state per test so each starts idle.
unset( $GLOBALS['_wp_options'][ WPDO_Term_Stress_Tester::OPT_STATE ] );
unset( $GLOBALS['_wp_transients'][ WPDO_Term_Stress_Tester::CANCEL_FLAG ] );
unset( $GLOBALS['_wp_transients']['wpdo_term_stress_pump_lock'] );
}
// ── create() (fast-path direct SQL) ──────────────────────────────────────
public function test_create_inserts_terms_into_taxonomy(): void {
$result = WPDO_Term_Stress_Tester::create( 'category', 5 );
$this->assertSame( 5, $result['created'] );
$this->assertSame( 'category', $result['taxonomy'] );
$this->assertNotNull( $result['first_id'] );
$this->assertNotNull( $result['last_id'] );
global $wpdb;
$count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMS . '`' );
$this->assertSame( 5, $count );
$tax_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::TERM_TAXONOMY . "` WHERE taxonomy = 'category'" );
$this->assertSame( 5, $tax_count );
}
public function test_create_uses_stress_slug_prefix(): void {
WPDO_Term_Stress_Tester::create( 'category', 3 );
global $wpdb;
$prefix_count = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM `" . self::TERMS . "` WHERE slug LIKE %s",
WPDO_Term_Stress_Tester::TEST_TERM_PREFIX . '%'
)
);
$this->assertSame( 3, $prefix_count );
}
public function test_create_seeds_termmeta_keys(): void {
WPDO_Term_Stress_Tester::create( 'category', 3 );
global $wpdb;
$total_meta = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' );
// 3 terms × 3 keys (hp_sort_order/hp_default/hp_icon) = 9
$this->assertSame( 9, $total_meta );
}
public function test_create_rejects_empty_taxonomy(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Term_Stress_Tester::create( '', 3 );
}
public function test_create_rejects_zero_count(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Term_Stress_Tester::create( 'category', 0 );
}
public function test_create_rejects_excessive_count(): void {
$this->expectException( InvalidArgumentException::class );
WPDO_Term_Stress_Tester::create( 'category', 100001 );
}
// ── count_test_terms() ────────────────────────────────────────────────────
public function test_count_test_terms_returns_zero_for_empty(): void {
$this->assertSame( 0, WPDO_Term_Stress_Tester::count_test_terms() );
}
public function test_count_test_terms_counts_only_stress_prefix(): void {
WPDO_Term_Stress_Tester::create( 'category', 4 );
global $wpdb;
$wpdb->insert( self::TERMS, array( 'name' => 'Real', 'slug' => 'real-term', 'term_group' => 0 ) );
$this->assertSame( 4, WPDO_Term_Stress_Tester::count_test_terms() );
}
// ── cleanup() ─────────────────────────────────────────────────────────────
public function test_cleanup_removes_test_terms_and_cascade(): void {
WPDO_Term_Stress_Tester::create( 'category', 5 );
$this->assertSame( 5, WPDO_Term_Stress_Tester::count_test_terms() );
$result = WPDO_Term_Stress_Tester::cleanup();
$this->assertSame( 5, $result['deleted_terms'] );
global $wpdb;
$this->assertSame( 0, (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMS . '`' ) );
$this->assertSame( 0, (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMMETA . '`' ) );
$this->assertSame( 0, (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERM_TAXONOMY . '`' ) );
}
public function test_cleanup_preserves_non_stress_terms(): void {
global $wpdb;
$wpdb->insert( self::TERMS, array( 'name' => 'Real', 'slug' => 'real-term', 'term_group' => 0 ) );
WPDO_Term_Stress_Tester::create( 'category', 3 );
$result = WPDO_Term_Stress_Tester::cleanup();
$this->assertSame( 3, $result['deleted_terms'] );
$remaining = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TERMS . '`' );
$this->assertSame( 1, $remaining );
}
public function test_cleanup_idempotent_on_empty(): void {
$first = WPDO_Term_Stress_Tester::cleanup();
$second = WPDO_Term_Stress_Tester::cleanup();
$this->assertSame( 0, $first['deleted_terms'] );
$this->assertSame( 0, $second['deleted_terms'] );
}
// ── State machine ────────────────────────────────────────────────────────
public function test_get_state_returns_empty_when_idle(): void {
$this->assertSame( array(), WPDO_Term_Stress_Tester::get_state() );
}
public function test_get_progress_returns_idle_when_no_state(): void {
$progress = WPDO_Term_Stress_Tester::get_progress( false );
$this->assertSame( 'idle', $progress['status'] );
}
public function test_start_persists_state_with_running_status(): void {
// Stub taxonomy_exists() — fall back to true via global flag.
$GLOBALS['_taxonomy_exists_override'] = true;
$result = WPDO_Term_Stress_Tester::start( 'category', 10, 'fast', 5 );
$this->assertTrue( $result['ok'], 'start should succeed' );
$state = $result['state'];
$this->assertSame( 'running', $state['status'] );
$this->assertSame( 'category', $state['taxonomy'] );
$this->assertSame( 'fast', $state['mode'] );
$this->assertSame( 10, $state['target'] );
$this->assertSame( 5, $state['batch_size'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_start_rejects_unknown_taxonomy(): void {
$GLOBALS['_taxonomy_exists_override'] = false;
$result = WPDO_Term_Stress_Tester::start( 'never_exists', 10 );
$this->assertFalse( $result['ok'] );
$this->assertStringContainsString( 'unknown_taxonomy', $result['error'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_start_rejects_invalid_mode(): void {
$GLOBALS['_taxonomy_exists_override'] = true;
$result = WPDO_Term_Stress_Tester::start( 'category', 10, 'turbo' );
$this->assertFalse( $result['ok'] );
$this->assertSame( 'invalid mode', $result['error'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_start_rejects_concurrent_run(): void {
$GLOBALS['_taxonomy_exists_override'] = true;
WPDO_Term_Stress_Tester::start( 'category', 10 );
$result = WPDO_Term_Stress_Tester::start( 'category', 5 );
$this->assertFalse( $result['ok'] );
$this->assertSame( 'already_running', $result['error'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_run_batch_advances_processed_count(): void {
$GLOBALS['_taxonomy_exists_override'] = true;
WPDO_Term_Stress_Tester::start( 'category', 6, 'fast', 3 );
WPDO_Term_Stress_Tester::run_batch();
$progress = WPDO_Term_Stress_Tester::get_progress( false );
$this->assertSame( 3, $progress['processed'] );
$this->assertSame( 1, $progress['batches_done'] );
$this->assertSame( 'running', $progress['status'] );
WPDO_Term_Stress_Tester::run_batch();
$progress = WPDO_Term_Stress_Tester::get_progress( false );
$this->assertSame( 6, $progress['processed'] );
$this->assertSame( 'completed', $progress['status'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_cancel_marks_state_as_cancelled(): void {
$GLOBALS['_taxonomy_exists_override'] = true;
WPDO_Term_Stress_Tester::start( 'category', 100, 'fast', 50 );
$result = WPDO_Term_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_Term_Stress_Tester::run_batch();
$state = WPDO_Term_Stress_Tester::get_state();
$this->assertSame( 'cancelled', $state['status'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_cancel_returns_no_active_job_when_idle(): void {
$result = WPDO_Term_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 {
$GLOBALS['_taxonomy_exists_override'] = true;
WPDO_Term_Stress_Tester::start( 'category', 10, 'fast', 5 );
WPDO_Term_Stress_Tester::run_batch();
$progress = WPDO_Term_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_term_count', $progress );
$this->assertSame( 50.0, $progress['pct'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
public function test_run_benchmark_returns_structured_payload(): void {
$GLOBALS['_taxonomy_exists_override'] = true;
WPDO_Term_Stress_Tester::start( 'category', 4, 'fast', 4 );
WPDO_Term_Stress_Tester::run_batch();
$state = WPDO_Term_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( 'category', $state['benchmark']['taxonomy'] );
unset( $GLOBALS['_taxonomy_exists_override'] );
}
}