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