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:
@@ -0,0 +1,143 @@
|
||||
<?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 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user