feat(2meet-data-optimizer): report flat-vs-native drift with wp tmdo drift-check
- New TMDO_Drift_Check compares every backfillable group of post / user / term / comment with native meta by value (first native row → safe unserialize → Type_Caster::to_db): miss / diff / extra per key, set-based SQL with BINARY exact matches, PHP comparison only for the remaining candidates. Serialized-vs-JSON and 50 vs 50.000000 are no longer drift; over-long text stored as NULL is not a miss; extra covers rows of other post types. Outcome per group goes to the wpdo_drift_status option; nothing else is written. - `wp tmdo drift-check <entity> [--group] [--sample=<n>] [--format]`. A new class instead of extending the post-only shadow verifier, which queries row by row and writes shadow_diffs. - Verify: DriftCheckTest; live suite drift; dev36 post miss=533 diff=2 extra=1 in 23 s (checker's 45 json false diffs gone), user/term/comment 0. - PR: PR-070
This commit is contained in:
@@ -41,6 +41,7 @@ Exit code 0 when every test passed, 1 otherwise. Output per test:
|
||||
| `resync` | dual_write / shadow_read: flat follows native after every write (4 entities × 2 modes: update, same value, delete, refused writes, stale flat, key rename by meta_id) |
|
||||
| `router` | a routed WP_Query returns the same ids, in order, as native (OR, several post types, untyped numeric compares, named orderby, LIKE, keys of two groups, NOT EXISTS with the status/date copy route, meta orderby: HivePress sort shape, named clause, meta_key + meta_value_num); routed SQL has no GROUP BY |
|
||||
| `rest` | anonymous GET of the public listings endpoints returns no protected or credential-like field (1.1.1) |
|
||||
| `drift` | `TMDO_Drift_Check`: json groups show no false diff; a planted flat mismatch / flat-only value counts exactly once (1.2.0) |
|
||||
| `zero_side_effect` | zone tables that were never created are never queried on save / delete |
|
||||
|
||||
## Tools (`tools/`)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Suite: TMDO_Drift_Check on a real site (2.2 PR-070).
|
||||
*
|
||||
* - json groups (attachment / nav_menu_item) compare by value: a serialized
|
||||
* native array and its JSON flat copy are not drift.
|
||||
* - a flat value planted behind the checker's back shows up as exactly one
|
||||
* diff / one extra, nothing else moves.
|
||||
*
|
||||
* @package WP_Data_Optimizer
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Run the drift check for one group and put the site's recorded status back.
|
||||
*
|
||||
* @param string $entity Entity type.
|
||||
* @param string $group Group.
|
||||
* @return array<string, mixed> Report.
|
||||
*/
|
||||
function tmdo_live_drift( string $entity, string $group ): array {
|
||||
$status = get_option( TMDO_Drift_Check::OPTION, null );
|
||||
try {
|
||||
return TMDO_Drift_Check::run( $entity, $group );
|
||||
} finally {
|
||||
null === $status ? delete_option( TMDO_Drift_Check::OPTION ) : update_option( TMDO_Drift_Check::OPTION, $status );
|
||||
}
|
||||
}
|
||||
|
||||
TMDO_Live::test(
|
||||
'drift-check reports no diff for json groups whose values match native',
|
||||
static function () {
|
||||
$checked = 0;
|
||||
foreach ( array( 'attachment', 'nav_menu_item' ) as $group ) {
|
||||
if ( ! in_array( $group, TMDO_Entity_Migration_Engine::backfillable_groups( 'post' ), true )
|
||||
|| ! TMDO_Schema_Manager::table_exists( TMDO_Schema_Manager::get_table_name( 'post', $group ) ) ) {
|
||||
continue;
|
||||
}
|
||||
$report = tmdo_live_drift( 'post', $group );
|
||||
tmdo_live_assert_same( 0, $report['total']['diff'], "post/{$group} diff" );
|
||||
++$checked;
|
||||
}
|
||||
if ( 0 === $checked ) {
|
||||
throw new TMDO_Live_Skip( 'no attachment / nav_menu_item flat table on this site' );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'drift-check counts a planted flat mismatch once and a flat-only value once',
|
||||
static function () {
|
||||
global $wpdb;
|
||||
TMDO_Live_Fixtures::use_modes( array( 'user' => 'disabled' ) );
|
||||
$field = TMDO_Live_Fixtures::field( 'user', 'login_count' );
|
||||
$group = $field['group'];
|
||||
$table = TMDO_Schema_Manager::get_table_name( 'user', $group );
|
||||
|
||||
$before = tmdo_live_drift( 'user', $group )['groups'][ $group ]['keys']['login_count'];
|
||||
|
||||
$mismatch = TMDO_Live_Fixtures::user();
|
||||
$flat_only = TMDO_Live_Fixtures::user();
|
||||
add_user_meta( $mismatch, 'login_count', '5' );
|
||||
try {
|
||||
$wpdb->insert( $table, array( 'user_id' => $mismatch, 'login_count' => 6 ) );
|
||||
$wpdb->insert( $table, array( 'user_id' => $flat_only, 'login_count' => 9 ) );
|
||||
|
||||
$after = tmdo_live_drift( 'user', $group )['groups'][ $group ]['keys']['login_count'];
|
||||
} finally {
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM `{$table}` WHERE user_id IN (%d, %d)", $mismatch, $flat_only ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
|
||||
tmdo_live_assert_same( $before['diff'] + 1, $after['diff'], 'login_count diff +1' );
|
||||
tmdo_live_assert_same( $before['extra'] + 1, $after['extra'], 'login_count extra +1' );
|
||||
tmdo_live_assert_same( $before['miss'], $after['miss'], 'login_count miss unchanged' );
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user