prefix . 'wpdo_cold_itest' */ private static string $table; // ── Fixture lifecycle ───────────────────────────────────────────────────── public static function setUpBeforeClass(): void { global $wpdb; self::$table = $wpdb->prefix . 'wpdo_cold_' . self::POST_TYPE; $wpdb->query( "CREATE TABLE IF NOT EXISTS `" . self::$table . "` ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, data LONGTEXT NOT NULL, updated_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (id), UNIQUE KEY ui_post_id (post_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" ); } public static function tearDownAfterClass(): void { global $wpdb; $wpdb->query( "DROP TABLE IF EXISTS `" . self::$table . "`" ); } protected function setUp(): void { global $wpdb; $wpdb->query( "TRUNCATE TABLE `" . self::$table . "`" ); $GLOBALS['_wp_cache'] = []; } // ── set / get ───────────────────────────────────────────────────────────── public function test_set_and_get_single_field(): void { WPDO_Zone_Cold::set( 100, self::POST_TYPE, 'hp_description', 'Hello World' ); $val = WPDO_Zone_Cold::get( 100, self::POST_TYPE, 'hp_description' ); $this->assertSame( 'Hello World', $val ); } public function test_get_returns_null_for_missing_key(): void { WPDO_Zone_Cold::set( 101, self::POST_TYPE, 'hp_description', 'present' ); $val = WPDO_Zone_Cold::get( 101, self::POST_TYPE, 'missing_key' ); $this->assertNull( $val ); } public function test_get_returns_null_for_missing_post(): void { $val = WPDO_Zone_Cold::get( 9999, self::POST_TYPE, 'hp_description' ); $this->assertNull( $val ); } public function test_set_overwrites_existing_value(): void { WPDO_Zone_Cold::set( 102, self::POST_TYPE, 'hp_website', 'http://old.example.com' ); WPDO_Zone_Cold::set( 102, self::POST_TYPE, 'hp_website', 'http://new.example.com' ); $val = WPDO_Zone_Cold::get( 102, self::POST_TYPE, 'hp_website' ); $this->assertSame( 'http://new.example.com', $val ); } public function test_set_preserves_other_keys_in_blob(): void { WPDO_Zone_Cold::set( 103, self::POST_TYPE, 'hp_description', 'Keep me' ); WPDO_Zone_Cold::set( 103, self::POST_TYPE, 'hp_website', 'https://keep.example.com' ); // Update only one key. WPDO_Zone_Cold::set( 103, self::POST_TYPE, 'hp_website', 'https://updated.example.com' ); $this->assertSame( 'Keep me', WPDO_Zone_Cold::get( 103, self::POST_TYPE, 'hp_description' ) ); $this->assertSame( 'https://updated.example.com', WPDO_Zone_Cold::get( 103, self::POST_TYPE, 'hp_website' ) ); } // ── set_many / get_blob ─────────────────────────────────────────────────── public function test_set_many_stores_multiple_fields(): void { WPDO_Zone_Cold::set_many( 200, self::POST_TYPE, [ 'hp_description' => 'A great listing', 'hp_website' => 'https://example.com', 'hp_facebook' => 'https://facebook.com/test', ] ); $this->assertSame( 'A great listing', WPDO_Zone_Cold::get( 200, self::POST_TYPE, 'hp_description' ) ); $this->assertSame( 'https://example.com', WPDO_Zone_Cold::get( 200, self::POST_TYPE, 'hp_website' ) ); $this->assertSame( 'https://facebook.com/test', WPDO_Zone_Cold::get( 200, self::POST_TYPE, 'hp_facebook' ) ); } public function test_get_blob_returns_all_fields(): void { WPDO_Zone_Cold::set_many( 201, self::POST_TYPE, [ 'hp_description' => 'Blob test', 'hp_website' => 'https://blob.example.com', ] ); $blob = WPDO_Zone_Cold::get_blob( 201, self::POST_TYPE ); $this->assertIsArray( $blob ); $this->assertArrayHasKey( 'hp_description', $blob ); $this->assertArrayHasKey( 'hp_website', $blob ); $this->assertSame( 'Blob test', $blob['hp_description'] ); $this->assertSame( 'https://blob.example.com', $blob['hp_website'] ); } public function test_get_blob_returns_empty_array_for_missing_post(): void { $blob = WPDO_Zone_Cold::get_blob( 9998, self::POST_TYPE ); $this->assertIsArray( $blob ); $this->assertEmpty( $blob ); } public function test_set_many_merges_with_existing_blob(): void { WPDO_Zone_Cold::set_many( 202, self::POST_TYPE, [ 'hp_description' => 'First' ] ); WPDO_Zone_Cold::set_many( 202, self::POST_TYPE, [ 'hp_website' => 'https://merge.example.com' ] ); $this->assertSame( 'First', WPDO_Zone_Cold::get( 202, self::POST_TYPE, 'hp_description' ) ); $this->assertSame( 'https://merge.example.com', WPDO_Zone_Cold::get( 202, self::POST_TYPE, 'hp_website' ) ); } // ── remove ──────────────────────────────────────────────────────────────── public function test_remove_key_from_blob(): void { WPDO_Zone_Cold::set_many( 300, self::POST_TYPE, [ 'hp_description' => 'Keep me', 'hp_website' => 'https://remove.example.com', ] ); WPDO_Zone_Cold::remove( 300, self::POST_TYPE, 'hp_website' ); $this->assertSame( 'Keep me', WPDO_Zone_Cold::get( 300, self::POST_TYPE, 'hp_description' ) ); $this->assertNull( WPDO_Zone_Cold::get( 300, self::POST_TYPE, 'hp_website' ) ); } public function test_remove_nonexistent_key_does_not_error(): void { WPDO_Zone_Cold::set( 301, self::POST_TYPE, 'hp_description', 'Safe' ); WPDO_Zone_Cold::remove( 301, self::POST_TYPE, 'no_such_key' ); // Original key should still be intact. $this->assertSame( 'Safe', WPDO_Zone_Cold::get( 301, self::POST_TYPE, 'hp_description' ) ); } // ── delete ──────────────────────────────────────────────────────────────── public function test_delete_removes_row(): void { global $wpdb; WPDO_Zone_Cold::set( 400, self::POST_TYPE, 'hp_description', 'To be deleted' ); WPDO_Zone_Cold::delete( 400, self::POST_TYPE ); $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$table . "` WHERE post_id = 400" ); $this->assertSame( 0, $count ); $this->assertNull( WPDO_Zone_Cold::get( 400, self::POST_TYPE, 'hp_description' ) ); } public function test_delete_nonexistent_post_does_not_error(): void { WPDO_Zone_Cold::delete( 9997, self::POST_TYPE ); $this->assertTrue( true ); // Must not throw. } // ── Object Cache ────────────────────────────────────────────────────────── public function test_get_blob_populates_object_cache(): void { WPDO_Zone_Cold::set( 500, self::POST_TYPE, 'hp_description', 'Cached value' ); // Clear cache to force a DB read on the next call. $GLOBALS['_wp_cache'] = []; // First get: reads from DB, warms the cache. $val = WPDO_Zone_Cold::get( 500, self::POST_TYPE, 'hp_description' ); $this->assertSame( 'Cached value', $val ); // Cache entry must now exist. $cached = wp_cache_get( 'cold_500', 'wpdo_cold_itest' ); $this->assertIsArray( $cached ); $this->assertSame( 'Cached value', $cached['hp_description'] ); } public function test_set_invalidates_object_cache(): void { WPDO_Zone_Cold::set( 501, self::POST_TYPE, 'hp_description', 'Original' ); // Warm the cache by reading once. WPDO_Zone_Cold::get( 501, self::POST_TYPE, 'hp_description' ); $this->assertNotFalse( wp_cache_get( 'cold_501', 'wpdo_cold_itest' ) ); // Write a new value — must invalidate the cached blob. WPDO_Zone_Cold::set( 501, self::POST_TYPE, 'hp_description', 'Updated' ); $this->assertFalse( wp_cache_get( 'cold_501', 'wpdo_cold_itest' ) ); // Subsequent read must return the updated value (from DB). $val = WPDO_Zone_Cold::get( 501, self::POST_TYPE, 'hp_description' ); $this->assertSame( 'Updated', $val ); } public function test_delete_invalidates_object_cache(): void { WPDO_Zone_Cold::set( 502, self::POST_TYPE, 'hp_description', 'Will be deleted' ); // Warm the cache. WPDO_Zone_Cold::get( 502, self::POST_TYPE, 'hp_description' ); // Delete — must clear cache. WPDO_Zone_Cold::delete( 502, self::POST_TYPE ); $this->assertFalse( wp_cache_get( 'cold_502', 'wpdo_cold_itest' ) ); } // ── isolation ───────────────────────────────────────────────────────────── public function test_different_post_ids_are_independent(): void { WPDO_Zone_Cold::set( 600, self::POST_TYPE, 'hp_description', 'Post 600' ); WPDO_Zone_Cold::set( 601, self::POST_TYPE, 'hp_description', 'Post 601' ); $this->assertSame( 'Post 600', WPDO_Zone_Cold::get( 600, self::POST_TYPE, 'hp_description' ) ); $this->assertSame( 'Post 601', WPDO_Zone_Cold::get( 601, self::POST_TYPE, 'hp_description' ) ); } }