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
This commit is contained in:
2026-07-31 05:06:36 +08:00
commit d36bb954d1
206 changed files with 66538 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
if ( ! function_exists( '__' ) ) {
function __( string $text, string $domain = 'default' ): string { return $text; }
}
if ( ! function_exists( 'number_format_i18n' ) ) {
function number_format_i18n( $n, int $decimals = 0 ): string {
return number_format( (float) $n, $decimals );
}
}
require_once dirname( __DIR__, 3 ) . '/includes/class-tmdo-logger.php';
require_once dirname( __DIR__, 3 ) . '/includes/class-tmdo-feature-flags.php';
require_once dirname( __DIR__, 3 ) . '/includes/advisor/class-tmdo-module-rules.php';
require_once dirname( __DIR__, 3 ) . '/includes/advisor/class-tmdo-module-detector.php';
/**
* Unit tests for WPDO_Module_Rules + WPDO_Module_Detector (v2.5.0 M16).
*
* Pure-logic tests using mocked WPDO_Compatibility (via dynamic class
* substitution where needed) and option-driven post counts.
*/
class ModuleDetectorTest extends TestCase {
protected function setUp(): void {
$GLOBALS['_wp_options'] = array();
// Reset Feature_Flags cache.
$ref = new ReflectionClass( WPDO_Feature_Flags::class );
$prop = $ref->getProperty( 'cache' );
$prop->setAccessible( true );
$prop->setValue( null, null );
$this->setup_wpdb_mock();
}
private function setup_wpdb_mock(): void {
global $wpdb;
$wpdb = new class {
public string $prefix = 'wp_';
public string $posts = 'wp_posts';
public string $postmeta = 'wp_postmeta';
public string $options = 'wp_options';
public array $count_overrides = array();
public function prepare( string $sql, ...$args ): string {
$i = 0;
return preg_replace_callback( '/%[sd]/', function() use ( &$i, $args ) {
return is_string( $args[ $i ] ?? null ) ? "'" . $args[ $i++ ] . "'" : (string) ( $args[ $i++ ] ?? '?' );
}, $sql );
}
public function get_var( string $sql ) {
// Mock by inspecting SQL pattern.
if ( str_contains( $sql, "post_type =" ) ) {
if ( preg_match( "/post_type = '([^']+)'/", $sql, $m ) ) {
return (string) ( $this->count_overrides[ $m[1] ] ?? 0 );
}
}
if ( str_contains( $sql, "post_status = 'trash'" ) ) {
return (string) ( $this->count_overrides['__trash__'] ?? 0 );
}
return '0';
}
public function get_results( string $sql, $output = ARRAY_A ): array { return array(); }
};
}
public function test_module_rules_known_modules_count(): void {
$modules = WPDO_Module_Rules::known_modules();
$this->assertGreaterThanOrEqual( 14, count( $modules ), '預期 ≥ 14 個 module9 HPCT + 6 zone - 1 dup' );
$this->assertContains( 'reviews', $modules );
$this->assertContains( 'warm', $modules );
$this->assertContains( 'archive', $modules );
$this->assertContains( 'hot_hp_listing', $modules );
}
public function test_module_rule_for_module_returns_array(): void {
$rule = WPDO_Module_Rules::for_module( 'reviews' );
$this->assertIsArray( $rule );
$this->assertArrayHasKey( 'compat_required', $rule );
$this->assertArrayHasKey( 'description', $rule );
}
public function test_module_rule_unknown_module_returns_null(): void {
$this->assertNull( WPDO_Module_Rules::for_module( 'totally_fake_module_xyz' ) );
}
public function test_detector_skip_when_module_not_idle(): void {
// Set reviews to dual_write.
WPDO_Feature_Flags::set( 'reviews', 'dual_write' );
$r = WPDO_Module_Detector::detect_one( 'reviews' );
$this->assertSame( 'skip', $r['recommendation'] );
$this->assertFalse( $r['available'] );
$this->assertNotEmpty( $r['blockers'] );
$this->assertSame( 'dual_write', $r['current_state'] );
}
public function test_detector_warm_module_recommends_for_any_site(): void {
// warm module's compat_required is empty + no post_type.
$r = WPDO_Module_Detector::detect_one( 'warm' );
$this->assertTrue( $r['available'] );
$this->assertSame( 'enable', $r['recommendation'] );
$this->assertGreaterThan( 0, $r['confidence'] );
$this->assertNotEmpty( $r['suggested_action'] );
$this->assertSame( 'dual_write', $r['suggested_action']['to_state'] );
}
public function test_detector_archive_blocked_when_no_trashed_posts(): void {
// trashed = 0, threshold = 50.
global $wpdb;
$wpdb->count_overrides['__trash__'] = 0;
$r = WPDO_Module_Detector::detect_one( 'archive' );
$this->assertSame( 'wait', $r['recommendation'] );
$this->assertFalse( $r['available'] );
}
public function test_detector_archive_passes_when_enough_trashed(): void {
global $wpdb;
$wpdb->count_overrides['__trash__'] = 100;
$r = WPDO_Module_Detector::detect_one( 'archive' );
$this->assertSame( 'enable', $r['recommendation'] );
$this->assertTrue( $r['available'] );
}
public function test_detector_hivepress_required_blocks_when_inactive(): void {
// reviews requires hivepress; Compatibility class isn't loaded in this test.
// Without Compatibility class, the helper returns the full required list as missing.
$r = WPDO_Module_Detector::detect_one( 'reviews' );
$this->assertSame( 'skip', $r['recommendation'] );
$this->assertNotEmpty( $r['blockers'] );
}
public function test_get_actionable_filters_by_confidence(): void {
$actionable = WPDO_Module_Detector::get_actionable( 0.5 );
$this->assertIsArray( $actionable );
// Each result should have available=true and recommendation=enable.
foreach ( $actionable as $module => $r ) {
$this->assertTrue( $r['available'], "$module should be available" );
$this->assertSame( 'enable', $r['recommendation'] );
$this->assertGreaterThanOrEqual( 0.5, $r['confidence'] );
}
}
public function test_get_actionable_sorts_by_confidence_desc(): void {
$actionable = WPDO_Module_Detector::get_actionable( 0.0 );
$last_confidence = 1.0;
foreach ( $actionable as $r ) {
$this->assertLessThanOrEqual( $last_confidence, (float) $r['confidence'] );
$last_confidence = (float) $r['confidence'];
}
}
public function test_detect_all_returns_entry_for_each_known_module(): void {
$all = WPDO_Module_Detector::detect_all( true );
$known = WPDO_Module_Rules::known_modules();
foreach ( $known as $m ) {
$this->assertArrayHasKey( $m, $all, "Detector should return entry for $m" );
}
}
}