Files
2meet-data-optimizer/tests/integration/WarmArchiveIntegrationTest.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

270 lines
9.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Integration tests — Warm Zone (Zone B) + Archive Zone (Zone D) lifecycle.
*
* Covers:
* - Warm zone: set/get/TTL/purge_expired/flush_views
* - Archive zone: archive_batch/get/stats/restore/gzip roundtrip
* - WPDO_Listing_Stats: increment + flush + REST view count
*
* Uses dedicated test tables (wp_itest_ prefix) to avoid collisions.
*/
use PHPUnit\Framework\TestCase;
class WarmArchiveIntegrationTest extends TestCase {
private static string $warm_table;
private static string $archive_table;
private static string $errors_table;
private static string $postmeta_table;
public static function setUpBeforeClass(): void {
global $wpdb;
self::$warm_table = $wpdb->prefix . 'wpdo_warm';
self::$archive_table = $wpdb->prefix . 'wpdo_archive';
self::$errors_table = $wpdb->prefix . 'wpdo_errors';
self::$postmeta_table = $wpdb->postmeta;
// DROP + CREATE ensures clean schema even after interrupted prior runs.
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$warm_table . "`" );
$wpdb->query(
"CREATE TABLE `" . self::$warm_table . "` (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT UNSIGNED NOT NULL,
meta_key VARCHAR(255) NOT NULL,
meta_value LONGTEXT,
expires_at DATETIME DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY post_meta (post_id, meta_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
// Create archive table (matches WPDO_Installer::install_system_tables DDL).
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$archive_table . "`" );
$wpdb->query(
"CREATE TABLE `" . self::$archive_table . "` (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
post_type VARCHAR(20) NOT NULL DEFAULT '',
meta_key VARCHAR(255) NOT NULL DEFAULT '',
meta_value LONGTEXT,
compressed TINYINT(1) NOT NULL DEFAULT 0,
archived_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
original_meta_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
KEY idx_post_id (post_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
// Create errors table (needed by WPDO_Logger).
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$errors_table . "`" );
$wpdb->query(
"CREATE TABLE `" . self::$errors_table . "` (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
severity VARCHAR(20) NOT NULL DEFAULT 'error',
module VARCHAR(100) NOT NULL DEFAULT '',
zone VARCHAR(50) DEFAULT NULL,
hook VARCHAR(100) NOT NULL DEFAULT '',
message TEXT NOT NULL,
context LONGTEXT DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
// Create postmeta table (needed by flush_views_to_postmeta batch query).
// Only create if it does not already exist — shared with other test classes.
$wpdb->query(
"CREATE TABLE IF NOT EXISTS `" . self::$postmeta_table . "` (
meta_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
meta_key VARCHAR(255) DEFAULT NULL,
meta_value LONGTEXT,
PRIMARY KEY (meta_id),
KEY post_id (post_id),
KEY meta_key (meta_key(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
}
public static function tearDownAfterClass(): void {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$warm_table . "`" );
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$archive_table . "`" );
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$errors_table . "`" );
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$postmeta_table . "`" );
}
protected function setUp(): void {
global $wpdb;
$wpdb->query( "TRUNCATE TABLE `" . self::$warm_table . "`" );
$wpdb->query( "TRUNCATE TABLE `" . self::$archive_table . "`" );
$GLOBALS['_wp_cache'] = [];
$GLOBALS['_wp_postmeta'] = [];
$GLOBALS['_wp_options'] = [];
}
// ── Zone B (Warm) ─────────────────────────────────────────────────────────
public function test_warm_set_and_get(): void {
WPDO_Zone_Warm::set( 100, 'wp_key', 'hello', null );
$val = WPDO_Zone_Warm::get( 100, 'wp_key' );
$this->assertSame( 'hello', $val );
}
public function test_warm_expired_returns_null(): void {
global $wpdb;
// Insert already-expired entry.
$wpdb->query(
"INSERT INTO `" . self::$warm_table . "` (post_id, meta_key, meta_value, expires_at)
VALUES (101, 'stale_key', 'old', '2000-01-01 00:00:00')"
);
$val = WPDO_Zone_Warm::get( 101, 'stale_key' );
$this->assertNull( $val );
}
public function test_warm_purge_expired(): void {
global $wpdb;
$wpdb->query(
"INSERT INTO `" . self::$warm_table . "` (post_id, meta_key, meta_value, expires_at)
VALUES (102, 'k1', 'v1', '2000-01-01 00:00:00'),
(103, 'k2', 'v2', DATE_ADD(NOW(), INTERVAL 1 HOUR))"
);
$deleted = WPDO_Zone_Warm::purge_expired();
$this->assertGreaterThanOrEqual( 1, $deleted );
// k2 (future TTL) should still exist.
$this->assertNotNull( WPDO_Zone_Warm::get( 103, 'k2' ) );
}
public function test_warm_delete_all_for_post(): void {
WPDO_Zone_Warm::set( 200, 'a', 'va', null );
WPDO_Zone_Warm::set( 200, 'b', 'vb', null );
WPDO_Zone_Warm::set( 201, 'a', 'other', null );
WPDO_Zone_Warm::delete_all( 200 );
$this->assertNull( WPDO_Zone_Warm::get( 200, 'a' ) );
$this->assertNull( WPDO_Zone_Warm::get( 200, 'b' ) );
$this->assertSame( 'other', WPDO_Zone_Warm::get( 201, 'a' ) );
}
// ── Zone D (Archive) ──────────────────────────────────────────────────────
/** Helper: build a row for archive_batch with required post_type. */
private function archive_rows( int $post_id, array $metas ): array {
return array_map( fn( $m ) => array_merge(
[ 'post_id' => $post_id, 'post_type' => 'hp_listing', 'meta_id' => 0 ],
$m
), $metas );
}
public function test_archive_batch_stores_compressed(): void {
global $wpdb;
$rows = $this->archive_rows( 400, [
[ 'meta_key' => 'hp_price', 'meta_value' => '999' ],
[ 'meta_key' => 'hp_featured', 'meta_value' => '1' ],
] );
WPDO_Zone_Archive::archive_batch( $rows, true );
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$archive_table . "` WHERE post_id = 400" );
$this->assertSame( 2, $count );
$comp = (int) $wpdb->get_var( "SELECT SUM(compressed) FROM `" . self::$archive_table . "` WHERE post_id = 400" );
$this->assertSame( 2, $comp );
}
public function test_archive_get_returns_values(): void {
$rows = $this->archive_rows( 401, [
[ 'meta_key' => 'hp_price', 'meta_value' => '500' ],
[ 'meta_key' => 'hp_verified', 'meta_value' => '1' ],
] );
WPDO_Zone_Archive::archive_batch( $rows, false );
$result = WPDO_Zone_Archive::get( 401 );
$this->assertCount( 2, $result );
$by_key = array_column( $result, 'meta_value', 'meta_key' );
$this->assertSame( '500', $by_key['hp_price'] );
$this->assertSame( '1', $by_key['hp_verified'] );
}
public function test_archive_get_with_key_filter(): void {
$rows = $this->archive_rows( 402, [
[ 'meta_key' => 'hp_price', 'meta_value' => '250' ],
[ 'meta_key' => 'hp_featured', 'meta_value' => '0' ],
] );
WPDO_Zone_Archive::archive_batch( $rows, false );
$result = WPDO_Zone_Archive::get( 402, 'hp_price' );
$this->assertCount( 1, $result );
$this->assertSame( 'hp_price', $result[0]['meta_key'] );
}
public function test_archive_restore_writes_postmeta(): void {
global $wpdb;
$rows = $this->archive_rows( 403, [
[ 'meta_key' => 'hp_price', 'meta_value' => '777' ],
] );
WPDO_Zone_Archive::archive_batch( $rows, false );
$restored = WPDO_Zone_Archive::restore( 403 );
$this->assertSame( 1, $restored );
$pm = $GLOBALS['_wp_postmeta'][403]['hp_price'] ?? null;
$this->assertSame( '777', $pm );
$remaining = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$archive_table . "` WHERE post_id = 403" );
$this->assertSame( 0, $remaining );
}
public function test_archive_gzip_roundtrip(): void {
$rows = $this->archive_rows( 404, [
[ 'meta_key' => 'hp_description', 'meta_value' => str_repeat( 'Lorem ipsum ', 50 ) ],
] );
WPDO_Zone_Archive::archive_batch( $rows, true );
$result = WPDO_Zone_Archive::get( 404 );
$this->assertCount( 1, $result );
$this->assertStringContainsString( 'Lorem ipsum', $result[0]['meta_value'] );
}
public function test_archive_delete_removes_rows(): void {
global $wpdb;
$rows = $this->archive_rows( 405, [
[ 'meta_key' => 'hp_price', 'meta_value' => '1' ],
] );
WPDO_Zone_Archive::archive_batch( $rows, false );
WPDO_Zone_Archive::delete( 405 );
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$archive_table . "` WHERE post_id = 405" );
$this->assertSame( 0, $count );
}
public function test_archive_stats_counts_correctly(): void {
global $wpdb;
$wpdb->query( "TRUNCATE TABLE `" . self::$archive_table . "`" );
WPDO_Zone_Archive::archive_batch( $this->archive_rows( 500, [
[ 'meta_key' => 'k1', 'meta_value' => 'a' ],
[ 'meta_key' => 'k2', 'meta_value' => 'b' ],
] ), true );
WPDO_Zone_Archive::archive_batch( $this->archive_rows( 502, [
[ 'meta_key' => 'k3', 'meta_value' => 'c' ],
] ), false );
$stats = WPDO_Zone_Archive::stats();
$this->assertSame( 3, $stats['total_rows'] );
$this->assertSame( 2, $stats['compressed_rows'] );
}
}