def test_one_callback(self):
     action = Action('test', sources=['draft'], callbacks=[disable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     action.change_state(state)
     self.assertEqual(self.invoice.status, 'draft')
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(state.is_locked())
 def test_many_side_effects(self):
     action = Action('test',
                     sources=['draft'],
                     side_effects=[disable_invoice, enable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     action.change_state(state)
     self.assertEqual(self.invoice.status, 'draft')
     self.assertTrue(self.invoice.is_available)
     self.assertFalse(state.is_locked())
 def test_many_side_effects(self):
     transition = Transition('test',
                             sources=[],
                             target='cancelled',
                             side_effects=[disable_invoice, enable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     transition.change_state(state)
     self.assertEqual(self.invoice.status, transition.target)
     self.assertTrue(self.invoice.is_available)
     self.assertFalse(state.is_locked())
 def test_one_callback(self):
     transition = Transition('test',
                             sources=[],
                             target='cancelled',
                             callbacks=[disable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     transition.change_state(state)
     self.assertEqual(self.invoice.status, transition.target)
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(state.is_locked())
Example #5
0
 def complete_transition(self, state: State, **kwargs):
     """
     It completes the transition process for provided state.
     The instance will be unlocked and callbacks executed
     :param state: State object
     """
     state.set_state(self.target)
     logging.info(f'{state.instance_key} state changed to {self.target}')
     state.unlock()
     logging.info(f'{state.instance_key} has been unlocked')
     self.callbacks.execute(state, **kwargs)
 def test_failure_during_callbacks_with_failed_state(self):
     action = Action(
         'test',
         failed_state='failed',
         sources=['draft'],
         side_effects=[disable_invoice, fail_invoice, enable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     action.change_state(state)
     self.assertEqual(self.invoice.status, 'failed')
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(state.is_locked())
Example #7
0
 def fail_transition(self, state: State, exception: Exception, **kwargs):
     """
     It triggers fail transition in case of any failure during the side effects execution.
     :param state: State object
     :param exception: Exception that caused transition failure
     """
     if self.failed_state:
         state.set_state(self.failed_state)
         logging.info(
             f'{state.instance_key} state changed to {self.failed_state}')
     state.unlock()
     logging.info(f'{state.instance_key} has been unlocked')
     self.failure_callbacks.execute(state, exception=exception, **kwargs)
 def test_failure_during_side_effect_with_failed_state(self):
     transition = Transition(
         'test',
         sources=[],
         target='cancelled',
         failed_state='failed',
         side_effects=[disable_invoice, fail_invoice, enable_invoice])
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     transition.change_state(state)
     self.assertEqual(self.invoice.status, 'failed')
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(state.is_locked())
 def test_one_callback(self):
     transition = Transition('test',
                             sources=[],
                             target='success',
                             side_effects=[fail_invoice],
                             failure_callbacks=[disable_invoice],
                             failed_state='failed')
     self.assertTrue(self.invoice.is_available)
     state = State(self.invoice, 'status')
     transition.change_state(state)
     self.assertEqual(self.invoice.status, 'failed')
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(state.is_locked())
 def test_many_callback(self):
     action = Action('test',
                     side_effects=[fail_invoice],
                     sources=['draft'],
                     failure_callbacks=[disable_invoice, receive_invoice],
                     failed_state='failed')
     self.assertTrue(self.invoice.is_available)
     self.assertFalse(self.invoice.customer_received)
     state = State(self.invoice, 'status')
     action.change_state(state)
     self.assertEqual(self.invoice.status, 'failed')
     self.assertFalse(self.invoice.is_available)
     self.assertTrue(self.invoice.customer_received)
     self.assertFalse(state.is_locked())
 def test_callbacks_with_parameters(self):
     update_invoice(self.invoice, is_available=True, customer_received=True)
     action = Action('test',
                     failed_state='failed',
                     sources=['draft'],
                     callbacks=[update_invoice])
     self.invoice.refresh_from_db()
     self.assertTrue(self.invoice.is_available)
     self.assertTrue(self.invoice.customer_received)
     state = State(self.invoice, 'status')
     action.change_state(state, is_available=False, customer_received=False)
     self.invoice.refresh_from_db()
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(self.invoice.customer_received)
     self.assertFalse(state.is_locked())
 def test_side_effect_with_parameters(self):
     update_invoice(self.invoice, is_available=True, customer_received=True)
     transition = Transition('test',
                             sources=[],
                             target='cancelled',
                             failed_state='failed',
                             side_effects=[update_invoice])
     self.invoice.refresh_from_db()
     self.assertTrue(self.invoice.is_available)
     self.assertTrue(self.invoice.customer_received)
     state = State(self.invoice, 'status')
     transition.change_state(state,
                             is_available=False,
                             customer_received=False)
     self.invoice.refresh_from_db()
     self.assertFalse(self.invoice.is_available)
     self.assertFalse(self.invoice.customer_received)
     self.assertFalse(state.is_locked())
Example #13
0
 def is_valid(self, state: State, user=None) -> bool:
     """
     It validates this process to meet conditions and pass permissions
     :param state: State object
     :param user: any object used to pass permissions
     :return: True or False
     """
     return (not state.is_locked()
             and self.permissions.execute(state, user)
             and self.conditions.execute(state))
Example #14
0
    def change_state(self, state: State, **kwargs):
        """
        This method changes a state by the following algorithm:
        - Lock state
        - Change state to `in progress` if such exists
        - Run side effects which should run `complete_transition` in case of success
        or `fail_transition` in case of failure.
        :param state: State object
        """
        if state.is_locked():
            logging.info(f'{state.instance_key} is locked')
            raise TransitionNotAllowed("State is locked")

        state.lock()
        logging.info(f'{state.instance_key} has been locked')
        if self.in_progress_state:
            state.set_state(self.in_progress_state)
            logging.info(
                f'{state.instance_key} state changed to {self.in_progress_state}'
            )
        self.side_effects.execute(state, **kwargs)
 def test_failure_callback_exception_passed(self, debug_mock):
     update_invoice(self.invoice, is_available=True, customer_received=True)
     action = Action('test',
                     failed_state='failed',
                     side_effects=[fail_invoice],
                     sources=['draft'],
                     failure_callbacks=[debug_action])
     self.invoice.refresh_from_db()
     state = State(self.invoice, 'status')
     action.change_state(state, foo="bar")
     self.assertTrue(debug_mock.called)
     self.assertEqual(debug_mock.call_count, 1)
     call_args = debug_mock.call_args[0]
     call_kwargs = debug_mock.call_args[1]
     self.assertEqual(call_args, (self.invoice, ))
     self.assertEqual(len(call_kwargs), 2)
     self.assertTrue(isinstance(call_kwargs['exception'], Exception))
     self.assertEqual(call_kwargs['foo'], 'bar')
Example #16
0
 def setUp(self) -> None:
     self.state = State(Invoice.objects.create(status='draft'), 'status')
Example #17
0
class StateTestCase(TestCase):
    def setUp(self) -> None:
        self.state = State(Invoice.objects.create(status='draft'), 'status')

    def test_hash_remains_the_same(self):
        self.assertEqual(self.state._get_hash(), self.state._get_hash())

    def test_get_db_state(self):
        self.assertEqual(self.state.get_db_state(), 'draft')

    def test_lock(self):
        self.assertFalse(self.state.is_locked())
        self.state.lock()
        self.assertTrue(self.state.is_locked())

        # nothing should happen
        self.state.lock()
        self.assertTrue(self.state.is_locked())

        self.state.unlock()
        self.assertFalse(self.state.is_locked())

    def test_set_state(self):
        self.state.set_state('void')
        self.assertEqual(self.state.instance.status, 'void')
        # make sure it was saved to db
        self.state.instance.refresh_from_db()
        self.assertEqual(self.state.instance.status, 'void')