Files
2meet-data-optimizer/tests/live/suites/drift.php
T
wpdev 02045c6f07 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
2026-09-26 23:56:23 +08:00

78 lines
2.8 KiB
PHP

<?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' );
}
);