fix(2meet-data-optimizer): restore native meta without overwriting newer values or inventing defaults
- TMDO_Native_Restorer::run() gains a policy: overwrite (flat wins; the default in aeav_only, where writes only reached flat and native is stale) or only_missing (native wins, flat only fills gaps; the default in every other mode, where native is the source of truth and may be newer — on dev36 an aeav_only round trip overwrote WooCommerce stock 42 with 0). CLI: --overwrite / --only-missing. - Native values are read first and each owned key is classified as missing / differs / same / default_skipped; unchanged values are no longer rewritten. A flat value equal to its field default is not written for an entity without a native row (dev36: 1,368 invented session_count / account_flags rows). --dry-run reports the counts. - Verify: NativeRestorer* 17 OK (5 new: policy by mode, forced overwrite, only-missing in aeav_only, dry-run counts, no invented defaults); unit 687 OK, integration 490 OK, phpcs (phpcs.xml) 0, stan OK; dev36 `bridge-restore-native user --dry-run`: policy only_missing, activity 120 defaults skipped - PR: PR-058
This commit is contained in:
@@ -139,4 +139,77 @@ class NativeRestorerTest extends TestCase {
|
||||
|
||||
$this->assertSame( array( '9' ), $this->native( 1, 'level' ) );
|
||||
}
|
||||
|
||||
private function set_user_mode( string $mode ): void {
|
||||
$GLOBALS['_wp_options']['wpdo_bridge_modes'] = array( 'user' => $mode );
|
||||
TMDO_Mode_Manager::reset_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Native is the source of truth outside aeav_only (e.g. WooCommerce writes
|
||||
* stock with direct SQL): the default policy there only fills gaps.
|
||||
*/
|
||||
public function test_native_authoritative_modes_only_fill_missing_rows(): void {
|
||||
$this->set_user_mode( 'dual_write' );
|
||||
|
||||
$report = TMDO_Native_Restorer::run( 'user', false );
|
||||
|
||||
$this->assertSame( array( '1' ), $this->native( 1, 'level' ), 'differing native value kept' );
|
||||
$this->assertSame( array( serialize( array( 'a', 'b' ) ) ), $this->native( 1, 'tags' ), 'missing native row filled' );
|
||||
$this->assertSame( array( '5' ), $this->native( 2, 'level' ) );
|
||||
$this->assertSame( array( 'stale' ), $this->native( 2, 'tags' ), 'NULL flat value does not delete native here' );
|
||||
$this->assertSame( 'only_missing', $report['policy'] );
|
||||
$this->assertSame( 2, $report['groups']['rn_profile']['missing'] );
|
||||
$this->assertSame( 2, $report['groups']['rn_profile']['differs'] );
|
||||
}
|
||||
|
||||
public function test_overwrite_policy_replaces_native_in_any_mode(): void {
|
||||
$this->set_user_mode( 'dual_write' );
|
||||
|
||||
TMDO_Native_Restorer::run( 'user', false, 500, false, '', 'overwrite' );
|
||||
|
||||
$this->assertSame( array( '3' ), $this->native( 1, 'level' ) );
|
||||
$this->assertSame( array(), $this->native( 2, 'tags' ) );
|
||||
}
|
||||
|
||||
public function test_only_missing_policy_keeps_native_in_aeav_only(): void {
|
||||
TMDO_Native_Restorer::run( 'user', false, 500, false, '', 'only_missing' );
|
||||
|
||||
$this->assertSame( array( '1' ), $this->native( 1, 'level' ) );
|
||||
$this->assertSame( array( serialize( array( 'a', 'b' ) ) ), $this->native( 1, 'tags' ) );
|
||||
}
|
||||
|
||||
public function test_dry_run_classifies_without_writing(): void {
|
||||
$report = TMDO_Native_Restorer::run( 'user', true );
|
||||
|
||||
$this->assertSame( 'overwrite', $report['policy'], 'aeav_only: flat is the source of truth' );
|
||||
$this->assertSame( 2, $report['groups']['rn_profile']['missing'] );
|
||||
$this->assertSame( 2, $report['groups']['rn_profile']['differs'] );
|
||||
$this->assertSame( 0, $report['groups']['rn_profile']['same'] );
|
||||
$this->assertSame( array( '1' ), $this->native( 1, 'level' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* A flat column holding its field default for an entity that never had
|
||||
* the key (e.g. activity.session_count = 0) is not written back.
|
||||
*/
|
||||
public function test_defaults_are_not_invented_as_native_rows(): void {
|
||||
global $wpdb;
|
||||
$table = 'wp_itest_wpdo_user_rn_counts';
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
|
||||
$wpdb->query( "CREATE TABLE `{$table}` ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, visits BIGINT(20) DEFAULT 0, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_entity (user_id) ) ENGINE=InnoDB" );
|
||||
$wpdb->query( "INSERT INTO `{$table}` (user_id, visits, updated_at) VALUES (1, 0, '2020-01-01'), (2, 7, '2020-01-01')" );
|
||||
WPDO_Entity_Registry::register_group( 'user', 'rn_counts', array( array( 'key' => 'visits', 'type' => 'integer', 'default' => 0 ) ) );
|
||||
WPDO_Schema_Manager::flush_table_exists_cache();
|
||||
|
||||
try {
|
||||
$report = TMDO_Native_Restorer::run( 'user', false, 500, false, 'rn_counts' );
|
||||
|
||||
$this->assertSame( array(), $this->native( 1, 'visits' ), 'default 0 not invented' );
|
||||
$this->assertSame( array( '7' ), $this->native( 2, 'visits' ) );
|
||||
$this->assertSame( 1, $report['groups']['rn_counts']['default_skipped'] );
|
||||
} finally {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user