StateMachineSpec.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace spec\SM\StateMachine;
  3. use PhpSpec\ObjectBehavior;
  4. use Prophecy\Argument;
  5. use SM\Callback\CallbackFactoryInterface;
  6. use SM\Callback\CallbackInterface;
  7. use SM\Event\SMEvents;
  8. use spec\SM\DummyObject;
  9. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  10. class StateMachineSpec extends ObjectBehavior
  11. {
  12. protected $config = array(
  13. 'graph' => 'graph1',
  14. 'property_path' => 'state',
  15. 'states' => array('checkout', 'pending', 'confirmed', 'cancelled'),
  16. 'transitions' => array(
  17. 'create' => array(
  18. 'from' => array('checkout'),
  19. 'to' => 'pending'
  20. ),
  21. 'confirm' => array(
  22. 'from' => array('checkout', 'pending'),
  23. 'to' => 'confirmed'
  24. ),
  25. 'cancel' => array(
  26. 'from' => array('confirmed'),
  27. 'to' => 'cancelled'
  28. )
  29. ),
  30. 'callbacks' => array(
  31. 'guard' => array(
  32. 'guard-confirm' => array(
  33. 'from' => array('pending'),
  34. 'do' => 'dummy'
  35. )
  36. ),
  37. 'before' => array(
  38. 'from-checkout' => array(
  39. 'from' => array('checkout'),
  40. 'do' => 'dummy'
  41. )
  42. ),
  43. 'after' => array(
  44. 'on-confirm' => array(
  45. 'on' => array('confirm'),
  46. 'do' => 'dummy'
  47. ),
  48. 'to-cancelled' => array(
  49. 'to' => array('cancelled'),
  50. 'do' => 'dummy'
  51. )
  52. )
  53. )
  54. );
  55. function let(DummyObject $object, EventDispatcherInterface $dispatcher, CallbackFactoryInterface $callbackFactory)
  56. {
  57. $this->beConstructedWith($object, $this->config, $dispatcher, $callbackFactory);
  58. }
  59. function it_is_initializable()
  60. {
  61. $this->shouldHaveType('SM\StateMachine\StateMachine');
  62. }
  63. function it_can($object, $dispatcher, $callbackFactory, CallbackInterface $guard)
  64. {
  65. $object->getState()->shouldBeCalled()->willReturn('checkout');
  66. $object->setState(Argument::any())->shouldNotBeCalled();
  67. $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  68. $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);
  69. $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true);
  70. $this->can('create')->shouldReturn(true);
  71. }
  72. function it_cannot($object, $dispatcher)
  73. {
  74. $object->getState()->shouldBeCalled()->willReturn('cancel');
  75. $object->setState(Argument::any())->shouldNotBeCalled();
  76. $dispatcher->dispatch(Argument::any())->shouldNotBeCalled();
  77. $this->can('create')->shouldReturn(false);
  78. }
  79. function it_is_guarded_and_can($object, $dispatcher, $callbackFactory, CallbackInterface $guard)
  80. {
  81. $object->getState()->shouldBeCalled()->willReturn('pending');
  82. $object->setState(Argument::any())->shouldNotBeCalled();
  83. $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  84. $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);
  85. $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true);
  86. $this->can('confirm')->shouldReturn(true);
  87. }
  88. function it_is_guarded_and_cannot($object, $dispatcher, $callbackFactory, CallbackInterface $guard)
  89. {
  90. $object->getState()->shouldBeCalled()->willReturn('pending');
  91. $object->setState(Argument::any())->shouldNotBeCalled();
  92. $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  93. $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);
  94. $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(false);
  95. $this->can('confirm')->shouldReturn(false);
  96. }
  97. function it_throws_an_exception_if_transition_doesnt_exist_on_can()
  98. {
  99. $this->shouldThrow('SM\\SMException')->during('can', array('non-existing-transition'));
  100. }
  101. function it_applies_transition(
  102. $object,
  103. $dispatcher,
  104. $callbackFactory,
  105. CallbackInterface $guard,
  106. CallbackInterface $callback1,
  107. CallbackInterface $callback2,
  108. CallbackInterface $callback3
  109. ) {
  110. $object->getState()->shouldBeCalled()->willReturn('checkout');
  111. $object->setState('confirmed')->shouldBeCalled();
  112. $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  113. $dispatcher->dispatch(SMEvents::PRE_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  114. $dispatcher->dispatch(SMEvents::POST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  115. $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);
  116. $callbackFactory->get($this->config['callbacks']['before']['from-checkout'])->shouldBeCalled()->willReturn($callback1);
  117. $callbackFactory->get($this->config['callbacks']['after']['on-confirm'])->shouldBeCalled()->willReturn($callback2);
  118. $callbackFactory->get($this->config['callbacks']['after']['to-cancelled'])->shouldBeCalled()->willReturn($callback3);
  119. $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true);
  120. $callback1->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  121. $callback2->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  122. $callback3->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();
  123. $this->apply('confirm');
  124. }
  125. function it_throws_an_exception_if_transition_cannot_be_applied($object, $dispatcher)
  126. {
  127. $object->getState()->shouldBeCalled()->willReturn('cancel');
  128. $object->setState(Argument::any())->shouldNotBeCalled();
  129. $dispatcher->dispatch(Argument::any())->shouldNotBeCalled();
  130. $this->shouldThrow('SM\\SMException')->during('apply', array('confirm'));
  131. }
  132. function it_does_nothing_if_transition_cannot_be_applied_in_soft_mode($object, $dispatcher)
  133. {
  134. $object->getState()->shouldBeCalled()->willReturn('cancel');
  135. $object->setState(Argument::any())->shouldNotBeCalled();
  136. $dispatcher->dispatch(Argument::any())->shouldNotBeCalled();
  137. $this->apply('confirm', true);
  138. }
  139. function it_throws_an_exception_if_transition_doesnt_exist_on_apply()
  140. {
  141. $this->shouldThrow('SM\\SMException')->during('apply', array('non-existing-transition'));
  142. }
  143. function it_returns_current_state($object)
  144. {
  145. $object->getState()->shouldBeCalled()->willReturn('my-state');
  146. $this->getState()->shouldReturn('my-state');
  147. }
  148. function it_returns_current_graph()
  149. {
  150. $this->getGraph()->shouldReturn($this->config['graph']);
  151. }
  152. function it_returns_current_object($object)
  153. {
  154. $this->getObject()->shouldReturn($object);
  155. }
  156. function it_returns_possible_transitions($object, $callbackFactory, CallbackInterface $guard)
  157. {
  158. $object->getState()->shouldBeCalled()->willReturn('checkout');
  159. $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);
  160. $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true);
  161. $this->getPossibleTransitions()->shouldReturn(array('create', 'confirm'));
  162. }
  163. }