diff --git a/.travis.yml b/.travis.yml index a9929d7..278c510 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ before_script: - STORAGE=doctrine tests/app/console doctrine:schema:create script: - - phpunit -c phpunit.xml.dist --coverage-clover=coverage.clover + - vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover=coverage.clover after_script: - if [[ $CODE_COVERAGE == 'true' ]]; then wget https://scrutinizer-ci.com/ocular.phar ; fi diff --git a/UPGRADE.md b/UPGRADE.md index f95553c..25a2b82 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,8 +1,18 @@ UPGRADE ======= +- [1.1.0](#1.1.0) - [0.4.0](#0.4.0) +### 1.1.0 + +In the database table `ta_tasks` a new field was introduced. Run following +command to update the table. + +```bash +bin/console doctrine:schema:update +``` + ### 0.4.0 #### Identifier of tasks and executions diff --git a/composer.json b/composer.json index dd1e232..37c18da 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": "~5.5 || ~7.0", - "php-task/php-task": "^1.0", + "php-task/php-task": "dev-develop", "symfony/http-kernel": "^2.6 || ^3.0", "symfony/dependency-injection": "^2.6 || ^3.0", "symfony/config": "^2.6 || ^3.0", diff --git a/src/Builder/NotSupportedMethodException.php b/src/Builder/NotSupportedMethodException.php new file mode 100644 index 0000000..12e7201 --- /dev/null +++ b/src/Builder/NotSupportedMethodException.php @@ -0,0 +1,64 @@ +property = $property; + $this->task = $task; + } + + /** + * Returns property. + * + * @return string + */ + public function getProperty() + { + return $this->property; + } + + /** + * Returns task. + * + * @return TaskInterface + */ + public function getTask() + { + return $this->task; + } +} diff --git a/src/Builder/TaskBuilder.php b/src/Builder/TaskBuilder.php new file mode 100644 index 0000000..8b6e3e6 --- /dev/null +++ b/src/Builder/TaskBuilder.php @@ -0,0 +1,42 @@ +task instanceof Task) { + throw new NotSupportedMethodException('systemKey', $this->task); + } + + $this->task->setSystemKey($systemKey); + + return $this; + } +} diff --git a/src/Builder/TaskBuilderFactory.php b/src/Builder/TaskBuilderFactory.php new file mode 100644 index 0000000..aed0a26 --- /dev/null +++ b/src/Builder/TaskBuilderFactory.php @@ -0,0 +1,30 @@ +systemTasks = $systemTasks; + $this->scheduler = $scheduler; + $this->taskRepository = $taskRepository; + $this->taskExecutionRepository = $taskExecutionRepository; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setDescription('Schedule system-tasks')->setHelp( + <<<'EOT' +The %command.name% command schedules configured system tasks. + + $ %command.full_name% + +You can configure them by extending the task.system_task array in your config file. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln(sprintf('Schedule %s system-tasks:', count($this->systemTasks))); + $output->writeln(''); + + foreach ($this->systemTasks as $systemKey => $systemTask) { + try { + $this->processSystemTask($systemKey, $systemTask, $output); + } catch (\Exception $exception) { + $output->writeln( + sprintf( + ' * System-task "%s" failed because of: %s', + $systemKey, + $exception->getMessage() + ) + ); + } + } + + foreach ($this->taskRepository->findSystemTasks() as $task) { + if (!in_array($task->getSystemKey(), array_keys($this->systemTasks)) && ($this->disableTask($task))) { + $output->writeln( + sprintf(' * System-task "%s" was disabled', $task->getSystemKey()) + ); + } + } + + $output->writeln(''); + $output->writeln('System-tasks successfully scheduled'); + } + + /** + * Process single system task. + * + * @param string $systemKey + * @param array $systemTask + * @param OutputInterface $output + */ + private function processSystemTask($systemKey, array $systemTask, OutputInterface $output) + { + if (!$systemTask['enabled']) { + if ($this->disableSystemTask($systemKey)) { + $output->writeln(sprintf(' * System-task "%s" was disabled', $systemKey)); + } + + return; + } + + if ($task = $this->taskRepository->findBySystemKey($systemKey)) { + $this->updateTask($systemKey, $systemTask, $task); + + $output->writeln(sprintf(' * System-task "%s" was updated', $systemKey)); + + return; + } + + /** @var TaskBuilder $builder */ + $builder = $this->scheduler->createTask($systemTask['handler_class'], $systemTask['workload']); + $builder->setSystemKey($systemKey); + if ($systemTask['cron_expression']) { + $builder->cron($systemTask['cron_expression']); + } + + $builder->schedule(); + + $output->writeln(sprintf(' * System-task "%s" was created', $systemKey)); + } + + /** + * Disable task identified by system-key. + * + * @param string $systemKey + * + * @return bool + */ + private function disableSystemTask($systemKey) + { + if (!$task = $this->taskRepository->findBySystemKey($systemKey)) { + return false; + } + + $this->disableTask($task); + + return true; + } + + /** + * Disable given task identified. + * + * @param TaskInterface $task + * + * @return bool + */ + public function disableTask(TaskInterface $task) + { + $task->setInterval($task->getInterval(), $task->getFirstExecution(), new \DateTime()); + + return $this->abortPending($task); + } + + /** + * Update given task. + * + * @param string $systemKey + * @param array $systemTask + * @param TaskInterface $task + */ + private function updateTask($systemKey, array $systemTask, TaskInterface $task) + { + if ($task->getHandlerClass() !== $systemTask['handler_class'] + || $task->getWorkload() !== $systemTask['workload'] + ) { + throw new \InvalidArgumentException( + sprintf('No update of handle-class or workload is supported for system-task "%s".', $systemKey) + ); + } + + if ($task->getInterval() === $systemTask['cron_expression']) { + return; + } + + $task->setInterval(CronExpression::factory($systemTask['cron_expression']), $task->getFirstExecution()); + + $this->abortPending($task); + $this->scheduler->scheduleTasks(); + } + + /** + * Abort pending execution for given task. + * + * @param TaskInterface $task + * + * @return bool + */ + private function abortPending(TaskInterface $task) + { + if (!$execution = $this->taskExecutionRepository->findPending($task)) { + return false; + } + + $execution->setStatus(TaskStatus::ABORTED); + $this->taskExecutionRepository->save($execution); + + return true; + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5997ef2..7e8ba92 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -49,6 +49,16 @@ public function getConfigTreeBuilder() ->end() ->end() ->end() + ->arrayNode('system_tasks') + ->prototype('array') + ->children() + ->booleanNode('enabled')->defaultTrue()->end() + ->scalarNode('handler_class')->end() + ->variableNode('workload')->defaultNull()->end() + ->scalarNode('cron_expression')->end() + ->end() + ->end() + ->end() ->end(); return $treeBuilder; diff --git a/src/DependencyInjection/TaskExtension.php b/src/DependencyInjection/TaskExtension.php index 9fd77aa..c22ec65 100644 --- a/src/DependencyInjection/TaskExtension.php +++ b/src/DependencyInjection/TaskExtension.php @@ -34,6 +34,9 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); + $container->setParameter('task.system_tasks', $config['system_tasks']); + $container->setParameter('task.storage', $config['storage']); + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load(sprintf('storage/%s.xml', $config['storage'])); $loader->load('task_event_listener.xml'); diff --git a/src/Entity/Task.php b/src/Entity/Task.php index 24a1fd7..9dbafa2 100644 --- a/src/Entity/Task.php +++ b/src/Entity/Task.php @@ -24,6 +24,11 @@ class Task extends BaseTask */ private $intervalExpression; + /** + * @var string + */ + private $systemKey; + /** * @return mixed */ @@ -32,6 +37,9 @@ public function getIntervalExpression() return $this->intervalExpression; } + /** + * {@inheritdoc} + */ public function getInterval() { if (null === $this->interval && null !== $this->intervalExpression) { @@ -50,4 +58,28 @@ public function setInterval(CronExpression $interval, \DateTime $firstExecution $this->intervalExpression = $interval->getExpression(); } + + /** + * Returns system-key. + * + * @return string + */ + public function getSystemKey() + { + return $this->systemKey; + } + + /** + * Set system-key. + * + * @param string $systemKey + * + * @return $this + */ + public function setSystemKey($systemKey) + { + $this->systemKey = $systemKey; + + return $this; + } } diff --git a/src/Entity/TaskRepository.php b/src/Entity/TaskRepository.php index 9970a0d..026762b 100644 --- a/src/Entity/TaskRepository.php +++ b/src/Entity/TaskRepository.php @@ -12,6 +12,7 @@ namespace Task\TaskBundle\Entity; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NoResultException; use Task\Storage\TaskRepositoryInterface; use Task\TaskInterface; @@ -97,4 +98,37 @@ public function findEndBefore(\DateTime $dateTime) ->getQuery() ->getResult(); } + + /** + * Returns task identified by system-key. + * + * @param string $systemKey + * + * @return TaskInterface + */ + public function findBySystemKey($systemKey) + { + try { + return $this->createQueryBuilder('t') + ->where('t.systemKey = :systemKey') + ->setParameter('systemKey', $systemKey) + ->getQuery() + ->getSingleResult(); + } catch (NoResultException $exception) { + return; + } + } + + /** + * Returns all system-task. + * + * @return TaskInterface[] + */ + public function findSystemTasks() + { + return $this->createQueryBuilder('t') + ->where('t.systemKey IS NOT NULL') + ->getQuery() + ->getResult(); + } } diff --git a/src/Resources/config/doctrine/Task.orm.xml b/src/Resources/config/doctrine/Task.orm.xml index b3e06f5..2c6ddca 100644 --- a/src/Resources/config/doctrine/Task.orm.xml +++ b/src/Resources/config/doctrine/Task.orm.xml @@ -17,6 +17,7 @@ + diff --git a/src/Resources/config/scheduler.xml b/src/Resources/config/scheduler.xml index 9ccad0f..983dd6a 100644 --- a/src/Resources/config/scheduler.xml +++ b/src/Resources/config/scheduler.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + diff --git a/src/Resources/config/storage/doctrine.xml b/src/Resources/config/storage/doctrine.xml index fb22e29..731a509 100644 --- a/src/Resources/config/storage/doctrine.xml +++ b/src/Resources/config/storage/doctrine.xml @@ -16,5 +16,15 @@ TaskBundle:TaskExecution + + + task:schedule:system-tasks + %task.system_tasks% + + + + + + diff --git a/tests/Functional/BaseDatabaseTestCase.php b/tests/Functional/BaseDatabaseTestCase.php index 7fefd31..92cffd3 100644 --- a/tests/Functional/BaseDatabaseTestCase.php +++ b/tests/Functional/BaseDatabaseTestCase.php @@ -7,6 +7,10 @@ use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Task\TaskBundle\Entity\Task; +use Task\TaskBundle\Entity\TaskExecution; +use Task\TaskInterface; +use Task\TaskStatus; /** * Extends kernel-test-case with additional functions/properties. @@ -52,4 +56,33 @@ protected function purgeDatabase() $connection->executeUpdate('SET foreign_key_checks = 1;'); } } + + /** + * Create a new task. + * + * @param string $handlerClass + * + * @return Task + */ + protected function createTask($handlerClass = TestHandler::class) + { + return new Task($handlerClass); + } + + /** + * Create a new task-execution. + * + * @param TaskInterface $task + * @param \DateTime $scheduleTime + * @param string $status + * + * @return TaskExecution + */ + protected function createTaskExecution(TaskInterface $task, \DateTime $scheduleTime, $status = TaskStatus::PLANNED) + { + $execution = new TaskExecution($task, $task->getHandlerClass(), $scheduleTime); + $execution->setStatus($status); + + return $execution; + } } diff --git a/tests/Functional/Command/RunHandlerCommandTest.php b/tests/Functional/Command/RunHandlerCommandTest.php index ddc9f7b..d3a5dee 100644 --- a/tests/Functional/Command/RunHandlerCommandTest.php +++ b/tests/Functional/Command/RunHandlerCommandTest.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Functional\Command; +namespace Task\TaskBundle\Functional\Command; use Symfony\Component\Console\Output\OutputInterface; use Task\TaskBundle\Tests\Functional\BaseCommandTestCase; diff --git a/tests/Functional/Command/ScheduleSystemTasksCommandTest.php b/tests/Functional/Command/ScheduleSystemTasksCommandTest.php new file mode 100644 index 0000000..3c935a0 --- /dev/null +++ b/tests/Functional/Command/ScheduleSystemTasksCommandTest.php @@ -0,0 +1,44 @@ +getContainer()->getParameter('kernel.storage') !== 'doctrine') { + return $this->markTestSkipped('This testcase will only be called for doctrine storage.'); + } + + parent::setUp(); + } + + public function testExecute() + { + $this->commandTester->execute( + [ + 'command' => $this->command->getName(), + ] + ); + + $output = $this->commandTester->getDisplay(); + $this->assertContains('System-tasks successfully scheduled', $output); + + $taskRepository = self::$kernel->getContainer()->get('task.repository.task'); + $this->assertNotNull($taskRepository->findBySystemKey('testing')); + } + + /** + * Returns command. + * + * @return Command + */ + protected function getCommand() + { + return self::$kernel->getContainer()->get('task.command.schedule_system_tasks'); + } +} diff --git a/tests/Functional/Command/ScheduleTaskCommandTest.php b/tests/Functional/Command/ScheduleTaskCommandTest.php index b88696a..85d1451 100644 --- a/tests/Functional/Command/ScheduleTaskCommandTest.php +++ b/tests/Functional/Command/ScheduleTaskCommandTest.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Functional\Command; +namespace Task\TaskBundle\Functional\Command; use Task\TaskBundle\Tests\Functional\BaseCommandTestCase; use Task\TaskBundle\Tests\Functional\TestHandler; diff --git a/tests/Functional/Entity/TaskExecutionRepositoryTest.php b/tests/Functional/Entity/TaskExecutionRepositoryTest.php index 5a11391..f5cb54e 100644 --- a/tests/Functional/Entity/TaskExecutionRepositoryTest.php +++ b/tests/Functional/Entity/TaskExecutionRepositoryTest.php @@ -6,9 +6,7 @@ use Task\Storage\TaskExecutionRepositoryInterface; use Task\Storage\TaskRepositoryInterface; use Task\TaskBundle\Entity\Task; -use Task\TaskBundle\Entity\TaskExecution; use Task\TaskBundle\Tests\Functional\BaseDatabaseTestCase; -use Task\TaskBundle\Tests\Functional\TestHandler; use Task\TaskInterface; use Task\TaskStatus; @@ -197,33 +195,4 @@ private function save(TaskInterface $task = null, \DateTime $scheduleTime = null return $execution; } - - /** - * Create a new task. - * - * @param string $handlerClass - * - * @return TaskInterface - */ - private function createTask($handlerClass = TestHandler::class) - { - return new Task($handlerClass); - } - - /** - * Create a new task-execution. - * - * @param TaskInterface $task - * @param \DateTime $scheduleTime - * @param string $status - * - * @return TaskExecutionInterface - */ - private function createTaskExecution(TaskInterface $task, \DateTime $scheduleTime, $status = TaskStatus::PLANNED) - { - $execution = new TaskExecution($task, $task->getHandlerClass(), $scheduleTime); - $execution->setStatus($status); - - return $execution; - } } diff --git a/tests/Functional/Entity/TaskRepositoryTest.php b/tests/Functional/Entity/TaskRepositoryTest.php new file mode 100644 index 0000000..760e02f --- /dev/null +++ b/tests/Functional/Entity/TaskRepositoryTest.php @@ -0,0 +1,66 @@ +taskRepository = self::$kernel->getContainer()->get('task.storage.task'); + } + + public function testFindBySystemKey() + { + if (self::$kernel->getContainer()->getParameter('kernel.storage') !== 'doctrine') { + return $this->markTestSkipped('This testcase will only be called for doctrine storage.'); + } + + $task = $this->createTask(); + $task->setSystemKey('test'); + + $this->taskRepository->save($task); + + $result = $this->taskRepository->findBySystemKey('test'); + $this->assertEquals($task->getUuid(), $result->getUuid()); + } + + public function testFindBySystemKeyNotFound() + { + if (self::$kernel->getContainer()->getParameter('kernel.storage') !== 'doctrine') { + return $this->markTestSkipped('This testcase will only be called for doctrine storage.'); + } + + $task = $this->createTask(); + $this->taskRepository->save($task); + + $this->assertNull($this->taskRepository->findBySystemKey('test')); + } + + public function testFindSystemTasks() + { + if (self::$kernel->getContainer()->getParameter('kernel.storage') !== 'doctrine') { + return $this->markTestSkipped('This testcase will only be called for doctrine storage.'); + } + + $task1 = $this->createTask(); + $task1->setSystemKey('test'); + $this->taskRepository->save($task1); + + $task2 = $this->createTask(); + $this->taskRepository->save($task2); + + $result = $this->taskRepository->findSystemTasks(); + $this->assertCount(1, $result); + $this->assertEquals($task1->getUuid(), $result[0]->getUuid()); + } +} diff --git a/tests/Unit/Builder/TaskBuilderFactoryTest.php b/tests/Unit/Builder/TaskBuilderFactoryTest.php new file mode 100644 index 0000000..edb6f9d --- /dev/null +++ b/tests/Unit/Builder/TaskBuilderFactoryTest.php @@ -0,0 +1,24 @@ +prophesize(TaskInterface::class); + $scheduler = $this->prophesize(TaskSchedulerInterface::class); + + $taskBuilderFactory = new TaskBuilderFactory(); + + $this->assertInstanceOf( + TaskBuilder::class, + $taskBuilderFactory->createTaskBuilder($task->reveal(), $scheduler->reveal()) + ); + } +} diff --git a/tests/Unit/Builder/TaskBuilderTest.php b/tests/Unit/Builder/TaskBuilderTest.php new file mode 100644 index 0000000..24a7b23 --- /dev/null +++ b/tests/Unit/Builder/TaskBuilderTest.php @@ -0,0 +1,34 @@ +prophesize(Task::class); + $scheduler = $this->prophesize(TaskSchedulerInterface::class); + + $taskBuilder = new TaskBuilder($task->reveal(), $scheduler->reveal()); + $taskBuilder->setSystemKey('test'); + + $task->setSystemKey('test')->shouldBeCalled(); + } + + public function testSetSystemKeyNotSupported() + { + $this->setExpectedException(NotSupportedMethodException::class); + + $task = $this->prophesize(TaskInterface::class); + $scheduler = $this->prophesize(TaskSchedulerInterface::class); + + $taskBuilder = new TaskBuilder($task->reveal(), $scheduler->reveal()); + $taskBuilder->setSystemKey('test'); + } +} diff --git a/tests/Unit/Command/ScheduleSystemTasksCommandTest.php b/tests/Unit/Command/ScheduleSystemTasksCommandTest.php new file mode 100644 index 0000000..23e2e93 --- /dev/null +++ b/tests/Unit/Command/ScheduleSystemTasksCommandTest.php @@ -0,0 +1,293 @@ +scheduler = $this->prophesize(TaskSchedulerInterface::class); + $this->taskRepository = $this->prophesize(TaskRepository::class); + $this->taskExecutionRepository = $this->prophesize(TaskExecutionRepositoryInterface::class); + } + + /** + * @param array $systemTasks + * + * @return ScheduleSystemTasksCommand + */ + protected function createCommand(array $systemTasks) + { + return new ScheduleSystemTasksCommand( + 'task:schedule:system-tasks', + $systemTasks, + $this->scheduler->reveal(), + $this->taskRepository->reveal(), + $this->taskExecutionRepository->reveal() + ); + } + + public function testExecute() + { + $command = $this->createCommand( + [ + 'testing' => [ + 'enabled' => true, + 'handler_class' => TestHandler::class, + 'workload' => 'test', + 'cron_expression' => '* * * * *', + ], + ] + ); + + $task = $this->prophesize(Task::class); + $task->getSystemKey()->willReturn('testing'); + + $taskBuilder = $this->prophesize(TaskBuilder::class); + + $this->taskRepository->findBySystemKey('testing')->willReturn(null); + $this->taskRepository->findSystemTasks()->willReturn([$task->reveal()]); + + $this->scheduler->createTask(TestHandler::class, 'test')->shouldBeCalled()->willReturn($taskBuilder->reveal()); + + $taskBuilder->setSystemKey('testing')->shouldBeCalled(); + $taskBuilder->cron('* * * * *')->shouldBeCalled(); + $taskBuilder->schedule()->shouldBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } + + public function testExecuteMultiple() + { + $command = $this->createCommand( + [ + 'testing-1' => [ + 'enabled' => true, + 'handler_class' => TestHandler::class, + 'workload' => 'test-1', + 'cron_expression' => '* * * * *', + ], + 'testing-2' => [ + 'enabled' => true, + 'handler_class' => TestHandler::class, + 'workload' => 'test-2', + 'cron_expression' => '* * * * *', + ], + ] + ); + + $task1 = $this->prophesize(Task::class); + $task1->getSystemKey()->willReturn('testing-1'); + $task2 = $this->prophesize(Task::class); + $task2->getSystemKey()->willReturn('testing-2'); + + $this->taskRepository->findBySystemKey('testing-1')->willReturn(null); + $this->taskRepository->findBySystemKey('testing-2')->willReturn(null); + $this->taskRepository->findSystemTasks()->willReturn([$task1->reveal(), $task2->reveal()]); + + $taskBuilder1 = $this->prophesize(TaskBuilder::class); + $this->scheduler->createTask(TestHandler::class, 'test-1')->shouldBeCalled()->willReturn( + $taskBuilder1->reveal() + ); + $taskBuilder1->setSystemKey('testing-1')->shouldBeCalled(); + $taskBuilder1->cron('* * * * *')->shouldBeCalled(); + $taskBuilder1->schedule()->shouldBeCalled(); + + $taskBuilder2 = $this->prophesize(TaskBuilder::class); + $this->scheduler->createTask(TestHandler::class, 'test-2')->shouldBeCalled()->willReturn( + $taskBuilder2->reveal() + ); + $taskBuilder2->setSystemKey('testing-2')->shouldBeCalled(); + $taskBuilder2->cron('* * * * *')->shouldBeCalled(); + $taskBuilder2->schedule()->shouldBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } + + public function testExecuteDisable() + { + $command = $this->createCommand( + [ + 'testing' => [ + 'enabled' => false, + 'handler_class' => TestHandler::class, + 'workload' => 'test', + 'cron_expression' => '* * * * *', + ], + ] + ); + + $task = $this->prophesize(Task::class); + $task->getInterval()->willReturn(CronExpression::factory('* * * * *')); + $task->getFirstExecution()->willReturn(new \DateTime()); + $task->getSystemKey()->willReturn('testing'); + + $task->setInterval( + $task->reveal()->getInterval(), + $task->reveal()->getFirstExecution(), + Argument::that( + function ($date) { + return $date <= new \DateTime('+1 Minute'); + } + ) + )->shouldBeCalled(); + + $this->taskRepository->findBySystemKey('testing')->willReturn($task->reveal()); + $this->taskRepository->findSystemTasks()->willReturn([$task->reveal()]); + + $execution = $this->prophesize(TaskExecutionInterface::class); + $execution->setStatus(TaskStatus::ABORTED); + + $this->taskExecutionRepository->findPending($task->reveal())->willReturn($execution->reveal()); + $this->taskExecutionRepository->save($execution->reveal())->shouldBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } + + public function testExecuteUpdate() + { + $command = $this->createCommand( + [ + 'testing' => [ + 'enabled' => true, + 'handler_class' => TestHandler::class, + 'workload' => 'test', + 'cron_expression' => '* * * * *', + ], + ] + ); + + $task = $this->prophesize(Task::class); + $task->getSystemKey()->willReturn('testing'); + $task->getHandlerClass()->willReturn(TestHandler::class); + $task->getWorkload()->willReturn('test'); + $task->getInterval()->willReturn(CronExpression::factory('@daily')); + $task->getFirstExecution()->willReturn(new \DateTime()); + + $task->setInterval(CronExpression::factory('* * * * *'), $task->reveal()->getFirstExecution())->shouldBeCalled( + ); + + $this->taskRepository->findBySystemKey('testing')->willReturn($task->reveal()); + $this->taskRepository->findSystemTasks()->willReturn([$task->reveal()]); + + $execution = $this->prophesize(TaskExecutionInterface::class); + $execution->setStatus(TaskStatus::ABORTED); + + $this->taskExecutionRepository->findPending($task->reveal())->willReturn($execution->reveal()); + $this->taskExecutionRepository->save($execution->reveal())->shouldBeCalled(); + + $this->scheduler->scheduleTasks()->shouldBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } + + public function testExecuteUpdateNotSupported() + { + $command = $this->createCommand( + [ + 'testing' => [ + 'enabled' => true, + 'handler_class' => TestHandler::class, + 'workload' => 'test', + 'cron_expression' => '* * * * *', + ], + ] + ); + + $task = $this->prophesize(Task::class); + $task->getSystemKey()->willReturn('testing'); + $task->getHandlerClass()->willReturn('not-existing'); + $task->getWorkload()->willReturn('new-workload'); + $task->getInterval()->willReturn(CronExpression::factory('@daily')); + $task->getFirstExecution()->willReturn(new \DateTime()); + + $task->setInterval(Argument::cetera())->shouldNotBeCalled(); + + $this->taskRepository->findBySystemKey('testing')->willReturn($task->reveal()); + $this->taskRepository->findSystemTasks()->willReturn([$task->reveal()]); + + $this->taskExecutionRepository->save(Argument::cetera())->shouldNotBeCalled(); + + $this->scheduler->scheduleTasks()->shouldNotBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } + + public function testExecuteRemove() + { + $command = $this->createCommand([]); + + $task = $this->prophesize(Task::class); + $task->getInterval()->willReturn(CronExpression::factory('* * * * *')); + $task->getFirstExecution()->willReturn(new \DateTime()); + $task->getSystemKey()->willReturn('testing'); + + $task->setInterval( + $task->reveal()->getInterval(), + $task->reveal()->getFirstExecution(), + Argument::that( + function ($date) { + return $date <= new \DateTime('+1 Minute'); + } + ) + )->shouldBeCalled(); + + $this->taskRepository->findBySystemKey('testing')->willReturn($task->reveal()); + $this->taskRepository->findSystemTasks()->willReturn([$task->reveal()]); + + $execution = $this->prophesize(TaskExecutionInterface::class); + $execution->setStatus(TaskStatus::ABORTED); + + $this->taskExecutionRepository->findPending($task->reveal())->willReturn($execution->reveal()); + $this->taskExecutionRepository->save($execution->reveal())->shouldBeCalled(); + + $command->run( + $this->prophesize(InputInterface::class)->reveal(), + $this->prophesize(OutputInterface::class)->reveal() + ); + } +} diff --git a/tests/Unit/DependencyInjection/HandlerCompilerPassTest.php b/tests/Unit/DependencyInjection/HandlerCompilerPassTest.php index fdbf2a4..62bac6b 100644 --- a/tests/Unit/DependencyInjection/HandlerCompilerPassTest.php +++ b/tests/Unit/DependencyInjection/HandlerCompilerPassTest.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Unit\DependencyInjection; +namespace Task\TaskBundle\Unit\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; diff --git a/tests/app/config/config.doctrine.yml b/tests/app/config/config.doctrine.yml index 288a06c..bf2bcab 100644 --- a/tests/app/config/config.doctrine.yml +++ b/tests/app/config/config.doctrine.yml @@ -1,5 +1,11 @@ task: storage: doctrine + system_tasks: + testing: + enabled: true + handler_class: Task\TaskBundle\Tests\Functional\TestHandler + workload: ['test'] + cron_expression: '* * * * *' doctrine: orm: