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