setWorkflowName('dummy_workflow'); $workflow->setCreatedAt(new \DateTimeImmutable('2023-09-01')); $workflow->setStep('step1', new WorkflowTransitionContextDTO($workflow), 'to_step1', $daysAgos, new User()); $em = $this->prophesize(EntityManagerInterface::class); $em->flush()->shouldBeCalled(); $em->remove($workflow)->shouldNotBeCalled(); $handler = $this->buildHandler($workflow, $em->reveal(), $clock); $handler(new CancelStaleWorkflowMessage(1)); self::assertEquals('canceled', $workflow->getStep()); self::assertCount(3, $workflow->getSteps()); } public function testWorkflowNotInStaledHandlerIsUnrecoverable(): void { $this->expectException(UnrecoverableMessageHandlingException::class); $clock = new MockClock('2024-01-01'); $daysAgos = new \DateTimeImmutable('2023-12-31'); $workflow = new EntityWorkflow(); $workflow->setWorkflowName('dummy_workflow'); $workflow->setCreatedAt(new \DateTimeImmutable('2023-12-31')); $workflow->setStep('step1', new WorkflowTransitionContextDTO($workflow), 'to_step1', $daysAgos, new User()); $em = $this->prophesize(EntityManagerInterface::class); $em->flush()->shouldNotBeCalled(); $em->remove($workflow)->shouldNotBeCalled(); $handler = $this->buildHandler($workflow, $em->reveal(), $clock); $handler(new CancelStaleWorkflowMessage(1)); self::assertEquals('canceled', $workflow->getStep()); self::assertCount(3, $workflow->getSteps()); } public function testWorkflowStaledInInitialStateIsCompletelyRemoved(): void { $clock = new MockClock('2024-01-01'); $workflow = new EntityWorkflow(); $workflow->setWorkflowName('dummy_workflow'); $workflow->setCreatedAt(new \DateTimeImmutable('2023-09-01')); $em = $this->prophesize(EntityManagerInterface::class); $em->flush()->shouldBeCalled(); $em->remove($workflow)->shouldNotBeCalled(); $handler = $this->buildHandler($workflow, $em->reveal(), $clock); $handler(new CancelStaleWorkflowMessage(1)); self::assertEquals('canceled', $workflow->getStep()); self::assertCount(2, $workflow->getSteps()); } private function buildHandler( EntityWorkflow $entityWorkflow, EntityManagerInterface $entityManager, ClockInterface $clock, ): CancelStaleWorkflowHandler { // set an id for the workflow $reflection = new \ReflectionClass($entityWorkflow); $reflection->getProperty('id')->setValue($entityWorkflow, 1); $repository = $this->prophesize(EntityWorkflowRepository::class); $repository->find(1)->willReturn($entityWorkflow); return new CancelStaleWorkflowHandler($repository->reveal(), $this->buildRegistry(), $entityManager, new NullLogger(), $clock); } private function buildRegistry(): Registry { $definitionBuilder = new DefinitionBuilder(); $definitionBuilder ->setInitialPlaces('initial') ->addPlaces(['initial', 'step1', 'canceled', 'final']) ->addTransition(new Transition('to_step1', 'initial', 'step1')) ->addTransition(new Transition('cancel', 'initial', 'canceled')) ->addTransition(new Transition('finalize', 'initial', 'final')) ->addTransition(new Transition('cancel', 'step1', 'canceled')) ->addTransition(new Transition('finalize', 'step1', 'final')); $definitionBuilder->setMetadataStore(new InMemoryMetadataStore(placesMetadata: [ 'canceled' => [ 'isFinal' => true, 'isFinalPositive' => false, ], 'final' => [ 'isFinal' => true, 'isFinalPositive', true, ], ])); $workflow = new Workflow($definitionBuilder->build(), new EntityWorkflowMarkingStore(), null, 'dummy_workflow'); $supports = new class () implements WorkflowSupportStrategyInterface { public function supports(WorkflowInterface $workflow, object $subject): bool { return true; } }; $registry = new Registry(); $registry->addWorkflow($workflow, $supports); return $registry; } }