Files
2meet-data-optimizer/tests/integration/PostScopedRoutingTest.php
T
wpdev 3f4fe52e1b fix(2meet-data-optimizer): route post entity keys by post_type
- Entity groups can declare post_types; get_field() resolves a post's key against its own type's groups first, then unscoped ones (legacy last-wins lookup kept for callers without a post type)
- Hook Bus and the post query router pass the post type, so a listing's hp_rating / hp_status / hp_verified map to hp_listing_core instead of the vendor/request tables; core HP groups declare their post types (schema hashes unchanged)
- Behind tmdo_post_scoped_routing: on for sites without post flat rows, off where rows may sit in the wrong table until PR-034 reconciles them (dry-run: dev20–22 off, dev23/24/30/31/40 on)
- Verify: EntityRegistryScopeTest (8), PostScopedRoutingTest (4); core unit 630 / integration 443, HP 144/49, WC 10/22 OK
- PR: PR-033
2026-09-25 23:55:49 +08:00

122 lines
5.2 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Hook Bus routes post meta to the group of the post's own type.
*
* Two groups share `shared_key`; the listing group is scoped to hp_listing,
* the vendor group to hp_vendor. Before scoping, both posts' values landed
* in the vendor table (last registered wins).
*/
class PostScopedRoutingTest extends TestCase {
private const LISTING_TABLE = 'wp_itest_wpdo_post_scope_listing';
private const VENDOR_TABLE = 'wp_itest_wpdo_post_scope_vendor';
public static function setUpBeforeClass(): void {
global $wpdb;
$base = WPDO_PLUGIN_DIR;
foreach ( array(
'includes/adapters/interface-entity-adapter.php',
'includes/engine/class-tmdo-type-caster.php',
'includes/engine/class-tmdo-schema-manager.php',
'includes/engine/class-tmdo-entity-registry.php',
'includes/engine/class-tmdo-mode-manager.php',
'includes/engine/class-tmdo-cache-orchestrator.php',
'includes/adapters/class-tmdo-adapter-post.php',
'includes/engine/class-tmdo-hook-bus.php',
) as $file ) {
require_once $base . $file;
}
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
$wpdb->query(
"CREATE TABLE `{$table}` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`post_id` BIGINT(20) NOT NULL,
`shared_key` VARCHAR(255) DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_entity` (`post_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
}
}
public static function tearDownAfterClass(): void {
global $wpdb;
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
}
WPDO_Entity_Registry::init();
$ref = new ReflectionProperty( WPDO_Mode_Manager::class, 'cache' );
$ref->setAccessible( true );
$ref->setValue( null, null );
}
protected function setUp(): void {
global $wpdb;
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
$wpdb->query( "TRUNCATE TABLE `{$table}`" );
}
$GLOBALS['_wp_options'] = array( WPDO_Entity_Registry::SCOPED_ROUTING_OPTION => '1' );
$GLOBALS['_wp_cache'] = array();
$GLOBALS['_wp_post_types'] = array( 101 => 'hp_listing', 202 => 'hp_vendor', 303 => 'post' );
$GLOBALS['_wp_current_filter'] = 'update_post_metadata';
$tec = new ReflectionProperty( WPDO_Schema_Manager::class, 'table_exists_cache' );
$tec->setAccessible( true );
$tec->setValue( null, array( self::LISTING_TABLE => true, self::VENDOR_TABLE => true ) );
$l1 = new ReflectionProperty( WPDO_Cache_Orchestrator::class, 'l1_cache' );
$l1->setAccessible( true );
$l1->setValue( null, array() );
$mode = new ReflectionProperty( WPDO_Mode_Manager::class, 'cache' );
$mode->setAccessible( true );
$mode->setValue( null, array( 'post' => 'aeav_only', 'user' => 'disabled', 'term' => 'disabled', 'comment' => 'disabled' ) );
WPDO_Entity_Registry::init();
WPDO_Entity_Registry::register_adapter( 'post', new WPDO_Adapter_Post() );
WPDO_Entity_Registry::register_group( 'post', 'scope_listing', array( array( 'key' => 'shared_key', 'type' => 'text' ) ), array( 'post_types' => array( 'hp_listing' ) ) );
WPDO_Entity_Registry::register_group( 'post', 'scope_vendor', array( array( 'key' => 'shared_key', 'type' => 'text' ) ), array( 'post_types' => array( 'hp_vendor' ) ) );
}
private function value_in( string $table, int $post_id ): ?string {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "SELECT shared_key FROM `{$table}` WHERE post_id = %d", $post_id ) );
}
public function test_each_post_type_writes_to_its_own_group_table(): void {
WPDO_Hook_Bus::intercept_update( null, 101, 'shared_key', 'listing value', '' );
WPDO_Hook_Bus::intercept_update( null, 202, 'shared_key', 'vendor value', '' );
$this->assertSame( 'listing value', $this->value_in( self::LISTING_TABLE, 101 ) );
$this->assertNull( $this->value_in( self::VENDOR_TABLE, 101 ) );
$this->assertSame( 'vendor value', $this->value_in( self::VENDOR_TABLE, 202 ) );
}
public function test_reads_come_from_the_post_types_group_table(): void {
global $wpdb;
$wpdb->insert( self::LISTING_TABLE, array( 'post_id' => 101, 'shared_key' => 'from listing table' ) );
$wpdb->insert( self::VENDOR_TABLE, array( 'post_id' => 101, 'shared_key' => 'stray copy' ) );
$this->assertSame( array( 'from listing table' ), WPDO_Hook_Bus::intercept_get( null, 101, 'shared_key', true, 'post' ) );
}
public function test_other_post_types_fall_through_to_native_meta(): void {
$this->assertNull( WPDO_Hook_Bus::intercept_update( null, 303, 'shared_key', 'x', '' ) );
$this->assertNull( WPDO_Hook_Bus::intercept_get( null, 303, 'shared_key', true, 'post' ) );
}
public function test_without_the_option_routing_stays_legacy(): void {
$GLOBALS['_wp_options'][ WPDO_Entity_Registry::SCOPED_ROUTING_OPTION ] = '0';
WPDO_Hook_Bus::intercept_update( null, 101, 'shared_key', 'legacy', '' );
$this->assertSame( 'legacy', $this->value_in( self::VENDOR_TABLE, 101 ) );
$this->assertNull( $this->value_in( self::LISTING_TABLE, 101 ) );
}
}