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