Files
2meet-data-optimizer/tests/unit/ZoneArchiveTest.php
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

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