class TestProcess(Process): transitions = [ Transition('cancel', sources=[ 'draft', ], target='cancelled'), Transition('undo', sources=['cancelled'], target='draft') ]
def test_nested_process_fail(self): transition1 = Transition('action', sources=['draft'], target='done') transition2 = Transition('action', sources=['done'], target='closed') class ChildProcess(Process): permissions = [allowed] conditions = [is_editable] transitions = [transition1, transition2] process = ChildProcess(instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [transition1]) class ParentProcess(Process): permissions = [allowed] conditions = [is_editable] nested_processes = (ChildProcess, ) process = ParentProcess( instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [transition1]) class GrandParentProcess(Process): permissions = [allowed] conditions = [not_available] nested_processes = (ParentProcess, ) process = GrandParentProcess( instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [])
class CooperationPartnerProcess(Process): states = COOPERATION_PARTNER_STATE_CHOICES transitions = [ Transition(action_name='action_verify_account', sources=[ COOPERATION_PARTNER_STATE_REGISTERED, COOPERATION_PARTNER_STATE_EXPIRED ], target=COOPERATION_PARTNER_STATE_ON_HOLD, side_effects=[side_effects.verify_account], callbacks=[call_backs.verify_account]), Transition(action_name='action_approve_account', sources=[ COOPERATION_PARTNER_STATE_ON_HOLD, COOPERATION_PARTNER_STATE_REJECTED ], target=COOPERATION_PARTNER_STATE_APPROVED, side_effects=[side_effects.approve_account]), Transition(action_name='action_reject_account', sources=[ COOPERATION_PARTNER_STATE_ON_HOLD, COOPERATION_PARTNER_STATE_APPROVED ], target=COOPERATION_PARTNER_STATE_REJECTED, side_effects=[side_effects.reject_account]), ]
def test_transition_permissions_as_conditions(self): def is_staff(obj): return False def is_permitted(obj): return False transition1 = Transition('action', sources=['draft'], target='done', permissions=[is_staff]) transition2 = Transition('action', sources=['draft'], target='closed', permissions=[is_permitted]) class MainProcess(Process): transitions = [transition1, transition2] self.assertEqualNodes( annotate_nodes(MainProcess), { 'id': get_object_id(MainProcess), 'name': 'Main Process', 'type': 'process', 'nodes': [{ 'id': get_object_id(transition1), 'name': 'action', 'type': 'transition' }, { 'id': get_object_id(transition2), 'name': 'action', 'type': 'transition' }, { 'id': f'{get_object_id(transition1)}|conditions', 'name': 'is_staff', 'type': 'transition_conditions' }, { 'id': f'{get_object_id(transition2)}|conditions', 'name': 'is_permitted', 'type': 'transition_conditions' }, { 'id': 'closed', 'name': 'closed', 'type': 'state' }, { 'id': 'done', 'name': 'done', 'type': 'state' }, { 'id': 'draft', 'name': 'draft', 'type': 'state' }] })
def test_get_actions_with_the_same_name(self): transition1 = Transition('action', sources=['draft'], target='done') transition2 = Transition('action', sources=['draft'], target='closed') class ChildProcess(Process): transitions = [transition1, transition2] process = ChildProcess(instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(process.get_available_actions(), ['action'])
class UserLockerProcess(Process): permissions = [is_user] transitions = [ Transition(action_name='lock', sources=[LOCK_STATES.open], target=LOCK_STATES.locked), Transition(action_name='unlock', sources=[LOCK_STATES.locked], target=LOCK_STATES.open) ]
class TestProcess(Process): transitions = [ Transition('cancel', sources=[ 'draft', ], target='cancelled', callbacks=[disable_invoice]), Transition('undo', sources=['cancelled'], target='draft', callbacks=[update_invoice]) ]
def test_nested_process_with_nested_transitions_conditions_and_permissions_fail( self): transition1 = Transition('action', permissions=[allowed], conditions=[is_editable], sources=['draft'], target='done') transition2 = Transition('action', permissions=[disallow], conditions=[is_editable], sources=['done'], target='closed') transition3 = Transition('action', permissions=[allowed], conditions=[not_available], sources=['draft'], target='approved') transition4 = Transition('action', permissions=[allowed], conditions=[is_editable], sources=['done'], target='closed') transition5 = Transition('action', permissions=[disallow], conditions=[not_available], sources=['draft'], target='declined') class ChildProcess(Process): transitions = [transition1, transition2] class ParentProcess(Process): nested_processes = (ChildProcess, ) transitions = [transition3, transition4] class GrandParentProcess(Process): nested_processes = (ParentProcess, ) transitions = [transition5] process = GrandParentProcess( instance=Invoice.objects.create(status='draft'), field_name='status') for transition in process.get_available_transitions(self.user): self.assertIn(transition, [transition1]) process = GrandParentProcess( instance=Invoice.objects.create(status='done'), field_name='status') for transition in process.get_available_transitions(self.user): self.assertIn(transition, [transition4])
def test_get_sorted_list(self): transition1 = Transition('cancel', sources=['draft'], target='done') transition2 = Transition('action', sources=['draft'], target='closed') transition3 = Transition('bulk_action', sources=['draft'], target='closed') class ChildProcess(Process): transitions = [transition1, transition2, transition3] process = ChildProcess(instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(process.get_available_actions(), ['action', 'bulk_action', 'cancel'])
class TestProcess(Process): transitions = [ Transition('cancel', sources=[ 'draft', ], target='cancelled', conditions=[is_editable]), Transition('cancel', sources=[ 'draft', ], target='cancelled', conditions=[is_available]) ]
class TestProcess(Process): transitions = [ Transition('cancel', sources=[ 'draft', ], target='cancelled', permissions=[is_staff]), Transition('cancel', sources=[ 'draft', ], target='cancelled', permissions=[allowed]) ]
class TestProcess(Process): transitions = [ Transition('cancel', sources=[ 'draft', ], target='cancelled', callbacks=[disable_invoice]), Transition('undo', sources=['draft'], target='draft', side_effects=[fail_invoice], failed_state='failed', failure_callbacks=[update_invoice]) ]
class StaffLockerProcess(Process): permissions = [is_staff] all_states = [x for x, y in LOCK_STATES] transitions = [ Transition(action_name='lock', sources=[LOCK_STATES.open, LOCK_STATES.maintenance], target=LOCK_STATES.locked), Transition(action_name='unlock', sources=[LOCK_STATES.locked, LOCK_STATES.maintenance], target=LOCK_STATES.open), Transition(action_name='maintain', sources=all_states, target=LOCK_STATES.maintenance, conditions=[is_planned]) ]
def test_get_target_states(self): transition1 = Transition('cancel', sources=['draft'], target='done', failed_state='failed') transition2 = Transition('action', sources=['draft'], target='closed', failed_state='invalid') transition3 = Transition('bulk_action', sources=['draft'], target='closed', in_progress_state='closing') class ChildProcess(Process): transitions = [transition1, transition2, transition3] states = {'done', 'closed', 'failed', 'invalid', 'closing'} self.assertEqual(get_target_states(ChildProcess), states)
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())
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_process_fail(self): transition1 = Transition('action', sources=['draft'], target='done') transition2 = Transition('action', sources=['done'], target='closed') class ChildProcess(Process): conditions = [not_available] transitions = [transition1, transition2] process = ChildProcess(instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(list(process.get_available_transitions()), []) process = ChildProcess(instance=Invoice.objects.create(status='done'), field_name='status') self.assertEqual(list(process.get_available_transitions()), []) process = ChildProcess( instance=Invoice.objects.create(status='closed'), field_name='status') self.assertEqual(list(process.get_available_transitions()), [])
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_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_transitions(self): transition1 = Transition('action', sources=['draft'], target='done') transition2 = Transition('action', sources=['draft'], target='closed') class MainProcess(Process): transitions = [transition1, transition2] nodes = annotate_nodes(MainProcess) self.assertEqualNodes( nodes, { 'id': get_object_id(MainProcess), 'name': 'Main Process', 'type': 'process', 'nodes': [{ 'id': get_object_id(transition1), 'name': 'action', 'type': 'transition' }, { 'id': get_object_id(transition2), 'name': 'action', 'type': 'transition' }, { 'id': 'closed', 'name': 'closed', 'type': 'state' }, { 'id': 'done', 'name': 'done', 'type': 'state' }, { 'id': 'draft', 'name': 'draft', 'type': 'state' }] })
def test_get_all_states(self): transition1 = Transition('cancel', sources=['draft', 'open'], target='done', failed_state='failed') transition2 = Transition('action', sources=['draft'], target='closed', failed_state='invalid') transition3 = Transition('bulk_action', sources=['draft'], target='closed', in_progress_state='closing') class ChildProcess(Process): transitions = [transition1, transition2, transition3] states = { 'done', 'closed', 'failed', 'invalid', 'closing', 'draft', 'open' } self.assertEqual(get_all_states(ChildProcess), states) transition4 = Transition('approve', sources=['draft', 'created'], target='approved', failed_state='declined', in_progress_state='approving') states |= {'approved', 'approving', 'declined', 'created'} class MainProcess(Process): transitions = [ transition4, ] nested_processes = [ ChildProcess, ] self.assertEqual(get_all_states(MainProcess), states)
def test_conditions_and_permissions_successfully(self): transition1 = Transition('action', sources=['draft'], target='done') transition2 = Transition('action', sources=['done'], target='closed') class ChildProcess(Process): conditions = [is_editable] permissions = [allowed] transitions = [transition1, transition2] process = ChildProcess(instance=Invoice.objects.create(status='draft'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [transition1]) process = ChildProcess(instance=Invoice.objects.create(status='done'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [transition2]) process = ChildProcess( instance=Invoice.objects.create(status='closed'), field_name='status') self.assertEqual(list(process.get_available_transitions(self.user)), [])
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())
def test_failure_callback_exception_passed(self, debug_mock): update_invoice(self.invoice, is_available=True, customer_received=True) transition = Transition('test', sources=[], target='success', failed_state='failed', side_effects=[fail_invoice], failure_callbacks=[debug_action]) self.invoice.refresh_from_db() state = State(self.invoice, 'status') transition.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')
class InvoiceProcess(Process): process_name = 'invoice_process' states = ( ('draft', 'Draft'), ('paid', 'Paid'), ('void', 'Void'), ('sent', 'Sent'), ('failed', 'Failed'), ) transitions = [ Transition(action_name='approve', sources=['draft'], target='approved'), CeleryTransition(action_name='send_to_customer', sources=['approved'], side_effects=['demo.tasks.send_to_a_customer'], target='sent'), Transition(action_name='void', sources=['draft', 'paid'], target='voided'), CeleryTransition( action_name='demo', sources=['draft'], target='sent', in_progress_state='in_progress', side_effects=[ 'demo.tasks.demo_task_1', 'demo.tasks.demo_task_2', 'demo.tasks.demo_task_3' ], callbacks=['demo.tasks.demo_task_4', 'demo.tasks.demo_task_5']), CelerySingleTaskTransition( action_name='demo_single', sources=['draft'], target='sent', in_progress_state='in_progress', side_effects=[ tasks.demo_task_1, tasks.demo_task_2, tasks.demo_task_3 ], callbacks=[tasks.demo_task_4, tasks.demo_task_5]), CeleryTransition(action_name='failing_transition', sources=['draft'], target='sent', in_progress_state='in_progress', failed_state='failed', side_effects=[ 'demo.tasks.demo_task_1', 'demo.tasks.demo_task_exception', 'demo.tasks.demo_task_2' ], failure_callbacks=[ 'demo.tasks.demo_task_3', 'demo.tasks.demo_task_4' ]), CelerySingleTaskTransition( action_name='failing_transition_single', sources=['draft'], target='sent', in_progress_state='in_progress', failed_state='failed', side_effects=[ tasks.demo_task_1, tasks.demo_task_exception, tasks.demo_task_2 ], failure_callbacks=[tasks.demo_task_3, tasks.demo_task_4]) ]
def test_transition_conditions(self): def is_available(obj): return True def is_open(obj): return True transition1 = Transition('action', sources=['draft'], target='done', conditions=[ is_available, ]) transition2 = Transition('action', sources=['draft'], target='closed', conditions=[ is_open, ]) class MainProcess(Process): transitions = [transition1, transition2] self.assertEqualNodes( annotate_nodes(MainProcess), { 'id': get_object_id(MainProcess), 'name': 'Main Process', 'type': 'process', 'nodes': [{ 'id': get_object_id(transition1), 'name': 'action', 'type': 'transition' }, { 'id': get_object_id(transition2), 'name': 'action', 'type': 'transition' }, { 'id': f'{get_object_id(transition1)}|conditions', 'name': 'is_available', 'type': 'transition_conditions' }, { 'id': f'{get_object_id(transition2)}|conditions', 'name': 'is_open', 'type': 'transition_conditions' }, { 'id': 'closed', 'name': 'closed', 'type': 'state' }, { 'id': 'done', 'name': 'done', 'type': 'state' }, { 'id': 'draft', 'name': 'draft', 'type': 'state' }] })
def test_intersect_states(self): transition1 = Transition('action', sources=['draft'], target='open') transition2 = Transition('action', sources=['draft'], target='closed') transition3 = Transition('action', sources=['draft'], target='closed') transition4 = Transition('action', sources=['draft'], target='open') class ChildProcess(Process): transitions = [transition1, transition2] class MainProcess(Process): transitions = [transition3] nested_processes = [ChildProcess] class SuperProcess(Process): transitions = [transition4] nested_processes = [MainProcess] self.assertEqualNodes( annotate_nodes(SuperProcess), { "id": get_object_id(SuperProcess), "name": "Super Process", "type": "process", "nodes": [{ "id": get_object_id(transition4), "name": "action", "type": "transition" }, { "id": "open", "name": "open", "type": "state" }, { "id": "draft", "name": "draft", "type": "state" }, { "id": get_object_id(MainProcess), "name": "Main Process", "type": "process", "nodes": [{ "id": get_object_id(transition3), "name": "action", "type": "transition" }, { "id": "closed", "name": "closed", "type": "state" }, { "id": get_object_id(ChildProcess), "name": "Child Process", "type": "process", "nodes": [{ "id": get_object_id(transition1), "name": "action", "type": "transition" }, { "id": get_object_id(transition2), "name": "action", "type": "transition" }] }] }] })
class MyProcess(Process): transitions = [ Transition('get_available_transition', sources=['draft'], target='valid') ]
class MyProcess(Process): transitions = [ Transition('validate', sources=['draft'], target='valid') ]