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')
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 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 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)