From cafe2ed8a7c876f6f44efdf25bbd17d5f6a5df5e Mon Sep 17 00:00:00 2001 From: Jan Jakes Date: Wed, 14 Sep 2022 15:12:44 +0200 Subject: [PATCH] Add no cycle rule for workflow validation [MAILPOET-4629] --- .../Validation/WorkflowRules/NoCycleRule.php | 35 ++++++++++++ .../WorkflowRules/NoCycleRuleTest.php | 57 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 mailpoet/lib/Automation/Engine/Validation/WorkflowRules/NoCycleRule.php create mode 100644 mailpoet/tests/unit/Automation/Engine/Validation/WorkflowRules/NoCycleRuleTest.php diff --git a/mailpoet/lib/Automation/Engine/Validation/WorkflowRules/NoCycleRule.php b/mailpoet/lib/Automation/Engine/Validation/WorkflowRules/NoCycleRule.php new file mode 100644 index 0000000000..720e6b159d --- /dev/null +++ b/mailpoet/lib/Automation/Engine/Validation/WorkflowRules/NoCycleRule.php @@ -0,0 +1,35 @@ +getStep(); + $parents = $node->getParents(); + $parentIdsMap = array_combine( + array_map(function (Step $parent) { + return $parent->getId(); + }, $node->getParents()), + $parents + ) ?: []; + + foreach ($step->getNextSteps() as $nextStep) { + $nextStepId = $nextStep->getId(); + if ($nextStepId === $step->getId() || isset($parentIdsMap[$nextStepId])) { + throw Exceptions::workflowStructureNotValid(__('Cycle found in workflow graph', 'mailpoet')); + } + } + } + + public function complete(Workflow $workflow): void { + } +} diff --git a/mailpoet/tests/unit/Automation/Engine/Validation/WorkflowRules/NoCycleRuleTest.php b/mailpoet/tests/unit/Automation/Engine/Validation/WorkflowRules/NoCycleRuleTest.php new file mode 100644 index 0000000000..082c2899e8 --- /dev/null +++ b/mailpoet/tests/unit/Automation/Engine/Validation/WorkflowRules/NoCycleRuleTest.php @@ -0,0 +1,57 @@ +createWorkflow([ + 'root' => ['a'], + 'a' => ['b'], + 'b' => ['c'], + 'c' => ['a'], + ]); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Invalid workflow structure: Cycle found in workflow graph'); + (new WorkflowWalker())->walk($workflow, [new NoCycleRule()]); + } + + public function testItDetectsSelfCycle(): void { + $workflow = $this->createWorkflow([ + 'root' => ['root'], + ]); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Invalid workflow structure: Cycle found in workflow graph'); + (new WorkflowWalker())->walk($workflow, [new NoCycleRule()]); + } + + public function testItPassesWithSimplePath(): void { + $workflow = $this->createWorkflow([ + 'root' => ['a'], + 'a' => ['b'], + 'b' => ['c'], + 'c' => [], + ]); + + (new WorkflowWalker())->walk($workflow, [new NoCycleRule()]); + // no exception thrown + } + + public function testItPassesWithPathSplitAndJoin(): void { + $workflow = $this->createWorkflow([ + 'root' => ['a1', 'a2'], + 'a1' => ['b'], + 'a2' => ['b'], + 'b' => [], + ]); + + (new WorkflowWalker())->walk($workflow, [new NoCycleRule()]); + // no exception thrown + } +}