From 2df7d2f6867a3db7a7f23ca20109225525dd4491 Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Wed, 20 Jul 2022 10:06:58 +0200 Subject: [PATCH] Split integration tests after refactoring AS runner [MAILPOET-4274] --- .../ActionSchedulerTestHelper.php | 21 ++++ .../ActionScheduler/Actions/DaemonRunTest.php | 74 +++++++++++ .../Actions/DaemonTriggerTest.php | 98 +++++++++++++++ .../Cron/DaemonActionSchedulerRunnerTest.php | 119 ++---------------- 4 files changed, 203 insertions(+), 109 deletions(-) create mode 100644 mailpoet/tests/integration/Cron/ActionScheduler/ActionSchedulerTestHelper.php create mode 100644 mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonRunTest.php create mode 100644 mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonTriggerTest.php diff --git a/mailpoet/tests/integration/Cron/ActionScheduler/ActionSchedulerTestHelper.php b/mailpoet/tests/integration/Cron/ActionScheduler/ActionSchedulerTestHelper.php new file mode 100644 index 0000000000..898ca3887a --- /dev/null +++ b/mailpoet/tests/integration/Cron/ActionScheduler/ActionSchedulerTestHelper.php @@ -0,0 +1,21 @@ + ActionScheduler::GROUP_ID, + 'status' => [\ActionScheduler_Store::STATUS_PENDING, \ActionScheduler_Store::STATUS_RUNNING], + ]); + return $actions; + } + + public function getMailPoetCompleteActions(): array { + $actions = as_get_scheduled_actions([ + 'group' => ActionScheduler::GROUP_ID, + 'status' => [\ActionScheduler_Store::STATUS_COMPLETE], + ]); + return $actions; + } +} diff --git a/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonRunTest.php b/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonRunTest.php new file mode 100644 index 0000000000..8b3e3e82c3 --- /dev/null +++ b/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonRunTest.php @@ -0,0 +1,74 @@ +daemonRun = $this->diContainer->get(DaemonRun::class); + $this->actionScheduler = $this->diContainer->get(ActionScheduler::class); + $this->cleanup(); + (new ScheduledTask())->withDefaultTasks(); + $this->actionSchedulerHelper = new ActionSchedulerTestHelper(); + } + + public function testCanProcessActions(): void { + $settings = $this->diContainer->get(SettingsController::class); + $settings->set('cron_trigger.method', CronTrigger::METHOD_ACTION_SCHEDULER); + // We need configure sender so that Daemon::run doesn't fail due incomplete configuration for Mailer. + $settings->set('sender', [ + 'name' => 'John', + 'address' => 'john@example.com', + ]); + $this->daemonRun->init(); + expect($this->daemonRun->getDaemonExecutionLimit())->equals(20); // Verify initial execution limit + + $this->actionScheduler->scheduleRecurringAction(time() - 1, 100, DaemonRun::NAME); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(1); + $doneActions = $this->actionSchedulerHelper->getMailPoetCompleteActions(); + expect($doneActions)->count(0); + + // We can't call $this->daemonRun->process directly because it ends up with wp_die(); + // We must also instantiate fresh runner, because the global instance may have exhausted execution time, because it is created + // at the start of all tests + $runner = new \ActionScheduler_QueueRunner(); + $runner->run(); + + $doneActions = $this->actionSchedulerHelper->getMailPoetCompleteActions(); + expect($doneActions)->count(1); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + + // Verify execution limit after run. floor(30 - some time taken by previous action) - 10s (safety execution timout margin) + expect($this->daemonRun->getDaemonExecutionLimit())->greaterThan(0); + expect($this->daemonRun->getDaemonExecutionLimit())->lessThan(20); + } + + private function cleanup(): void { + global $wpdb; + $actionsTable = $wpdb->prefix . 'actionscheduler_actions'; + $wpdb->query('TRUNCATE ' . $actionsTable); + $claimsTable = $wpdb->prefix . 'actionscheduler_claims'; + $wpdb->query('TRUNCATE ' . $claimsTable); + $this->truncateEntity(ScheduledTaskEntity::class); + } +} diff --git a/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonTriggerTest.php b/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonTriggerTest.php new file mode 100644 index 0000000000..cacc4942bb --- /dev/null +++ b/mailpoet/tests/integration/Cron/ActionScheduler/Actions/DaemonTriggerTest.php @@ -0,0 +1,98 @@ +daemonTrigger = $this->diContainer->get(DaemonTrigger::class); + $this->cleanup(); + $this->scheduledTaskFactory = new ScheduledTask(); + $this->scheduledTaskFactory->withDefaultTasks(); + $this->actionSchedulerHelper = new ActionSchedulerTestHelper(); + } + + public function testItSchedulesTriggerActionOnInit(): void { + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + $this->daemonTrigger->init(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(1); + $action = reset($actions); + $this->assertInstanceOf(\ActionScheduler_Action::class, $action); + expect($action->get_hook())->equals(DaemonTrigger::NAME); + } + + public function testTriggerDoesNotTriggerAnythingIfThereAreNoJobs(): void { + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + $this->daemonTrigger->process(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + } + + public function testTriggerUnschedulesRunJobIfThereIsNoMoreWork(): void { + $actionScheduler = $this->diContainer->get(ActionScheduler::class); + $actionScheduler->scheduleRecurringAction(time() + 60, 1, DaemonRun::NAME); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(1); + $this->daemonTrigger->process(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + } + + public function testTriggerTriggerRunnerActionWhenThereIsJob(): void { + $this->diContainer->get(SettingsController::class)->set('cron_trigger.method', CronTrigger::METHOD_ACTION_SCHEDULER); + $this->createDueScheduledTask(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(0); + $remoteExecutorHandlerMock = $this->createMock(RemoteExecutorHandler::class); + $remoteExecutorHandlerMock->expects($this->once()) + ->method('triggerExecutor'); + $daemonTrigger = $this->getServiceWithOverrides(DaemonTrigger::class, [ + 'remoteExecutorHandler' => $remoteExecutorHandlerMock, + ]); + $daemonTrigger->process(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); + expect($actions)->count(1); + $action = reset($actions); + $this->assertInstanceOf(\ActionScheduler_Action::class, $action); + expect($action->get_hook())->equals(DaemonRun::NAME); + $this->cleanup(); + } + + private function createDueScheduledTask(): void { + $date = Carbon::now()->subSecond(); + $this->scheduledTaskFactory->create(UnsubscribeTokens::TASK_TYPE, ScheduledTaskEntity::STATUS_SCHEDULED, $date); + } + + private function cleanup(): void { + global $wpdb; + $actionsTable = $wpdb->prefix . 'actionscheduler_actions'; + $wpdb->query('TRUNCATE ' . $actionsTable); + $claimsTable = $wpdb->prefix . 'actionscheduler_claims'; + $wpdb->query('TRUNCATE ' . $claimsTable); + $this->truncateEntity(ScheduledTaskEntity::class); + } +} diff --git a/mailpoet/tests/integration/Cron/DaemonActionSchedulerRunnerTest.php b/mailpoet/tests/integration/Cron/DaemonActionSchedulerRunnerTest.php index def72dac80..fad74ac96c 100644 --- a/mailpoet/tests/integration/Cron/DaemonActionSchedulerRunnerTest.php +++ b/mailpoet/tests/integration/Cron/DaemonActionSchedulerRunnerTest.php @@ -2,36 +2,31 @@ namespace MailPoet\Cron; -use MailPoet\Cron\ActionScheduler\Actions\DaemonRun; use MailPoet\Cron\ActionScheduler\Actions\DaemonTrigger; -use MailPoet\Cron\ActionScheduler\ActionScheduler; -use MailPoet\Cron\Workers\UnsubscribeTokens; +use MailPoet\Cron\ActionScheduler\ActionSchedulerTestHelper; use MailPoet\Entities\ScheduledTaskEntity; -use MailPoet\Settings\SettingsController; -use MailPoet\Test\DataFactories\ScheduledTask; -use MailPoet\WP\Functions; -use MailPoetVendor\Carbon\Carbon; + +require_once __DIR__ . '/ActionScheduler/ActionSchedulerTestHelper.php'; class DaemonActionSchedulerRunnerTest extends \MailPoetTest { /** @var DaemonActionSchedulerRunner */ private $actionSchedulerRunner; - /** @var ScheduledTask */ - private $scheduledTaskFactory; + /** @var ActionSchedulerTestHelper */ + private $actionSchedulerHelper; public function _before(): void { $this->actionSchedulerRunner = $this->diContainer->get(DaemonActionSchedulerRunner::class); $this->cleanup(); - $this->scheduledTaskFactory = new ScheduledTask(); - $this->scheduledTaskFactory->withDefaultTasks(); + $this->actionSchedulerHelper = new ActionSchedulerTestHelper(); } public function testItSchedulesTriggerActionOnInit(): void { - $actions = $this->getMailPoetScheduledActions(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); expect($actions)->count(0); $this->actionSchedulerRunner->init(); - $actions = $this->getMailPoetScheduledActions(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); expect($actions)->count(1); $action = reset($actions); $this->assertInstanceOf(\ActionScheduler_Action::class, $action); @@ -40,107 +35,13 @@ class DaemonActionSchedulerRunnerTest extends \MailPoetTest { public function testItDeactivateAllTasks(): void { $this->actionSchedulerRunner->init(); - $actions = $this->getMailPoetScheduledActions(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); expect($actions)->count(1); $this->actionSchedulerRunner->deactivate(); - $actions = $this->getMailPoetScheduledActions(); + $actions = $this->actionSchedulerHelper->getMailPoetScheduledActions(); expect($actions)->count(0); } - public function testTriggerDoesNotTriggerAnythingIfThereAreNoJobs(): void { - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(0); - $triggerAction = $this->diContainer->get(DaemonTrigger::class); - $triggerAction->process(); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(0); - } - - public function testTriggerUnschedulesRunJobIfThereIsNoMoreWork(): void { - $actionScheduler = $this->diContainer->get(ActionScheduler::class); - $actionScheduler->scheduleRecurringAction(time() + 60, 1, DaemonRun::NAME); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(1); - $triggerAction = $this->diContainer->get(DaemonTrigger::class); - $triggerAction->process(); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(0); - } - - public function testTriggerTriggerRunnerActionWhenThereIsJob(): void { - $this->diContainer->get(SettingsController::class)->set('cron_trigger.method', CronTrigger::METHOD_ACTION_SCHEDULER); - $this->createDueScheduledTask(); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(0); - $triggerAction = $this->diContainer->get(DaemonTrigger::class); - $triggerAction->process(); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(2); - $action = reset($actions); - $this->assertInstanceOf(\ActionScheduler_Action::class, $action); - expect($action->get_hook())->equals(DaemonRun::NAME); - $this->cleanup(); - } - - public function testRunnerCanProcessActions(): void { - $settings = $this->diContainer->get(SettingsController::class); - $settings->set('cron_trigger.method', CronTrigger::METHOD_ACTION_SCHEDULER); - // We need configure sender so that Daemon::run doesn't fail due incomplete configuration for Mailer. - $settings->set('sender', [ - 'name' => 'John', - 'address' => 'john@example.com', - ]); - $runAction = $this->diContainer->get(DaemonRun::class); - // Activate filter for watching execution limit. - // This normally happens in DaemonActionSchedulerRunner::init but it can't be called in tests since it cause some background requests and made test flaky - $wp = $this->diContainer->get(Functions::class); - $wp->addFilter('action_scheduler_maximum_execution_time_likely_to_be_exceeded', [$runAction, 'storeRemainingExecutionLimit'], 10, 5); - expect($runAction->getDaemonExecutionLimit())->equals(20); // Verify initial execution limit - - $actionScheduler = $this->diContainer->get(ActionScheduler::class); - $actionScheduler->scheduleRecurringAction(time() - 1, 100, DaemonRun::NAME); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(1); - $doneActions = $this->getMailPoetCompleteActions(); - expect($doneActions)->count(0); - - // We can't call $this->actionSchedulerRunner->runActionScheduler directly because it ends up with wp_die(); - // We must also instantiate fresh runner, because the global instance may have exhausted execution time, because it is created - // at the start of all tests - $runner = new \ActionScheduler_QueueRunner(); - $runner->run(); - - $doneActions = $this->getMailPoetCompleteActions(); - expect($doneActions)->count(1); - $actions = $this->getMailPoetScheduledActions(); - expect($actions)->count(1); - - // Verify execution limit after run. floor(30 - some time taken by previous action) - 10s (safety execution timout margin) - expect($runAction->getDaemonExecutionLimit())->greaterThan(0); - expect($runAction->getDaemonExecutionLimit())->lessThan(20); - } - - private function getMailPoetScheduledActions(): array { - $actions = as_get_scheduled_actions([ - 'group' => ActionScheduler::GROUP_ID, - 'status' => [\ActionScheduler_Store::STATUS_PENDING, \ActionScheduler_Store::STATUS_RUNNING], - ]); - return $actions; - } - - private function getMailPoetCompleteActions(): array { - $actions = as_get_scheduled_actions([ - 'group' => ActionScheduler::GROUP_ID, - 'status' => [\ActionScheduler_Store::STATUS_COMPLETE], - ]); - return $actions; - } - - private function createDueScheduledTask(): void { - $date = Carbon::now()->subSecond(); - $this->scheduledTaskFactory->create(UnsubscribeTokens::TASK_TYPE, ScheduledTaskEntity::STATUS_SCHEDULED, $date); - } - private function cleanup(): void { global $wpdb; $actionsTable = $wpdb->prefix . 'actionscheduler_actions';