fix(2meet-data-optimizer): serve native meta in shadow_read and compare flat values
- In shadow_read, TMDO_Hook_Bus::intercept_get() returned the flat value. update_metadata() reads the old value through that filter after the Hook Bus has upserted the new one, so WP saw old === new and never wrote native meta: every update in shadow_read was lost on demotion (dev36 2026-09-26). shadow_read now returns $check (native) and compares flat on a sample of reads (filter tmdo_shadow_compare_sample_rate, default 0.1). aeav_only is unchanged; the post query router still routes in shadow_read. - Live suite shadow_write: xfail markers removed, HivePress listing case added (new int key hp_rating_count); fixtures gain post() and a post_type-scoped field()/flat(). - Verify: live shadow_write + serialized 8/8 PASS (old code: 4 FAIL); unit 685 OK, integration 484 OK, phpcs (phpcs.xml) 0, stan OK - PR: PR-056
This commit is contained in:
@@ -36,7 +36,8 @@ Exit code 0 when every test passed, 1 otherwise. Output per test:
|
||||
|
||||
| Suite | Covers |
|
||||
|---|---|
|
||||
| `shadow_write` | writes in shadow_read reach native meta (xfail until PR-056) |
|
||||
| `shadow_write` | writes in shadow_read reach native meta (user, term, HivePress listing) |
|
||||
| `serialized` | array meta values are stored serialized in flat tables, never "Array" |
|
||||
|
||||
## Tools (`tools/`)
|
||||
|
||||
|
||||
@@ -89,6 +89,30 @@ final class TMDO_Live_Fixtures {
|
||||
return (int) $t['term_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a throwaway post.
|
||||
*
|
||||
* @param string $post_type Post type.
|
||||
*/
|
||||
public static function post( string $post_type ): int {
|
||||
if ( ! post_type_exists( $post_type ) ) {
|
||||
throw new TMDO_Live_Skip( "post type {$post_type} not registered on this site" );
|
||||
}
|
||||
$id = wp_insert_post(
|
||||
array(
|
||||
'post_type' => $post_type,
|
||||
'post_title' => 'tmdo-live-' . wp_generate_password( 6, false, false ),
|
||||
'post_status' => 'draft',
|
||||
),
|
||||
true
|
||||
);
|
||||
if ( is_wp_error( $id ) ) {
|
||||
throw new TMDO_Live_Failure( 'fixture post: ' . $id->get_error_message() );
|
||||
}
|
||||
self::$created[] = array( 'post', (int) $id );
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Native meta values for a key, read with SQL (no filters, no cache).
|
||||
*
|
||||
@@ -109,14 +133,15 @@ final class TMDO_Live_Fixtures {
|
||||
/**
|
||||
* Raw flat-table value for a key; 'NO-ROW' when the entity has no row.
|
||||
*
|
||||
* @param string $type Entity type.
|
||||
* @param int $id Entity ID.
|
||||
* @param string $key Meta key.
|
||||
* @param string $type Entity type.
|
||||
* @param int $id Entity ID.
|
||||
* @param string $key Meta key.
|
||||
* @param string $post_type For posts: the post type whose group owns the key.
|
||||
* @return string|null
|
||||
*/
|
||||
public static function flat( string $type, int $id, string $key ) {
|
||||
public static function flat( string $type, int $id, string $key, string $post_type = '' ) {
|
||||
global $wpdb;
|
||||
$field = self::field( $type, $key );
|
||||
$field = self::field( $type, $key, $post_type );
|
||||
$table = TMDO_Schema_Manager::get_table_name( $type, $field['group'] );
|
||||
$col = TMDO_Schema_Manager::sanitize_column_name( $key );
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table/column come from the registry.
|
||||
@@ -127,11 +152,24 @@ final class TMDO_Live_Fixtures {
|
||||
/**
|
||||
* Registry field for a key, or skip the test when the site does not register it.
|
||||
*
|
||||
* @param string $type Entity type.
|
||||
* @param string $key Meta key.
|
||||
* A key can live in several post groups (hp_rating in listing and vendor
|
||||
* groups); pass the post type to pick the group scoped to it.
|
||||
*
|
||||
* @param string $type Entity type.
|
||||
* @param string $key Meta key.
|
||||
* @param string $post_type For posts: the post type whose group owns the key.
|
||||
*/
|
||||
public static function field( string $type, string $key ): array {
|
||||
public static function field( string $type, string $key, string $post_type = '' ): array {
|
||||
$field = TMDO_Entity_Registry::get_field( $type, $key );
|
||||
if ( '' !== $post_type ) {
|
||||
foreach ( TMDO_Entity_Registry::get_groups_for_type( $type ) as $group ) {
|
||||
if ( in_array( $post_type, TMDO_Entity_Registry::get_group_post_types( $type, $group ), true )
|
||||
&& in_array( $key, TMDO_Entity_Registry::get_group_keys( $type, $group ), true ) ) {
|
||||
$field = array( 'group' => $group ) + (array) $field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! $field ) {
|
||||
throw new TMDO_Live_Skip( "{$type} key {$key} is not registered on this site" );
|
||||
}
|
||||
@@ -151,6 +189,8 @@ final class TMDO_Live_Fixtures {
|
||||
wp_delete_user( $c[1] );
|
||||
} elseif ( 'term' === $c[0] ) {
|
||||
wp_delete_term( $c[1], $c[2] );
|
||||
} elseif ( 'post' === $c[0] ) {
|
||||
wp_delete_post( $c[1], true );
|
||||
}
|
||||
}
|
||||
self::$created = array();
|
||||
|
||||
@@ -41,8 +41,7 @@ TMDO_Live::test(
|
||||
|
||||
tmdo_live_assert_true( false !== $ret, 'update_user_meta returned false' );
|
||||
tmdo_live_assert_same( array( 'After' ), TMDO_Live_Fixtures::native( 'user', $uid, 'first_name' ), 'native first_name' );
|
||||
},
|
||||
array( 'xfail' => 'PR-056' )
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
@@ -59,8 +58,7 @@ TMDO_Live::test(
|
||||
wp_cache_delete( $uid, 'user_meta' );
|
||||
|
||||
tmdo_live_assert_same( 'After', get_user_meta( $uid, 'last_name', true ), 'last_name after demotion' );
|
||||
},
|
||||
array( 'xfail' => 'PR-056' )
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
@@ -76,6 +74,23 @@ TMDO_Live::test(
|
||||
|
||||
tmdo_live_assert_true( false !== $ret, 'update_term_meta returned false' );
|
||||
tmdo_live_assert_same( array( 'heart' ), TMDO_Live_Fixtures::native( 'term', $tid, 'hp_icon' ), 'native hp_icon' );
|
||||
},
|
||||
array( 'xfail' => 'PR-056' )
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'shadow_read: HivePress listing hp_rating_count (new int key) reaches native',
|
||||
static function () {
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating_count', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'dual_write' ) );
|
||||
$lid = TMDO_Live_Fixtures::post( 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
|
||||
// HivePress writes the recomputed count as an int; the flat read cast it
|
||||
// to the same int, so the old code skipped the native write.
|
||||
$ret = update_post_meta( $lid, 'hp_rating_count', 1 );
|
||||
|
||||
tmdo_live_assert_true( false !== $ret, 'update_post_meta returned false' );
|
||||
tmdo_live_assert_same( array( '1' ), TMDO_Live_Fixtures::native( 'post', $lid, 'hp_rating_count' ), 'native hp_rating_count' );
|
||||
tmdo_live_assert_same( '1', TMDO_Live_Fixtures::flat( 'post', $lid, 'hp_rating_count', 'hp_listing' ), 'flat hp_rating_count' );
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user