d36bb954d1
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
137 lines
5.7 KiB
PHP
137 lines
5.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Tests for WPDO_Migration_Engine — 7-state static lifecycle controller.
|
|
*
|
|
* Validates valid/invalid transitions, rollback, and status.
|
|
* High-level operations (migrate/verify/cutover) require a registered
|
|
* migration instance and are tested at the transition level here.
|
|
*/
|
|
class MigrationEngineTest extends TestCase {
|
|
|
|
protected function setUp(): void {
|
|
$GLOBALS['_wp_options'] = [];
|
|
// Reset static migrations registry.
|
|
$ref = new ReflectionClass( WPDO_Migration_Engine::class );
|
|
$m = $ref->getProperty( 'migrations' );
|
|
$m->setAccessible( true );
|
|
$m->setValue( null, [] );
|
|
}
|
|
|
|
// ── can_transition ───────────────────────────────────────────────────────
|
|
|
|
/** @dataProvider valid_transitions_provider */
|
|
public function test_can_transition_returns_true_for_valid_paths( string $from, string $to ): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', $from );
|
|
$this->assertTrue(
|
|
WPDO_Migration_Engine::can_transition( 'hot_hp_listing', $to ),
|
|
"Expected valid transition: $from → $to"
|
|
);
|
|
}
|
|
|
|
public static function valid_transitions_provider(): array {
|
|
return [
|
|
[ 'idle', 'dual_write' ],
|
|
[ 'dual_write', 'backfill' ],
|
|
[ 'backfill', 'verify' ],
|
|
[ 'backfill', 'dual_write' ], // backfill can go back to dual_write.
|
|
[ 'verify', 'cutover' ],
|
|
[ 'verify', 'dual_write' ], // verify can step back.
|
|
[ 'cutover', 'cleanup' ],
|
|
[ 'cleanup', 'complete' ],
|
|
// Any state → idle is always allowed (rollback path).
|
|
[ 'cutover', 'idle' ],
|
|
[ 'complete', 'idle' ],
|
|
];
|
|
}
|
|
|
|
/** @dataProvider invalid_transitions_provider */
|
|
public function test_can_transition_returns_false_for_invalid_paths( string $from, string $to ): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', $from );
|
|
$this->assertFalse(
|
|
WPDO_Migration_Engine::can_transition( 'hot_hp_listing', $to ),
|
|
"Expected invalid transition: $from → $to"
|
|
);
|
|
}
|
|
|
|
public static function invalid_transitions_provider(): array {
|
|
return [
|
|
[ 'idle', 'cutover' ], // Must traverse intermediate states.
|
|
[ 'complete', 'backfill' ], // Cannot go backwards except to idle.
|
|
[ 'idle', 'complete' ],
|
|
];
|
|
}
|
|
|
|
// ── transition ───────────────────────────────────────────────────────────
|
|
|
|
public function test_transition_updates_state_on_valid_path(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'idle' );
|
|
$result = WPDO_Migration_Engine::transition( 'hot_hp_listing', 'dual_write' );
|
|
$this->assertTrue( $result );
|
|
$this->assertSame( 'dual_write', WPDO_Feature_Flags::get( 'hot_hp_listing' ) );
|
|
}
|
|
|
|
public function test_transition_returns_false_and_preserves_state_on_invalid_path(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'idle' );
|
|
$result = WPDO_Migration_Engine::transition( 'hot_hp_listing', 'complete' );
|
|
$this->assertFalse( $result );
|
|
$this->assertSame( 'idle', WPDO_Feature_Flags::get( 'hot_hp_listing' ) );
|
|
}
|
|
|
|
// ── rollback ─────────────────────────────────────────────────────────────
|
|
|
|
public function test_rollback_resets_state_to_idle(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'cutover' );
|
|
$result = WPDO_Migration_Engine::rollback( 'hot_hp_listing' );
|
|
$this->assertSame( 'idle', $result['status'] );
|
|
$this->assertSame( 'idle', WPDO_Feature_Flags::get( 'hot_hp_listing' ) );
|
|
}
|
|
|
|
public function test_rollback_from_idle_returns_idle_status(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'idle' );
|
|
$result = WPDO_Migration_Engine::rollback( 'hot_hp_listing' );
|
|
// Engine returns idle status with "already idle" message (not error).
|
|
$this->assertSame( 'idle', $result['status'] );
|
|
}
|
|
|
|
// ── status ────────────────────────────────────────────────────────────────
|
|
|
|
public function test_status_returns_current_state(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'backfill' );
|
|
$status = WPDO_Migration_Engine::status( 'hot_hp_listing' );
|
|
$this->assertSame( 'backfill', $status['state'] );
|
|
$this->assertSame( 'hot_hp_listing', $status['module'] );
|
|
}
|
|
|
|
// ── cleanup / enable require prior states ───────────────────────────────
|
|
|
|
public function test_cleanup_fails_when_not_in_cutover(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'backfill' );
|
|
$result = WPDO_Migration_Engine::cleanup( 'hot_hp_listing' );
|
|
$this->assertSame( 'error', $result['status'] );
|
|
}
|
|
|
|
public function test_enable_fails_when_not_in_cleanup(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'cutover' );
|
|
$result = WPDO_Migration_Engine::enable( 'hot_hp_listing' );
|
|
$this->assertSame( 'error', $result['status'] );
|
|
}
|
|
|
|
public function test_enable_succeeds_from_cleanup(): void {
|
|
WPDO_Feature_Flags::set( 'hot_hp_listing', 'cleanup' );
|
|
$result = WPDO_Migration_Engine::enable( 'hot_hp_listing' );
|
|
$this->assertSame( 'complete', $result['status'] );
|
|
$this->assertSame( 'complete', WPDO_Feature_Flags::get( 'hot_hp_listing' ) );
|
|
}
|
|
|
|
// ── migrate without registered migration ────────────────────────────────
|
|
|
|
public function test_migrate_without_registration_returns_error(): void {
|
|
$result = WPDO_Migration_Engine::migrate( 'hot_unregistered' );
|
|
$this->assertSame( 'error', $result['status'] );
|
|
}
|
|
}
|