Files
2meet-data-optimizer/tests/unit/MigrationEngineTest.php
T
wpdev d36bb954d1 chore: initial snapshot of 2meet-data-optimizer v0.1.0
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
2026-07-31 05:06:36 +08:00

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