getProperty( 'cache' ); $prop->setAccessible( true ); $prop->setValue( null, null ); $this->setup_wpdb_mock(); } private function setup_wpdb_mock(): void { global $wpdb; $wpdb = new class { public string $prefix = 'wp_'; public string $postmeta = 'wp_postmeta'; public string $options = 'wp_options'; public function prepare( string $sql, ...$args ): string { return $sql; } public function get_var( string $sql ) { return '0'; } public function get_results( string $sql, $output = ARRAY_A ): array { return array(); } }; } public function test_default_disabled(): void { $this->assertFalse( WPDO_FSM_Automator::is_enabled() ); } public function test_run_returns_disabled_when_off(): void { $result = WPDO_FSM_Automator::run(); $this->assertFalse( $result['ok'] ); $this->assertContains( 'automator disabled', $result['errors'] ); } public function test_run_blocked_by_critical_cool_off(): void { // Enable + simulate critical alert in last run. update_option( 'wpdo_automator_enabled', '1', false ); update_option( WPDO_Health_Cron::OPTION_LAST_RUN, array( 'critical_count' => 2, 'ran_at' => gmdate( 'Y-m-d H:i:s' ), ), false ); $result = WPDO_FSM_Automator::run(); $this->assertFalse( $result['ok'] ); $this->assertStringContainsString( 'cool-off', $result['errors'][0] ); } public function test_run_proceeds_when_no_critical(): void { update_option( 'wpdo_automator_enabled', '1', false ); update_option( WPDO_Health_Cron::OPTION_LAST_RUN, array( 'critical_count' => 0, 'ran_at' => gmdate( 'Y-m-d H:i:s' ), ), false ); $result = WPDO_FSM_Automator::run(); $this->assertTrue( $result['ok'] ); // Most modules will be in idle and Advisor recommends WAIT (soak time); // expected that 0 actions are executed in baseline test. $this->assertGreaterThanOrEqual( 0, $result['executed'] ); } public function test_blacklist_skips_module(): void { update_option( 'wpdo_automator_enabled', '1', false ); update_option( 'wpdo_automator_blacklist', array( 'reviews', 'wc_orders' ), false ); update_option( WPDO_Health_Cron::OPTION_LAST_RUN, array( 'critical_count' => 0, 'ran_at' => gmdate( 'Y-m-d H:i:s' ), ), false ); $blacklist = WPDO_FSM_Automator::blacklist(); $this->assertContains( 'reviews', $blacklist ); $this->assertContains( 'wc_orders', $blacklist ); $result = WPDO_FSM_Automator::run(); $this->assertTrue( $result['ok'] ); // Skipped at least the 2 blacklisted ones. $this->assertGreaterThanOrEqual( 2, $result['skipped'] ); } public function test_forbidden_transitions_constant(): void { $ref = new ReflectionClass( WPDO_FSM_Automator::class ); $constant = $ref->getReflectionConstant( 'FORBIDDEN_TRANSITIONS' ); $this->assertNotNull( $constant ); $value = $constant->getValue(); $this->assertContains( array( 'verify', 'cutover' ), $value ); $this->assertContains( array( 'cutover', 'cleanup' ), $value ); $this->assertContains( array( 'cleanup', 'complete' ), $value ); } public function test_options_keys_match_admin_form(): void { $this->assertSame( 'wpdo_automator_enabled', WPDO_FSM_Automator::OPT_ENABLED ); $this->assertSame( 'wpdo_automator_blacklist', WPDO_FSM_Automator::OPT_BLACKLIST ); $this->assertSame( 'wpdo_automator_last_action', WPDO_FSM_Automator::OPT_LAST_ACTION ); } public function test_last_actions_returns_array(): void { $this->assertSame( array(), WPDO_FSM_Automator::last_actions() ); update_option( 'wpdo_automator_last_action', array( 'reviews' => '2026-04-28 04:30:00' ), false ); $last = WPDO_FSM_Automator::last_actions(); $this->assertSame( '2026-04-28 04:30:00', $last['reviews'] ); } }