Skip to content

Test event logging in error case. #13

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
May 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@
"test-coverage" : "phpunit --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml",
"phpstan": "vendor/bin/phpstan.phar analyse -c tests/phpstan.neon -l 7 src/",
"cs-check": "vendor/bin/phpcs --colors --standard=config/ruleset.xml -s -p --ignore=/config/Migrations/,/src/Dto/ src/ tests/ config/",
"cs-fix": "vendor/bin/phpcbf --colors --standard=config/ruleset.xml -v --ignore=/config/Migrations/,/src/Dto/ src/ tests/ config/"
"cs-fix": "vendor/bin/phpcbf --colors --standard=config/ruleset.xml --ignore=/config/Migrations/,/src/Dto/ src/ tests/ config/"
}
}
19 changes: 16 additions & 3 deletions src/Business/Logger/TransitionLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,24 @@ public function __construct(
*/
public function setEvent(EventInterface $event): void
{
$nameEvent = $event->getName();
$nameEvent .= $event->getEventTypeLabel();
$eventName = $event->getName();
$eventName .= $event->getEventTypeLabel();

foreach ($this->logEntities as $logEntity) {
$logEntity->event = $nameEvent;
$logEntity->event = $eventName;
}
}

/**
* @param \StateMachine\Dto\StateMachine\ItemDto $itemDto
* @param string $eventName
*
* @return void
*/
public function setEventName(ItemDto $itemDto, string $eventName): void
{
foreach ($this->logEntities as $logEntity) {
$logEntity->event = $eventName;
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/Business/Logger/TransitionLogInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ interface TransitionLogInterface
*/
public function setEvent(EventInterface $event): void;

/**
* @param \StateMachine\Dto\StateMachine\ItemDto $itemDto
* @param string $eventName
*
* @return void
*/
public function setEventName(ItemDto $itemDto, string $eventName): void;

/**
* @param \StateMachine\Dto\StateMachine\ItemDto[] $stateMachineItems
*
Expand Down
14 changes: 14 additions & 0 deletions src/Business/StateMachine/Trigger.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public function triggerEvent(string $eventName, array $stateMachineItems): int

$this->transitionLog->init($stateMachineItems);
$this->logSourceState($stateMachineItems);
$this->logEventName($stateMachineItems, $eventName);

$this->runCommand($eventName, $stateMachineItems, $processes);

Expand Down Expand Up @@ -384,6 +385,19 @@ protected function logSourceState(array $stateMachineItems): void
}
}

/**
* @param \StateMachine\Dto\StateMachine\ItemDto[] $stateMachineItems
* @param string $eventName
*
* @return void
*/
protected function logEventName(array $stateMachineItems, string $eventName): void
{
foreach ($stateMachineItems as $itemDto) {
$this->transitionLog->setEventName($itemDto, $eventName);
}
}

/**
* @param string $commandString
* @param string $stateMachineName
Expand Down
2 changes: 1 addition & 1 deletion src/Model/Table/StateMachineTransitionLogsTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public function afterSave(EventInterface $event, EntityInterface $entity, ArrayO
/**
* @param int $stateMachineItemId
*
* @return array
* @return \StateMachine\Model\Entity\StateMachineTransitionLog[]
*/
public function getLogs(int $stateMachineItemId): array
{
Expand Down
96 changes: 96 additions & 0 deletions tests/TestCase/Business/StateMachineFacadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@
use Cake\I18n\FrozenTime;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
use InvalidArgumentException;
use StateMachine\Business\StateMachineFacade;
use StateMachine\Business\StateMachineFacadeInterface;
use StateMachine\Dependency\StateMachineHandlerInterface;
use StateMachine\Dto\StateMachine\ItemDto;
use StateMachine\Dto\StateMachine\ProcessDto;
use StateMachine\Model\Table\StateMachineItemsTable;
use StateMachine\Model\Table\StateMachineItemStateHistoryTable;
use StateMachine\Model\Table\StateMachineItemStatesTable;
use StateMachine\Model\Table\StateMachineProcessesTable;
use StateMachine\Model\Table\StateMachineTimeoutsTable;
use StateMachine\Model\Table\StateMachineTransitionLogsTable;

class StateMachineFacadeTest extends TestCase
{
protected const TESTING_SM = 'TestingSm';
protected const TEST_PROCESS_NAME = 'TestProcess';
protected const TEST_PROCESS_WITH_LOOP_NAME = 'TestProcessWithLoop';
protected const TEST_PROCESS_WITH_ERROR_NAME = 'TestProcessWithError';
protected const TEST_PROCESS_WITH_COMMAND_ERROR_NAME = 'TestProcessWithCommandError';

/**
* @var \StateMachine\Model\Table\StateMachineItemStateHistoryTable
Expand All @@ -48,6 +53,11 @@ class StateMachineFacadeTest extends TestCase
*/
protected $StateMachineTimeouts;

/**
* @var \StateMachine\Model\Table\StateMachineTransitionLogsTable
*/
protected $StateMachineTransitionLogs;

/**
* @var array
*/
Expand Down Expand Up @@ -81,6 +91,12 @@ public function setUp(): void

$config = TableRegistry::getTableLocator()->exists('StateMachineTimeouts') ? [] : ['className' => StateMachineTimeoutsTable::class];
$this->StateMachineTimeouts = TableRegistry::getTableLocator()->get('StateMachineTimeouts', $config);

$config = TableRegistry::getTableLocator()->exists('StateMachineTransitionLogs') ? [] : ['className' => StateMachineTransitionLogsTable::class];
$this->StateMachineTransitionLogs = TableRegistry::getTableLocator()->get('StateMachineTransitionLogs', $config);

$config = TableRegistry::getTableLocator()->exists('StateMachineItems') ? [] : ['className' => StateMachineItemsTable::class];
$this->StateMachineItems = TableRegistry::getTableLocator()->get('StateMachineItems', $config);
}

/**
Expand Down Expand Up @@ -211,6 +227,86 @@ public function testTriggerEventForItemWithManualEventShouldMoveToNextStateWithM
$this->assertSame($identifier, $itemDto->getIdentifier());
}

/**
* @return void
*/
public function testTriggerEventConditionFailureLogsTransition(): void
{
$processName = static::TEST_PROCESS_WITH_ERROR_NAME;
$identifier = 1985;

$processDto = new ProcessDto();
$processDto->setProcessName($processName);
$processDto->setStateMachineName(static::TESTING_SM);

$stateMachineHandler = $this->createTestStateMachineHandler();
$stateMachineFacade = $this->createStateMachineFacade($stateMachineHandler);

$failed = false;
try {
$stateMachineFacade->triggerForNewStateMachineItem($processDto, $identifier);
} catch (InvalidArgumentException $exception) {
$failed = true;
}
$this->assertTrue($failed, 'Should have thrown exception');

$itemDto = $stateMachineHandler->getItemStateUpdated();
$this->assertSame('invoice sent', $itemDto->getStateName());
$this->assertSame($identifier, $itemDto->getIdentifier());

$stateMachineItem = $this->StateMachineItems->find()->where(['state_machine' => static::TESTING_SM, 'identifier' => $identifier])->firstOrFail();

$logs = $this->StateMachineTransitionLogs->getLogs($stateMachineItem->id);
$this->assertCount(3, $logs);

$lastLog = array_shift($logs);
$this->assertSame('export order (on enter)', $lastLog->event);
$this->assertSame('invoice sent', $lastLog->source_state);
$this->assertNull($lastLog->target_state);
$this->assertTrue($lastLog->is_error);
$this->assertContains('Test exception for identity', $lastLog->error_message);
}

/**
* @return void
*/
public function testTriggerEventCommandFailureLogsTransition(): void
{
$processName = static::TEST_PROCESS_WITH_COMMAND_ERROR_NAME;
$identifier = 1985;

$processDto = new ProcessDto();
$processDto->setProcessName($processName);
$processDto->setStateMachineName(static::TESTING_SM);

$stateMachineHandler = $this->createTestStateMachineHandler();
$stateMachineFacade = $this->createStateMachineFacade($stateMachineHandler);

$failed = false;
try {
$stateMachineFacade->triggerForNewStateMachineItem($processDto, $identifier);
} catch (InvalidArgumentException $exception) {
$failed = true;
}
$this->assertTrue($failed, 'Should have thrown exception');

$itemDto = $stateMachineHandler->getItemStateUpdated();
$this->assertSame('invoice created', $itemDto->getStateName());
$this->assertSame($identifier, $itemDto->getIdentifier());

$stateMachineItem = $this->StateMachineItems->find()->where(['state_machine' => static::TESTING_SM, 'identifier' => $identifier])->firstOrFail();

$logs = $this->StateMachineTransitionLogs->getLogs($stateMachineItem->id);
$this->assertCount(2, $logs);

$lastLog = array_shift($logs);
$this->assertSame('send invoice', $lastLog->event);
$this->assertSame('invoice created', $lastLog->source_state);
$this->assertNull($lastLog->target_state);
$this->assertTrue($lastLog->is_error);
$this->assertContains('Test exception for identity', $lastLog->error_message);
}

/**
* @return void
*/
Expand Down
29 changes: 29 additions & 0 deletions tests/test_app/src/StateMachine/Command/TestErrorCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace App\StateMachine\Command;

use InvalidArgumentException;
use StateMachine\Dependency\CommandPluginInterface;
use StateMachine\Dto\StateMachine\ItemDto;

class TestErrorCommand implements CommandPluginInterface
{
/**
* This method is called when event have concrete command assigned.
*
* @param \StateMachine\Dto\StateMachine\ItemDto $itemDto
*
* @throws \InvalidArgumentException
*
* @return bool
*/
public function run(ItemDto $itemDto): bool
{
throw new InvalidArgumentException('Test exception for identity: ' . $itemDto->getIdentifierOrFail());
}
}
30 changes: 30 additions & 0 deletions tests/test_app/src/StateMachine/Condition/TestErrorCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace App\StateMachine\Condition;

use InvalidArgumentException;
use StateMachine\Dependency\ConditionPluginInterface;
use StateMachine\Dto\StateMachine\ItemDto;

class TestErrorCondition implements ConditionPluginInterface
{
/**
* Specification:
* - This method is called when transition in SM xml file have concrete condition assigned.
*
* @param \StateMachine\Dto\StateMachine\ItemDto $itemDto
*
* @throws \InvalidArgumentException
*
* @return bool
*/
public function check(ItemDto $itemDto): bool
{
throw new InvalidArgumentException('Test exception for identity: ' . $itemDto->getIdentifierOrFail());
}
}
4 changes: 4 additions & 0 deletions tests/test_app/src/StateMachine/TestStateMachineHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
namespace App\StateMachine;

use App\StateMachine\Command\TestCommand;
use App\StateMachine\Command\TestErrorCommand;
use App\StateMachine\Condition\TestErrorCondition;
use App\StateMachine\Condition\TestTrueCondition;
use StateMachine\Dependency\StateMachineHandlerInterface;
use StateMachine\Dto\StateMachine\ItemDto;
Expand All @@ -34,6 +36,7 @@ public function getCommands(): array
{
return [
'Test/Command' => TestCommand::class,
'Test/ErrorCommand' => TestErrorCommand::class,
];
}

Expand All @@ -44,6 +47,7 @@ public function getConditions(): array
{
return [
'Test/Condition' => TestTrueCondition::class,
'Test/ErrorCondition' => TestErrorCondition::class,
];
}

Expand Down
38 changes: 38 additions & 0 deletions tests/test_files/TestingSm/TestProcessWithCommandError.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0"?>
<statemachine
xmlns="spryker:state-machine-01"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="spryker:state-machine-01 http://static.spryker.com/state-machine-01.xsd">

<process name="TestProcess" main="true">
<states>
<state name="new"/>
<state name="invoice created">
<flag>Flag1</flag>
</state>
<state name="invoice sent">
<flag>Flag1</flag>
<flag>Flag2</flag>
</state>
</states>

<transitions>
<transition >
<source>new</source>
<target>invoice created</target>
<event>create invoice</event>
</transition>

<transition>
<source>invoice created</source>
<target>invoice sent</target>
<event>send invoice</event>
</transition>
</transitions>

<events>
<event name="create invoice" onEnter="true"/>
<event name="send invoice" onEnter="true" command="Test/ErrorCommand" />
</events>
</process>
</statemachine>
46 changes: 46 additions & 0 deletions tests/test_files/TestingSm/TestProcessWithError.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<statemachine
xmlns="spryker:state-machine-01"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="spryker:state-machine-01 http://static.spryker.com/state-machine-01.xsd">

<process name="TestProcess" main="true">
<states>
<state name="new"/>
<state name="invoice created">
<flag>Flag1</flag>
</state>
<state name="invoice sent">
<flag>Flag1</flag>
<flag>Flag2</flag>
</state>
<state name="order exported" />
</states>

<transitions>
<transition >
<source>new</source>
<target>invoice created</target>
<event>create invoice</event>
</transition>

<transition>
<source>invoice created</source>
<target>invoice sent</target>
<event>send invoice</event>
</transition>

<transition happy="true" condition="Test/ErrorCondition">
<source>invoice sent</source>
<target>order exported</target>
<event>export order</event>
</transition>
</transitions>

<events>
<event name="create invoice" onEnter="true" command="Test/Command" />
<event name="send invoice" onEnter="true"/>
<event name="export order" onEnter="true" />
</events>
</process>
</statemachine>