Files
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

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 );
}
}