d36bb954d1
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
144 lines
5.4 KiB
PHP
144 lines
5.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Tests for WPDO_Zone_Classifier — zone suggestion engine.
|
|
*
|
|
* Tests private static methods via PHP Reflection.
|
|
*/
|
|
class ZoneClassifierTest extends TestCase {
|
|
|
|
/**
|
|
* Invoke a private static method on WPDO_Zone_Classifier via reflection.
|
|
*/
|
|
private function invoke_private( string $method, array $args ): mixed {
|
|
$ref = new ReflectionClass( WPDO_Zone_Classifier::class );
|
|
$m = $ref->getMethod( $method );
|
|
$m->setAccessible( true );
|
|
return $m->invokeArgs( null, $args );
|
|
}
|
|
|
|
// ── is_wp_internal() ─────────────────────────────────────────────────────
|
|
|
|
public function test_is_wp_internal_returns_true_for_edit_lock(): void {
|
|
$result = $this->invoke_private( 'is_wp_internal', [ '_edit_lock' ] );
|
|
$this->assertTrue( $result );
|
|
}
|
|
|
|
public function test_is_wp_internal_returns_true_for_thumbnail_id(): void {
|
|
$result = $this->invoke_private( 'is_wp_internal', [ '_thumbnail_id' ] );
|
|
$this->assertTrue( $result );
|
|
}
|
|
|
|
public function test_is_wp_internal_returns_false_for_hp_price(): void {
|
|
$result = $this->invoke_private( 'is_wp_internal', [ 'hp_price' ] );
|
|
$this->assertFalse( $result );
|
|
}
|
|
|
|
public function test_is_wp_internal_returns_false_for_custom_key(): void {
|
|
$result = $this->invoke_private( 'is_wp_internal', [ 'my_custom_meta' ] );
|
|
$this->assertFalse( $result );
|
|
}
|
|
|
|
// ── score_zones() — hot ──────────────────────────────────────────────────
|
|
|
|
public function test_score_zones_hot_for_numeric_short_values(): void {
|
|
$signals = $this->make_signals( [
|
|
'avg_length' => 10,
|
|
'numeric_ratio' => 0.9,
|
|
'distinct_values' => 5,
|
|
] );
|
|
|
|
$scores = $this->invoke_private( 'score_zones', [ $signals ] );
|
|
|
|
// Hot should outrank cold and archive.
|
|
$this->assertGreaterThan( $scores['cold'], $scores['hot'] );
|
|
$this->assertGreaterThan( $scores['archive'], $scores['hot'] );
|
|
}
|
|
|
|
// ── score_zones() — warm ─────────────────────────────────────────────────
|
|
|
|
public function test_score_zones_warm_for_transient_prefix(): void {
|
|
$signals = $this->make_signals( [ 'prefix' => 'transient' ] );
|
|
|
|
$scores = $this->invoke_private( 'score_zones', [ $signals ] );
|
|
|
|
$this->assertGreaterThanOrEqual( 0.8, $scores['warm'] );
|
|
}
|
|
|
|
// ── score_zones() — cold ─────────────────────────────────────────────────
|
|
|
|
public function test_score_zones_cold_for_long_json_values(): void {
|
|
$signals = $this->make_signals( [
|
|
'avg_length' => 300,
|
|
'is_json' => true,
|
|
] );
|
|
|
|
$scores = $this->invoke_private( 'score_zones', [ $signals ] );
|
|
|
|
$this->assertGreaterThan( $scores['hot'], $scores['cold'] );
|
|
$this->assertGreaterThan( $scores['archive'], $scores['cold'] );
|
|
}
|
|
|
|
// ── score_zones() — archive ──────────────────────────────────────────────
|
|
|
|
public function test_score_zones_archive_for_high_trash_ratio(): void {
|
|
$signals = $this->make_signals( [ 'trash_ratio' => 0.7 ] );
|
|
|
|
$scores = $this->invoke_private( 'score_zones', [ $signals ] );
|
|
|
|
$this->assertGreaterThanOrEqual( 0.6, $scores['archive'] );
|
|
}
|
|
|
|
// ── score_zones() — default ───────────────────────────────────────────────
|
|
|
|
public function test_score_zones_defaults_cold_when_no_signals(): void {
|
|
$signals = $this->make_signals( [] );
|
|
|
|
$scores = $this->invoke_private( 'score_zones', [ $signals ] );
|
|
|
|
// With avg_length=0, numeric_ratio=0, etc., hot gets 0.3 (avg_length<50 is true for 0).
|
|
// Cold must have at least a non-zero score (either from scoring or the fallback).
|
|
$max = max( $scores );
|
|
$this->assertGreaterThan( 0.0, $max );
|
|
$this->assertGreaterThan( 0.0, $scores['cold'] + $scores['hot'] ); // At least one has a score.
|
|
}
|
|
|
|
// ── build_reasons() ──────────────────────────────────────────────────────
|
|
|
|
public function test_build_reasons_hot_includes_numeric_message(): void {
|
|
$signals = $this->make_signals( [
|
|
'avg_length' => 10,
|
|
'numeric_ratio' => 0.9,
|
|
'distinct_values' => 5,
|
|
] );
|
|
|
|
$reasons = $this->invoke_private( 'build_reasons', [ $signals, 'hot' ] );
|
|
|
|
$combined = implode( ' ', $reasons );
|
|
$this->assertStringContainsStringIgnoringCase( 'numeric', $combined );
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Build a signals array with defaults, overriding specific keys.
|
|
*/
|
|
private function make_signals( array $overrides ): array {
|
|
return array_merge( [
|
|
'meta_key' => 'test_key',
|
|
'row_count' => 100,
|
|
'avg_length' => 0,
|
|
'max_length' => 0,
|
|
'distinct_values' => 0,
|
|
'numeric_ratio' => 0.0,
|
|
'trash_ratio' => 0.0,
|
|
'is_serialized' => false,
|
|
'is_json' => false,
|
|
'prefix' => '',
|
|
], $overrides );
|
|
}
|
|
}
|