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
303 lines
11 KiB
PHP
303 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Unit tests for WPDO_Zone_Archive — gzip-compressed historical archival.
|
|
*
|
|
* Uses an in-memory store to intercept $wpdb calls.
|
|
*/
|
|
class ZoneArchiveTest extends TestCase {
|
|
|
|
/** In-memory archive rows captured from $wpdb->insert() calls. */
|
|
public static array $store = [];
|
|
|
|
/** Arguments of the last $wpdb->delete() call. */
|
|
public static array $last_delete = [];
|
|
|
|
protected function setUp(): void {
|
|
self::$store = [];
|
|
self::$last_delete = [];
|
|
$GLOBALS['_wp_postmeta'] = [];
|
|
$this->setup_wpdb_mock();
|
|
}
|
|
|
|
private function setup_wpdb_mock(): void {
|
|
global $wpdb;
|
|
|
|
$wpdb = new class {
|
|
public string $prefix = 'wp_';
|
|
|
|
public function prepare( string $sql, ...$args ): string {
|
|
$i = 0;
|
|
return preg_replace_callback( '/%([sd])/', function ( $m ) use ( &$i, $args ) {
|
|
$val = $args[ $i++ ] ?? '';
|
|
return $m[1] === 'd' ? (string) (int) $val : "'" . addslashes( (string) $val ) . "'";
|
|
}, $sql );
|
|
}
|
|
|
|
public function insert( string $table, array $data, $format = null ): int|false {
|
|
ZoneArchiveTest::$store[] = $data;
|
|
return 1;
|
|
}
|
|
|
|
public function delete( string $table, array $where, $format = null ): int|false {
|
|
ZoneArchiveTest::$last_delete = [ 'table' => $table, 'where' => $where ];
|
|
// Remove matching rows (single-column where only).
|
|
ZoneArchiveTest::$store = array_values( array_filter(
|
|
ZoneArchiveTest::$store,
|
|
static function ( array $row ) use ( $where ): bool {
|
|
foreach ( $where as $col => $val ) {
|
|
if ( isset( $row[ $col ] ) && (string) $row[ $col ] === (string) $val ) {
|
|
return false; // row matches → remove.
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
) );
|
|
return 1;
|
|
}
|
|
|
|
public function get_results( string $sql, $output = OBJECT ): array {
|
|
$flat = preg_replace( '/\s+/', ' ', $sql );
|
|
|
|
// stats() GROUP BY post_type query.
|
|
if ( stripos( $flat, 'GROUP BY post_type' ) !== false ) {
|
|
$by_type = [];
|
|
foreach ( ZoneArchiveTest::$store as $row ) {
|
|
$pt = $row['post_type'] ?? 'unknown';
|
|
$by_type[ $pt ] = ( $by_type[ $pt ] ?? 0 ) + 1;
|
|
}
|
|
$result = [];
|
|
foreach ( $by_type as $pt => $cnt ) {
|
|
$result[] = [ 'post_type' => $pt, 'cnt' => (string) $cnt ];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
// get() queries — filter by post_id and optional meta_key.
|
|
$post_id = null;
|
|
$meta_key = null;
|
|
|
|
if ( preg_match( '/post_id = (\d+)/', $flat, $m ) ) {
|
|
$post_id = (int) $m[1];
|
|
}
|
|
if ( preg_match( "/AND meta_key = '([^']+)'/", $flat, $m ) ) {
|
|
$meta_key = $m[1];
|
|
}
|
|
|
|
$result = [];
|
|
foreach ( ZoneArchiveTest::$store as $row ) {
|
|
if ( $post_id !== null && (int) ( $row['post_id'] ?? 0 ) !== $post_id ) {
|
|
continue;
|
|
}
|
|
if ( $meta_key !== null && ( $row['meta_key'] ?? '' ) !== $meta_key ) {
|
|
continue;
|
|
}
|
|
$result[] = [
|
|
'meta_key' => $row['meta_key'] ?? '',
|
|
'meta_value' => $row['meta_value'] ?? '',
|
|
'compressed' => $row['compressed'] ?? 0,
|
|
'archived_at' => $row['archived_at'] ?? '',
|
|
];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function get_var( string $sql ): ?string {
|
|
$flat = preg_replace( '/\s+/', ' ', $sql );
|
|
|
|
if ( stripos( $flat, 'WHERE compressed = 1' ) !== false ) {
|
|
$count = count( array_filter(
|
|
ZoneArchiveTest::$store,
|
|
static fn( array $r ) => (int) ( $r['compressed'] ?? 0 ) === 1
|
|
) );
|
|
return (string) $count;
|
|
}
|
|
|
|
if ( stripos( $flat, 'COUNT(*)' ) !== false ) {
|
|
return (string) count( ZoneArchiveTest::$store );
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function query( string $sql ): int|bool {
|
|
return 1; // BEGIN, COMMIT, ROLLBACK pass-through.
|
|
}
|
|
};
|
|
}
|
|
|
|
// ── table() ──────────────────────────────────────────────────────────────
|
|
|
|
public function test_table_returns_archive_table_name(): void {
|
|
$this->assertSame( 'wp_wpdo_archive', WPDO_Zone_Archive::table() );
|
|
}
|
|
|
|
// ── archive() ────────────────────────────────────────────────────────────
|
|
|
|
public function test_archive_inserts_row_with_correct_fields(): void {
|
|
WPDO_Zone_Archive::archive( 1, 'hp_listing', 'hp_price', '99.99' );
|
|
|
|
$this->assertCount( 1, self::$store );
|
|
$row = self::$store[0];
|
|
$this->assertSame( 1, $row['post_id'] );
|
|
$this->assertSame( 'hp_listing', $row['post_type'] );
|
|
$this->assertSame( 'hp_price', $row['meta_key'] );
|
|
$this->assertSame( '99.99', $row['meta_value'] );
|
|
}
|
|
|
|
public function test_archive_without_compress_keeps_plaintext_value(): void {
|
|
WPDO_Zone_Archive::archive( 2, 'hp_listing', 'hp_price', 'plain_value', 0, false );
|
|
|
|
$this->assertSame( 'plain_value', self::$store[0]['meta_value'] );
|
|
$this->assertSame( 0, self::$store[0]['compressed'] );
|
|
}
|
|
|
|
public function test_archive_with_compress_sets_compressed_flag(): void {
|
|
WPDO_Zone_Archive::archive( 3, 'hp_listing', 'hp_price', 'compress_me', 0, true );
|
|
|
|
$this->assertSame( 1, self::$store[0]['compressed'] );
|
|
}
|
|
|
|
public function test_archive_with_compress_stores_base64_encoded_gzip(): void {
|
|
$original = 'hello compressed world';
|
|
WPDO_Zone_Archive::archive( 4, 'hp_listing', 'hp_bio', $original, 0, true );
|
|
|
|
$stored = self::$store[0]['meta_value'];
|
|
$decoded = base64_decode( $stored, true );
|
|
$restored = gzdecode( $decoded );
|
|
$this->assertSame( $original, $restored );
|
|
}
|
|
|
|
public function test_archive_stores_original_meta_id(): void {
|
|
WPDO_Zone_Archive::archive( 5, 'hp_listing', 'hp_price', '10', 42 );
|
|
|
|
$this->assertSame( 42, self::$store[0]['original_meta_id'] );
|
|
}
|
|
|
|
// ── archive_batch() ──────────────────────────────────────────────────────
|
|
|
|
public function test_archive_batch_inserts_all_entries(): void {
|
|
WPDO_Zone_Archive::archive_batch( [
|
|
[ 'post_id' => 10, 'post_type' => 'hp_listing', 'meta_key' => 'hp_price', 'meta_value' => '100', 'meta_id' => 0 ],
|
|
[ 'post_id' => 11, 'post_type' => 'hp_listing', 'meta_key' => 'hp_price', 'meta_value' => '200', 'meta_id' => 0 ],
|
|
[ 'post_id' => 12, 'post_type' => 'hp_listing', 'meta_key' => 'hp_price', 'meta_value' => '300', 'meta_id' => 0 ],
|
|
] );
|
|
|
|
$this->assertCount( 3, self::$store );
|
|
}
|
|
|
|
public function test_archive_batch_with_empty_entries_is_safe(): void {
|
|
WPDO_Zone_Archive::archive_batch( [] );
|
|
$this->assertCount( 0, self::$store );
|
|
}
|
|
|
|
// ── get() ─────────────────────────────────────────────────────────────────
|
|
|
|
public function test_get_returns_empty_array_for_missing_post(): void {
|
|
$result = WPDO_Zone_Archive::get( 999 );
|
|
$this->assertSame( [], $result );
|
|
}
|
|
|
|
public function test_get_returns_all_entries_for_post(): void {
|
|
WPDO_Zone_Archive::archive( 20, 'hp_listing', 'hp_price', '50' );
|
|
WPDO_Zone_Archive::archive( 20, 'hp_listing', 'hp_category', '3' );
|
|
|
|
$result = WPDO_Zone_Archive::get( 20 );
|
|
$this->assertCount( 2, $result );
|
|
}
|
|
|
|
public function test_get_with_meta_key_filter_returns_only_matching(): void {
|
|
WPDO_Zone_Archive::archive( 21, 'hp_listing', 'hp_price', '150' );
|
|
WPDO_Zone_Archive::archive( 21, 'hp_listing', 'hp_category', '2' );
|
|
|
|
$result = WPDO_Zone_Archive::get( 21, 'hp_price' );
|
|
$this->assertCount( 1, $result );
|
|
$this->assertSame( 'hp_price', $result[0]['meta_key'] );
|
|
}
|
|
|
|
public function test_get_decompresses_gzipped_values(): void {
|
|
$original = 'hello decompressed world';
|
|
WPDO_Zone_Archive::archive( 22, 'hp_listing', 'hp_bio', $original, 0, true );
|
|
|
|
$result = WPDO_Zone_Archive::get( 22 );
|
|
$this->assertCount( 1, $result );
|
|
$this->assertSame( $original, $result[0]['meta_value'] );
|
|
}
|
|
|
|
public function test_get_removes_compressed_field_from_result(): void {
|
|
WPDO_Zone_Archive::archive( 23, 'hp_listing', 'hp_price', '10' );
|
|
|
|
$result = WPDO_Zone_Archive::get( 23 );
|
|
$this->assertArrayNotHasKey( 'compressed', $result[0] );
|
|
}
|
|
|
|
// ── restore() ────────────────────────────────────────────────────────────
|
|
|
|
public function test_restore_writes_to_post_meta(): void {
|
|
WPDO_Zone_Archive::archive( 30, 'hp_listing', 'hp_price', '77' );
|
|
WPDO_Zone_Archive::archive( 30, 'hp_listing', 'hp_category', '5' );
|
|
|
|
WPDO_Zone_Archive::restore( 30 );
|
|
|
|
$this->assertSame( '77', $GLOBALS['_wp_postmeta'][30]['hp_price'] );
|
|
$this->assertSame( '5', $GLOBALS['_wp_postmeta'][30]['hp_category'] );
|
|
}
|
|
|
|
public function test_restore_returns_correct_entry_count(): void {
|
|
WPDO_Zone_Archive::archive( 31, 'hp_listing', 'hp_price', '88' );
|
|
WPDO_Zone_Archive::archive( 31, 'hp_listing', 'hp_category', '9' );
|
|
|
|
$count = WPDO_Zone_Archive::restore( 31 );
|
|
$this->assertSame( 2, $count );
|
|
}
|
|
|
|
public function test_restore_returns_zero_for_missing_post(): void {
|
|
$count = WPDO_Zone_Archive::restore( 999 );
|
|
$this->assertSame( 0, $count );
|
|
}
|
|
|
|
// ── delete() ─────────────────────────────────────────────────────────────
|
|
|
|
public function test_delete_passes_correct_post_id_to_wpdb(): void {
|
|
WPDO_Zone_Archive::archive( 40, 'hp_listing', 'hp_price', '100' );
|
|
WPDO_Zone_Archive::delete( 40 );
|
|
|
|
$this->assertNotEmpty( self::$last_delete );
|
|
$this->assertSame( 40, self::$last_delete['where']['post_id'] );
|
|
}
|
|
|
|
// ── stats() ──────────────────────────────────────────────────────────────
|
|
|
|
public function test_stats_includes_required_keys(): void {
|
|
$stats = WPDO_Zone_Archive::stats();
|
|
|
|
$this->assertArrayHasKey( 'total_rows', $stats );
|
|
$this->assertArrayHasKey( 'compressed_rows', $stats );
|
|
$this->assertArrayHasKey( 'post_types', $stats );
|
|
}
|
|
|
|
public function test_stats_total_and_compressed_counts(): void {
|
|
WPDO_Zone_Archive::archive( 50, 'hp_listing', 'hp_price', '1', 0, true );
|
|
WPDO_Zone_Archive::archive( 51, 'hp_listing', 'hp_price', '2', 0, false );
|
|
WPDO_Zone_Archive::archive( 52, 'hp_listing', 'hp_price', '3', 0, true );
|
|
|
|
$stats = WPDO_Zone_Archive::stats();
|
|
$this->assertSame( 3, $stats['total_rows'] );
|
|
$this->assertSame( 2, $stats['compressed_rows'] );
|
|
}
|
|
|
|
public function test_stats_post_types_groups_by_type(): void {
|
|
WPDO_Zone_Archive::archive( 60, 'hp_listing', 'hp_price', '1' );
|
|
WPDO_Zone_Archive::archive( 61, 'hp_listing', 'hp_price', '2' );
|
|
WPDO_Zone_Archive::archive( 62, 'hp_vendor', 'hp_bio', 'x' );
|
|
|
|
$stats = WPDO_Zone_Archive::stats();
|
|
$by_type = array_column( $stats['post_types'], 'cnt', 'post_type' );
|
|
$this->assertSame( '2', $by_type['hp_listing'] );
|
|
$this->assertSame( '1', $by_type['hp_vendor'] );
|
|
}
|
|
}
|