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

230 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Tests for WPDO_Zone_Cold — JSON blob storage with Object Cache integration.
*/
class ZoneColdTest extends TestCase {
/** In-memory "database" store: post_id => json string */
public static array $db_store = [];
/** Control whether get_var returns the "id" existence check */
public static bool $row_exists = false;
protected function setUp(): void {
self::$db_store = [];
self::$row_exists = false;
$GLOBALS['_wp_cache'] = [];
$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 );
}
/**
* get_var is used for two things:
* 1. SELECT data ... → return JSON blob
* 2. SELECT id ... → return '1' if exists, else null
*/
public function get_var( string $sql ): ?string {
$flat = preg_replace( '/\s+/', ' ', $sql );
// Existence check (save_blob path).
if ( stripos( $flat, 'SELECT id' ) !== false ) {
if ( preg_match( "/post_id = (\d+)/", $flat, $m ) ) {
return isset( ZoneColdTest::$db_store[ (int) $m[1] ] ) ? '1' : null;
}
return null;
}
// Data fetch.
if ( preg_match( "/post_id = (\d+)/", $flat, $m ) ) {
return ZoneColdTest::$db_store[ (int) $m[1] ] ?? null;
}
return null;
}
public function get_row( string $sql, $output = OBJECT ) {
return null;
}
public function get_results( string $sql, $output = OBJECT ): array {
return [];
}
public function insert( string $table, array $data, $format = null ): int|false {
if ( isset( $data['post_id'], $data['data'] ) ) {
ZoneColdTest::$db_store[ (int) $data['post_id'] ] = $data['data'];
}
return 1;
}
public function update( string $table, array $data, array $where, $format = null, $where_format = null ): int|false {
if ( isset( $where['post_id'], $data['data'] ) ) {
ZoneColdTest::$db_store[ (int) $where['post_id'] ] = $data['data'];
}
return 1;
}
public function delete( string $table, array $where, $format = null ): int|false {
if ( isset( $where['post_id'] ) ) {
unset( ZoneColdTest::$db_store[ (int) $where['post_id'] ] );
}
return 1;
}
public function query( string $sql ): int|bool {
return 1;
}
};
}
// ── table() ──────────────────────────────────────────────────────────────
public function test_table_returns_prefixed_name(): void {
$this->assertSame( 'wp_wpdo_cold_hp_listing', WPDO_Zone_Cold::table( 'hp_listing' ) );
}
// ── get() ─────────────────────────────────────────────────────────────────
public function test_get_returns_null_for_missing_key(): void {
// Cache miss + no DB row → blob is empty array → key missing → null.
$result = WPDO_Zone_Cold::get( 99, 'hp_listing', 'hp_description' );
$this->assertNull( $result );
}
public function test_get_reads_from_cache_on_hit(): void {
// Pre-populate cache so DB should NOT be hit.
$group = 'wpdo_cold_hp_listing';
$cache_key = 'cold_1';
$GLOBALS['_wp_cache'][ $group ][ $cache_key ] = [ 'hp_bio' => 'cached value' ];
$result = WPDO_Zone_Cold::get( 1, 'hp_listing', 'hp_bio' );
$this->assertSame( 'cached value', $result );
// DB store should remain empty (DB was not queried for data).
$this->assertEmpty( self::$db_store );
}
// ── get_blob() ───────────────────────────────────────────────────────────
public function test_get_blob_queries_db_on_cache_miss(): void {
self::$db_store[5] = json_encode( [ 'hp_description' => 'Hello World', 'hp_location' => 'Paris' ] );
$blob = WPDO_Zone_Cold::get_blob( 5, 'hp_listing' );
$this->assertSame( 'Hello World', $blob['hp_description'] );
$this->assertSame( 'Paris', $blob['hp_location'] );
}
// ── set() ─────────────────────────────────────────────────────────────────
public function test_set_merges_new_key_into_blob(): void {
// Seed an existing blob.
self::$db_store[10] = json_encode( [ 'a' => 1 ] );
WPDO_Zone_Cold::set( 10, 'hp_listing', 'b', 2 );
$stored = json_decode( self::$db_store[10], true );
$this->assertArrayHasKey( 'a', $stored );
$this->assertArrayHasKey( 'b', $stored );
$this->assertSame( 1, $stored['a'] );
$this->assertSame( 2, $stored['b'] );
}
// ── set_many() ───────────────────────────────────────────────────────────
public function test_set_many_merges_multiple_keys(): void {
WPDO_Zone_Cold::set_many( 20, 'hp_listing', [
'key1' => 'v1',
'key2' => 'v2',
'key3' => 'v3',
] );
$stored = json_decode( self::$db_store[20], true );
$this->assertSame( 'v1', $stored['key1'] );
$this->assertSame( 'v2', $stored['key2'] );
$this->assertSame( 'v3', $stored['key3'] );
}
// ── remove() ─────────────────────────────────────────────────────────────
public function test_remove_deletes_key_from_blob(): void {
self::$db_store[30] = json_encode( [ 'keep' => 'yes', 'drop' => 'no' ] );
WPDO_Zone_Cold::remove( 30, 'hp_listing', 'drop' );
$stored = json_decode( self::$db_store[30], true );
$this->assertArrayHasKey( 'keep', $stored );
$this->assertArrayNotHasKey( 'drop', $stored );
}
// ── delete() ─────────────────────────────────────────────────────────────
public function test_delete_clears_cache(): void {
$group = 'wpdo_cold_hp_listing';
$cache_key = 'cold_1';
// Pre-populate cache.
$GLOBALS['_wp_cache'][ $group ][ $cache_key ] = [ 'some' => 'data' ];
WPDO_Zone_Cold::delete( 1, 'hp_listing' );
// Cache entry must be gone.
$this->assertFalse( isset( $GLOBALS['_wp_cache'][ $group ][ $cache_key ] ) );
}
// ── Additional edge-case tests ────────────────────────────────────────────
public function test_set_invalidates_object_cache(): void {
$group = 'wpdo_cold_hp_listing';
$cache_key = 'cold_50';
// Pre-populate cache with stale data.
$GLOBALS['_wp_cache'][ $group ][ $cache_key ] = [ 'stale' => 'old_value' ];
WPDO_Zone_Cold::set( 50, 'hp_listing', 'fresh', 'new_value' );
// Cache must be invalidated after write.
$this->assertFalse( isset( $GLOBALS['_wp_cache'][ $group ][ $cache_key ] ) );
}
public function test_get_blob_populates_cache_on_db_hit(): void {
// Seed the DB store so get_blob has something to fetch.
self::$db_store[60] = json_encode( [ 'cached_key' => 'cached_val' ] );
WPDO_Zone_Cold::get_blob( 60, 'hp_listing' );
// Cache must now contain the fetched data.
$group = 'wpdo_cold_hp_listing';
$cached = $GLOBALS['_wp_cache'][ $group ]['cold_60'] ?? false;
$this->assertIsArray( $cached );
$this->assertSame( 'cached_val', $cached['cached_key'] );
}
public function test_remove_nonexistent_key_is_safe(): void {
// Store an existing blob.
self::$db_store[70] = json_encode( [ 'keep' => 'this' ] );
// Remove a key that doesn't exist — should not throw.
WPDO_Zone_Cold::remove( 70, 'hp_listing', 'nonexistent_key' );
$stored = json_decode( self::$db_store[70], true );
$this->assertArrayHasKey( 'keep', $stored );
$this->assertArrayNotHasKey( 'nonexistent_key', $stored );
}
}