Introduce subject transformation

[MAILPOET-4935]
This commit is contained in:
David Remer
2023-03-15 08:23:27 +02:00
committed by Aschepikov
parent 06874645c7
commit 5328213d85
8 changed files with 427 additions and 28 deletions

View File

@@ -10,6 +10,7 @@ use MailPoet\Automation\Engine\Data\AutomationRunLog;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Data\StepRunArgs;
use MailPoet\Automation\Engine\Data\StepValidationArgs;
use MailPoet\Automation\Engine\Data\Subject as SubjectData;
use MailPoet\Automation\Engine\Data\SubjectEntry;
use MailPoet\Automation\Engine\Exceptions;
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
@@ -55,6 +56,9 @@ class StepHandler {
/** @var Registry */
private $registry;
/** @var SubjectTransformerHandler */
private $subjectTransformerHandler;
public function __construct(
ActionScheduler $actionScheduler,
ActionStepRunner $actionStepRunner,
@@ -64,7 +68,8 @@ class StepHandler {
AutomationRunStorage $automationRunStorage,
AutomationRunLogStorage $automationRunLogStorage,
AutomationStorage $automationStorage,
Registry $registry
Registry $registry,
SubjectTransformerHandler $subjectTransformerHandler
) {
$this->actionScheduler = $actionScheduler;
$this->actionStepRunner = $actionStepRunner;
@@ -75,6 +80,7 @@ class StepHandler {
$this->automationRunLogStorage = $automationRunLogStorage;
$this->automationStorage = $automationStorage;
$this->registry = $registry;
$this->subjectTransformerHandler = $subjectTransformerHandler;
}
public function initialize(): void {
@@ -217,6 +223,7 @@ class StepHandler {
$subjectEntries = [];
foreach ($requiredSubjectKeys as $key) {
$subjectData = $subjectDataMap[$key] ?? null;
$subjectData = $subjectData ?? $this->transformSubject($key, $automationRun);
if (!$subjectData) {
throw Exceptions::subjectDataNotFound($key, $automationRun->getId());
}
@@ -227,6 +234,13 @@ class StepHandler {
return $subjectEntries;
}
/**
* @return SubjectData[]|null
*/
private function transformSubject(string $target, AutomationRun $automationRun): ?array {
return $this->subjectTransformerHandler->transformSubjectData($target, $automationRun);
}
private function postProcessAutomationRun(int $automationRunId): void {
$automationRun = $this->automationRunStorage->getAutomationRun($automationRunId);
if (!$automationRun) {

View File

@@ -0,0 +1,142 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Engine\Control;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\AutomationRun;
use MailPoet\Automation\Engine\Data\Step as StepData;
use MailPoet\Automation\Engine\Data\Subject as SubjectData;
use MailPoet\Automation\Engine\Integration\Step;
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
use MailPoet\Automation\Engine\Registry;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
class SubjectTransformerHandler {
/* @var Registry */
private $registry;
/* @var AutomationStorage */
private $automationStorage;
public function __construct(
Registry $registry,
AutomationStorage $automationStorage
) {
$this->registry = $registry;
$this->automationStorage = $automationStorage;
}
/** @return string[] */
public function subjectKeysForAutomation(Automation $automation): array {
$configuredTriggers = array_filter(
$automation->getSteps(),
function (StepData $step): bool {
return $step->getType() === StepData::TYPE_TRIGGER;
}
);
$triggers = array_filter(array_map(
function(StepData $step): ?Step {
return $this->registry->getStep($step->getKey());
},
$configuredTriggers
));
$triggerKeys = [];
foreach ($triggers as $trigger) {
$triggerKeys[$trigger->getKey()] = $trigger->getSubjectKeys();
}
if (!$triggerKeys) {
return [];
}
$triggerKeys = count($triggerKeys) === 1 ? current($triggerKeys) : array_intersect(...array_values($triggerKeys));
$possibleKeys = [];
foreach ($triggerKeys as $key) {
$possibleKeys = array_merge($possibleKeys, $this->getPossibleTransformations($key));
}
return array_unique(array_values(array_merge($triggerKeys, $possibleKeys)));
}
private function getPossibleTransformations(string $key, string $stopKey = null): array {
if (!$stopKey) {
$stopKey = $key;
}
$allTransformer = $this->registry->getSubjectTransformer();
$possibleTransformer = array_filter(
$allTransformer,
function (SubjectTransformer $transformer) use ($key): bool {
return $transformer->accepts() === $key;
}
);
$possibleKeys = [];
foreach ($possibleTransformer as $transformer) {
$possibleKey = $transformer->returns();
if ($possibleKey === $stopKey) {
continue;
}
$possibleKeys[$possibleKey] = $possibleKey;
$possibleKeys = array_merge(
$possibleKeys,
$this->getPossibleTransformations($possibleKey, $stopKey)
);
}
return $possibleKeys;
}
/**
* @return SubjectData[]|null
*/
public function transformSubjectData(string $target, AutomationRun $automationRun): ?array {
$automation = $this->automationStorage->getAutomation($automationRun->getAutomationId(), $automationRun->getVersionId());
if (!$automation || !in_array($target, $this->subjectKeysForAutomation($automation), true)) {
return null;
}
$transformedSubjects = [];
$subjects = $automationRun->getSubjects();
foreach ($subjects as $subject) {
$transformerChain = $this->getTransformerChain($subject->getKey(), $target);
if (!$transformerChain) {
continue;
}
foreach ($transformerChain as $transformer) {
$subject = $transformer->transform($subject);
}
$transformedSubjects[] = $subject;
}
return count($transformedSubjects) > 0 ? $transformedSubjects : null;
}
/**
* @return SubjectTransformer[]
*/
private function getTransformerChain(string $from, string $to): array {
$transformers = $this->registry->getSubjectTransformer();
$transformerChain = [];
//walk the graph of transformers to find the shortest path
$queue = [];
$queue[] = [$from, []];
while (count($queue) > 0) {
$current = array_shift($queue);
$currentKey = $current[0];
$currentPath = $current[1];
if ($currentKey === $to) {
$transformerChain = $currentPath;
break;
}
foreach ($transformers as $transformer) {
if ($transformer->accepts() === $currentKey) {
$newPath = $currentPath;
$newPath[] = $transformer;
$queue[] = [$transformer->returns(), $newPath];
}
}
}
return $transformerChain;
}
}

View File

@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Engine\Integration;
use MailPoet\Automation\Engine\Data\Subject;
interface SubjectTransformer {
public function transform(Subject $data): Subject;
public function returns(): string;
public function accepts(): string;
}

View File

@@ -7,6 +7,7 @@ use MailPoet\Automation\Engine\Integration\Action;
use MailPoet\Automation\Engine\Integration\Payload;
use MailPoet\Automation\Engine\Integration\Step;
use MailPoet\Automation\Engine\Integration\Subject;
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
use MailPoet\Automation\Engine\Integration\Trigger;
class Registry {
@@ -16,6 +17,9 @@ class Registry {
/** @var array<string, Subject<Payload>> */
private $subjects = [];
/** @var SubjectTransformer[] */
private $subjectTransformer = [];
/** @var array<string, Trigger> */
private $triggers = [];
@@ -55,6 +59,14 @@ class Registry {
return $this->subjects;
}
public function addSubjectTransformer(SubjectTransformer $transformer): void {
$this->subjectTransformer[] = $transformer;
}
public function getSubjectTransformer(): array {
return $this->subjectTransformer;
}
public function addStep(Step $step): void {
if ($step instanceof Trigger) {
$this->addTrigger($step);

View File

@@ -2,6 +2,7 @@
namespace MailPoet\Automation\Engine\Validation\AutomationRules;
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Exceptions;
@@ -13,10 +14,15 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
/** @var Registry */
private $registry;
/** @var SubjectTransformerHandler */
private $subjectTransformerHandler;
public function __construct(
Registry $registry
Registry $registry,
SubjectTransformerHandler $subjectTransformerHandler
) {
$this->registry = $registry;
$this->subjectTransformerHandler = $subjectTransformerHandler;
}
public function initialize(Automation $automation): void {
@@ -39,7 +45,7 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
return;
}
$subjectKeys = $this->collectSubjectKeys($automation, $node->getParents());
$subjectKeys = $this->subjectTransformerHandler->subjectKeysForAutomation($automation);
$missingSubjectKeys = array_diff($requiredSubjectKeys, $subjectKeys);
if (count($missingSubjectKeys) > 0) {
throw Exceptions::missingRequiredSubjects($step, $missingSubjectKeys);
@@ -48,24 +54,4 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
public function complete(Automation $automation): void {
}
/**
* @param Step[] $parents
* @return string[]
*/
private function collectSubjectKeys(Automation $automation, array $parents): array {
$triggers = array_filter($parents, function (Step $step) {
return $step->getType() === Step::TYPE_TRIGGER;
});
$subjectKeys = [];
foreach ($triggers as $trigger) {
$registryTrigger = $this->registry->getTrigger($trigger->getKey());
if (!$registryTrigger) {
throw Exceptions::automationTriggerNotFound($automation->getId(), $trigger->getKey());
}
$subjectKeys = array_merge($subjectKeys, $registryTrigger->getSubjectKeys());
}
return array_unique($subjectKeys);
}
}

View File

@@ -123,6 +123,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Automation\Engine\Control\ActionScheduler::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Control\RootStep::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Control\StepHandler::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Control\SubjectTransformerHandler::class)->setPublic(true)->setShared(false);
$container->autowire(\MailPoet\Automation\Engine\Control\SubjectLoader::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Control\Steps\ActionStepRunner::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Control\TriggerHandler::class)->setPublic(true);

View File

@@ -0,0 +1,199 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Engine\Control;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\AutomationRun;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Data\Subject;
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
use MailPoet\Automation\Engine\Integration\Trigger;
use MailPoet\Automation\Engine\Registry;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoetUnitTest;
class SubjectTransformerHandlerTest extends MailPoetUnitTest {
public function testItFindsAllPossibleSubjects(): void {
$triggerSubject = 'subject_a';
$graphKeys = [
$triggerSubject => ['subject_b1', 'subject_b2'],
'subject_b1' => ['subject_c1'],
'subject_b2' => ['subject_c2'],
'subject_c1' => ['subject_d1'],
];
$transformers = [];
foreach ($graphKeys as $from => $tos) {
foreach ($tos as $to) {
$transformer = $this->createMock(SubjectTransformer::class);
$transformer->method('returns')->willReturn($to);
$transformer->method('accepts')->willReturn($from);
$transformers[] = $transformer;
}
}
$anotherRegisteredTransformer = $this->createMock(SubjectTransformer::class);
$anotherRegisteredTransformer->method('returns')->willReturn('yet_another_subject');
$anotherRegisteredTransformer->method('accepts')->willReturn('another_unrelated_subject');
$transformers[] = $anotherRegisteredTransformer;
$trigger = $this->createMock(Trigger::class);
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn([$triggerSubject]);
$registry = $this->createMock(Registry::class);
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformers);
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger){
return $key === 'trigger' ? $trigger : null;
});
$storage = $this->createMock(AutomationStorage::class);
$testee = new SubjectTransformerHandler($registry, $storage);
$triggerData = $this->createMock(Step::class);
$triggerData->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
$triggerData->expects($this->any())->method('getKey')->willReturn('trigger');
$automation = $this->createMock(Automation::class);
$automation->method('getSteps')->willReturn([$triggerData]);
$result = $testee->subjectKeysForAutomation($automation);
$this->assertEquals(['subject_a', 'subject_b1', 'subject_c1', 'subject_d1', 'subject_b2', 'subject_c2'], $result);
}
public function testItDoesNotRunInfiniteWhileFindingAllSubjects(): void {
$triggerSubject = 'subject_a';
$graphKeys = [
$triggerSubject => 'subject_b',
'subject_b' => 'subject_c',
'subject_c' => 'subject_a',
];
$transformers = [];
foreach ($graphKeys as $from => $to) {
$transformer = $this->createMock(SubjectTransformer::class);
$transformer->method('returns')->willReturn($to);
$transformer->method('accepts')->willReturn($from);
$transformers[] = $transformer;
}
$trigger = $this->createMock(Trigger::class);
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn([$triggerSubject]);
$registry = $this->createMock(Registry::class);
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformers);
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger){
return $key === 'trigger' ? $trigger : null;
});
$storage = $this->createMock(AutomationStorage::class);
$testee = new SubjectTransformerHandler($registry, $storage);
$triggerData = $this->createMock(Step::class);
$triggerData->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
$triggerData->expects($this->any())->method('getKey')->willReturn('trigger');
$automation = $this->createMock(Automation::class);
$automation->method('getSteps')->willReturn([$triggerData]);
$result = $testee->subjectKeysForAutomation($automation);
$this->assertEquals(['subject_a', 'subject_b', 'subject_c'], $result);
}
public function testItReturnsOnlyKeysInCommonForMultipleTriggers(): void {
$trigger1Keys = ['a', 'b', 'c'];
$trigger1 = $this->createMock(Trigger::class);
$trigger1->expects($this->any())->method('getKey')->willReturn('trigger1');
$trigger1->expects($this->any())->method('getSubjectKeys')->willReturn($trigger1Keys);
$trigger2Keys = ['b', 'c', 'd'];
$trigger2 = $this->createMock(Trigger::class);
$trigger2->expects($this->any())->method('getKey')->willReturn('trigger2');
$trigger2->expects($this->any())->method('getSubjectKeys')->willReturn($trigger2Keys);
$registry = $this->createMock(Registry::class);
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn([]);
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger1, $trigger2){
if ($key === 'trigger1') {
return $trigger1;
}
if ($key === 'trigger2') {
return $trigger2;
}
return null;
});
$storage = $this->createMock(AutomationStorage::class);
$testee = new SubjectTransformerHandler($registry, $storage);
$trigger1Data = $this->createMock(Step::class);
$trigger1Data->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
$trigger1Data->expects($this->any())->method('getKey')->willReturn('trigger1');
$trigger2Data = $this->createMock(Step::class);
$trigger2Data->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
$trigger2Data->expects($this->any())->method('getKey')->willReturn('trigger2');
$automation = $this->createMock(Automation::class);
$automation->method('getSteps')->willReturn([$trigger1Data, $trigger2Data]);
$result = $testee->subjectKeysForAutomation($automation);
$this->assertEquals(['b', 'c'], $result);
}
public function testItTransformsSubjects(): void {
$subjectTransformerStart = $this->createMock(SubjectTransformer::class);
$subjectTransformerStart->expects($this->any())->method('accepts')->willReturn('from');
$subjectTransformerStart->expects($this->any())->method('returns')->willReturn('middle');
$subjectTransformerStart->expects($this->any())->method('transform')->willReturnCallback(function($subject) {
if ($subject->getKey() === 'from') {
return new Subject('middle', []);
}
return $subject;
});
$subjectTransformerEnd = $this->createMock(SubjectTransformer::class);
$subjectTransformerEnd->expects($this->any())->method('accepts')->willReturn('middle');
$subjectTransformerEnd->expects($this->any())->method('returns')->willReturn('to');
$subjectTransformerEnd->expects($this->any())->method('transform')->willReturnCallback(function($subject) {
if ($subject->getKey() === 'middle') {
return new Subject('to', []);
}
return $subject;
});
$transformer = [
$subjectTransformerEnd,
$subjectTransformerStart,
];
$automation = $this->createMock(Automation::class);
$triggerStep = $this->createMock(Step::class);
$triggerStep->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
$triggerStep->expects($this->any())->method('getKey')->willReturn('trigger');
$automation->expects($this->any())->method('getSteps')->willReturn([$triggerStep]);
$registry = $this->createMock(Registry::class);
$registry->expects($this->any())->method('getStep')->willReturnCallback(
function($key) {
if ($key !== 'trigger') {
return null;
}
$trigger = $this->createMock(Trigger::class);
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn(['from']);
return $trigger;
}
);
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformer);
$storage = $this->createMock(AutomationStorage::class);
$storage->expects($this->any())->method('getAutomation')->willReturnCallback(function($id) use ($automation) {
return $id === 1 ? $automation : null;
});
$testee = new SubjectTransformerHandler($registry, $storage);
$subject = new Subject('from', ['key' => 'value']);
$automationRun = $this->createMock(AutomationRun::class);
$automationRun->expects($this->any())->method('getAutomationId')->willReturn(1);
$automationRun->expects($this->any())->method('getVersionId')->willReturn(1);
$automationRun->expects($this->any())->method('getSubjects')->willReturn([$subject]);
$subjects = $testee->transformSubjectData('to', $automationRun);
$this->assertNotNull($subjects);
$this->assertCount(1, $subjects);
$subject = current($subjects);
$this->assertInstanceOf(Subject::class, $subject);
$this->assertEquals('to', $subject->getKey());
}
}

View File

@@ -5,6 +5,7 @@ namespace MailPoet\Automation\Engine\Validation\AutomationRules;
require_once __DIR__ . '/AutomationRuleTest.php';
use MailPoet\Automation\Engine\Control\RootStep;
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\NextStep;
use MailPoet\Automation\Engine\Data\Step;
@@ -23,7 +24,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
$registry->addStep(new RootStep());
$registry->addStep($this->getTrigger('test:trigger', ['subject-a']));
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
$rule = new ValidStepOrderRule($registry);
$automation = $this->make(Automation::class, [
'getId' => 1,
@@ -34,6 +34,12 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
],
]);
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
return $a === $automation ? ['subject-a'] : [];
});
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage("Step with ID 'a' is missing required subjects with keys: subject-b");
(new AutomationWalker())->walk($automation, [$rule]);
@@ -45,7 +51,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b']));
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a']));
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
$rule = new ValidStepOrderRule($registry);
$automation = $this->make(Automation::class, [
'getId' => 1,
@@ -57,6 +62,14 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
],
]);
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
return $a === $automation ? ['subject-a'] : [];
});
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage("Step with ID 's' is missing required subjects with keys: subject-b");
(new AutomationWalker())->walk($automation, [$rule]);
@@ -67,7 +80,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
$registry->addStep(new RootStep());
$registry->addStep($this->getTrigger('test:trigger'));
$registry->addStep($this->getAction('test:action'));
$rule = new ValidStepOrderRule($registry);
$automation = $this->make(Automation::class, [
'getSteps' => [
@@ -76,6 +88,13 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
'a' => new Step('a', 'action', 'test:action', [], []),
],
]);
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
return $a === $automation ? ['subject-a'] : [];
});
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
(new AutomationWalker())->walk($automation, [$rule]);
}
@@ -84,7 +103,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
$registry->addStep(new RootStep());
$registry->addStep($this->getTrigger('test:trigger', ['subject-a', 'subject-b', 'subject-c']));
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
$rule = new ValidStepOrderRule($registry);
$automation = $this->make(Automation::class, [
'getSteps' => [
@@ -93,6 +111,14 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
'a' => new Step('a', 'action', 'test:action', [], []),
],
]);
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
return $a === $automation ? ['subject-a', 'subject-b'] : [];
});
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
(new AutomationWalker())->walk($automation, [$rule]);
}
@@ -102,7 +128,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b', 'subject-c']));
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a', 'subject-b']));
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
$rule = new ValidStepOrderRule($registry);
$automation = $this->make(Automation::class, [
'getId' => 1,
@@ -113,6 +138,13 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
's' => new Step('s', 'action', 'test:action', [], []),
],
]);
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
return $a === $automation ? ['subject-a', 'subject-b'] : [];
});
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
(new AutomationWalker())->walk($automation, [$rule]);
}