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,302 @@
|
||||
<?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'] );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user