def test_reject_with_dependencies(config, mongo): handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('validation-reloaded.2018-05-17.xml', 'node1') execution = ptr.execution.get() execution.started_at = datetime(2018, 4, 1, 21, 45) execution.save() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'validation-reloaded').get_state(), 'values': [ { '_type': 'fgroup', 'ref': '_execution', 'forms': [{ 'ref': '_execution', 'fields': [ { '_type': 'field', 'name': 'name', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, { '_type': 'field', 'name': 'description', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, ], }], }, ], }) # first call to node1 handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form1', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node2' # first call to node2 handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form2', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node3' # first call to node3 handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form3', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node4' # first call to validation handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('node4', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'valid', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', 'state': 'valid', }, { 'name': 'inputs', 'value': [{ 'ref': 'node1.juan.0:form1.task', }], 'value_caption': '', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node1' # second call to node1 handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form1', [ { 'name': 'task', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node2' # second call to node2 handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form2', [ { 'name': 'task', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node4' # second call to validation handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('node4', [ { 'name': 'response', 'value': 'accept', 'value_caption': 'accept', 'state': 'valid', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', 'state': 'valid', }, { 'name': 'inputs', 'value': None, 'value_caption': 'None', 'state': 'valid', }, ])], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node5' # first call to last node handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('form5', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], }) assert list(Pointer.q().filter(status='ongoing')) == [] # state is coherent state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] del state['finished_at'] values = state.pop('values') assert state == { '_type': 'execution', 'id': execution.id, 'name': '', 'description': '', 'state': { '_type': ':sorted_map', 'items': { 'node1': { '_type': 'node', 'type': 'action', 'id': 'node1', 'state': 'valid', 'comment': 'I do not like it', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('form1', [ { 'name': 'task', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Primer paso', 'description': 'información original', }, 'node2': { '_type': 'node', 'type': 'action', 'id': 'node2', 'state': 'valid', 'comment': 'I do not like it', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('form2', [ { 'name': 'task', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Segundo paso', 'description': 'depender de la info', }, 'node3': { '_type': 'node', 'type': 'action', 'id': 'node3', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('form3', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Tercer paso', 'description': 'no depender de nada', }, 'node4': { '_type': 'node', 'type': 'validation', 'id': 'node4', 'state': 'valid', 'comment': 'I do not like it', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('node4', [ { 'name': 'response', 'value': 'accept', 'value_caption': 'accept', 'state': 'valid', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', 'state': 'valid', }, { 'name': 'inputs', 'value': None, 'value_caption': 'None', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Cuarto paso', 'description': 'validar', }, 'node5': { '_type': 'node', 'type': 'action', 'id': 'node5', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('form5', [ { 'name': 'task', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Quinto paso', 'description': 'terminar', }, }, 'item_order': ['node1', 'node2', 'node3', 'node4', 'node5'], }, 'status': 'finished', 'actors': { 'node1': 'juan', 'node2': 'juan', 'node3': 'juan', 'node4': 'juan', 'node5': 'juan', }, } eval_context = make_context({'values': values}, {}) eval_actor_map = make_actor_map({'values': values}) expected_context = { '_env': [{}], '_execution': [{ 'name': '', 'get_name_display': '', 'description': '', 'get_description_display': '', }], 'node4': [{ 'comment': 'I like it', 'get_comment_display': 'I like it', 'inputs': None, 'get_inputs_display': 'None', 'response': 'accept', 'get_response_display': 'accept', }], 'form1': [{ 'task': '2', 'get_task_display': '2', }], 'form2': [{ 'task': '2', 'get_task_display': '2', }], 'form3': [{ 'task': '1', 'get_task_display': '1', }], 'form5': [{ 'task': '1', 'get_task_display': '1', }], } assert { k: list(v.all()) for k, v in eval_context.items() } == expected_context expected_actor_map = { '_execution': [ { 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, } ], 'node4': [ { 'comment': { 'actor': 'juan', }, 'response': { 'actor': 'juan', }, 'inputs': { 'actor': 'juan', }, } ], 'form1': [ { 'task': { 'actor': 'juan', }, }, ], 'form2': [ { 'task': { 'actor': 'juan', }, }, ], 'form3': [ { 'task': { 'actor': 'juan', }, }, ], 'form5': [ { 'task': { 'actor': 'juan', }, }, ], } for frms in eval_actor_map.values(): for frm in frms: for fld in frm.values(): assert fld.pop('set_at') assert eval_actor_map == expected_actor_map
def test_approve(config, mongo): ''' tests that a validation node can go forward on approval ''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('validation.2018-05-09.xml', 'approval_node') mongo[config["POINTER_COLLECTION"]].insert_one({ 'id': ptr.id, 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'execution': { 'id': ptr.execution.get().id, }, 'node': { 'id': 'approval_node', }, 'actors': { '_type': ':map', 'items': {}, }, 'actor_list': [], }) execution = ptr.execution.get() execution.started_at = datetime(2018, 4, 1, 21, 45) execution.save() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'validation.2018-05-09').get_state(), 'actors': { 'start_node': 'juan', }, 'values': [ { '_type': 'fgroup', 'ref': '_execution', 'forms': [{ 'ref': '_execution', 'fields': [ { '_type': 'field', 'name': 'name', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, { '_type': 'field', 'name': 'description', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, ], }], }, { '_type': 'fgroup', 'ref': 'work', 'forms': [{ 'ref': 'work', 'fields': [ { '_type': 'field', 'name': 'task', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': 'juan', }, 'set_at': execution.started_at, }, ], }], }, ], }) # thing to test handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('approval_node', [ { 'name': 'response', 'value': 'accept', 'value_caption': 'accept', 'state': 'valid', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', 'state': 'valid', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0.task', }], 'value_caption': '', 'state': 'valid', }, ])], }) # assertions assert Pointer.get(ptr.id).status == 'finished' new_ptr = next(Pointer.q().filter(status='ongoing')) assert new_ptr.node_id == 'final_node' reg = next(mongo[config["POINTER_COLLECTION"]].find()) assert reg['started_at'] == datetime(2018, 4, 1, 21, 45) assert_near_date(reg['finished_at']) assert reg['execution']['id'] == new_ptr.execution.get().id assert reg['node']['id'] == 'approval_node' assert reg['actor_list'] == [ { 'form': 'approval_node', 'actor': { '_type': 'user', 'fullname': 'Juan', 'identifier': 'juan', 'email': None, }, }, ] assert reg['actors'] == { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, 'forms': [Form.state_json('approval_node', [ { 'name': 'response', 'name': 'response', 'value': 'accept', 'value_caption': 'accept', 'state': 'valid', }, { 'name': 'comment', 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', 'state': 'valid', }, { 'name': 'inputs', 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0.task', }], 'value_caption': '', 'state': 'valid', }, ])], }, }, } # data is invalidated state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': ptr.execution.get().id, })) del state['_id'] values = state.pop('values') eval_context = make_context({'values': values}, {}) eval_actor_map = make_actor_map({'values': values}) expected_context = { '_env': [{}], '_execution': [{ 'name': '', 'get_name_display': '', 'description': '', 'get_description_display': '', }], 'work': [{ 'task': '', 'get_task_display': '', }], 'approval_node': [{ 'comment': 'I like it', 'get_comment_display': 'I like it', 'response': 'accept', 'get_response_display': 'accept', 'inputs': [{'ref': 'start_node.juan.0.task'}], 'get_inputs_display': [{'ref': 'start_node.juan.0.task'}], }], } assert { k: list(v.all()) for k, v in eval_context.items() } == expected_context expected_actor_map = { '_execution': [ { 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, } ], 'work': [ { 'task': { 'actor': 'juan', }, }, ], 'approval_node': [ { 'comment': { 'actor': 'juan', }, 'response': { 'actor': 'juan', }, 'inputs': { 'actor': 'juan', }, } ], } for frms in eval_actor_map.values(): for frm in frms: for fld in frm.values(): assert fld.pop('set_at') assert eval_actor_map == expected_actor_map
def test_reject(config, mongo): ''' tests that a rejection moves the pointer to a backward position ''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('validation.2018-05-09.xml', 'approval_node') execution = ptr.execution.get() execution.started_at = datetime(2018, 4, 1, 21, 45) execution.save() mongo[config["POINTER_COLLECTION"]].insert_one({ 'id': ptr.id, 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'execution': { 'id': execution.id, }, 'node': { 'id': 'approval_node', }, 'actors': { '_type': ':map', 'items': {}, }, 'actor_list': [], }) state = Xml.load(config, 'validation.2018-05-09').get_state() state['items']['start_node']['state'] = 'valid' state['items']['start_node']['actors']['items']['juan'] = { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, 'forms': [Form.state_json('work', [ { 'name': 'task', '_type': 'field', 'state': 'valid', 'value': '2', }, ])], } mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': state, 'values': [ { '_type': 'fgroup', 'ref': '_execution', 'forms': [{ 'ref': '_execution', 'fields': [ { '_type': 'field', 'name': 'name', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, { '_type': 'field', 'name': 'description', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, ], }], }, { '_type': 'fgroup', 'ref': 'work', 'forms': [{ 'ref': 'work', 'fields': [ { '_type': 'field', 'name': 'task', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': 'juan', }, 'set_at': execution.started_at, }, ], }], }, ], }) # will teardown the approval node handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('approval_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'valid', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', 'state': 'valid', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:work.task', }], 'value_caption': '', 'state': 'valid', }, ])], }) # assertions assert Pointer.get(ptr.id).status == 'finished' new_ptr = next(Pointer.q().filter(status='ongoing')) assert new_ptr.node_id == 'start_node' assert new_ptr in user.tasks # data is invalidated state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] values = state.pop('values') assert state == { '_type': 'execution', 'id': execution.id, 'name': '', 'description': '', 'state': { '_type': ':sorted_map', 'items': { 'start_node': { '_type': 'node', 'type': 'action', 'id': 'start_node', 'state': 'ongoing', 'comment': 'I do not like it', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('work', [ { 'name': 'task', '_type': 'field', 'state': 'invalid', 'value': '2', }, ], state='invalid')], 'state': 'invalid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Primer paso', 'description': 'Resolver una tarea', }, 'approval_node': { '_type': 'node', 'type': 'validation', 'id': 'approval_node', 'state': 'invalid', 'comment': 'I do not like it', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('approval_node', [ { 'name': 'response', 'state': 'invalid', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', 'state': 'valid', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.' 'juan.0:work.task', }], 'value_caption': '', 'state': 'valid', }, ], state='invalid')], 'state': 'invalid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Aprobación gerente reserva', 'description': 'aprobar reserva', }, 'final_node': { '_type': 'node', 'type': 'action', 'id': 'final_node', 'state': 'unfilled', 'comment': '', 'actors': { '_type': ':map', 'items': {}, }, 'milestone': False, 'name': '', 'description': '', }, }, 'item_order': ['start_node', 'approval_node', 'final_node'], }, 'actors': { 'approval_node': 'juan', }, } eval_context = make_context({'values': values}, {}) eval_actor_map = make_actor_map({'values': values}) expected_context = { '_env': [{}], '_execution': [{ 'name': '', 'get_name_display': '', 'description': '', 'get_description_display': '', }], 'work': [{}], 'approval_node': [{ 'comment': 'I do not like it', 'get_comment_display': 'I do not like it', 'response': 'reject', 'get_response_display': 'reject', 'inputs': [{'ref': 'start_node.juan.0:work.task'}], 'get_inputs_display': [{'ref': 'start_node.juan.0:work.task'}], }], } assert { k: list(v.all()) for k, v in eval_context.items() } == expected_context expected_actor_map = { '_execution': [ { 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, } ], 'work': [{}], 'approval_node': [ { 'comment': { 'actor': 'juan', }, 'response': { 'actor': 'juan', }, 'inputs': { 'actor': 'juan', }, } ], } for frms in eval_actor_map.values(): for frm in frms: for fld in frm.values(): assert fld.pop('set_at') assert eval_actor_map == expected_actor_map # mongo has the data reg = next(mongo[config["POINTER_COLLECTION"]].find()) assert reg['started_at'] == datetime(2018, 4, 1, 21, 45) assert (reg['finished_at'] - datetime.now()).total_seconds() < 2 assert reg['execution']['id'] == new_ptr.execution.get().id assert reg['node']['id'] == 'approval_node' assert reg['actor_list'] == [ { 'form': 'approval_node', 'actor': { '_type': 'user', 'fullname': 'Juan', 'identifier': 'juan', 'email': None, }, }, ] assert reg['actors'] == { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [Form.state_json('approval_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'valid', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', 'state': 'valid', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:work.task', }], 'value_caption': '', 'state': 'valid', }, ])], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }
def test_variable_proc_name_mix(config, mongo): ''' Test where the name is related to multiple forms in diferent nodes of the execution''' handler = Handler(config) user = make_user('juan', 'Juan') xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ]) ] execution = xml.start(node, input, mongo, user.identifier) ptr = next(execution.pointers.q().filter(status='ongoing')) handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ]) ], }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', 'state': 'valid', }, ]) ], })
def test_send_request_multiple(config, mongo, mocker): ''' Tests that values are stored in such a way that they can be iterated in a jinja template. Specifically in this test they'll be used as part of a request node, thus also testing that all of the values can be used ''' # test setup class ResponseMock: status_code = 200 text = 'request response' mock = MagicMock(return_value=ResponseMock()) mocker.patch('requests.request', new=mock) handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('request-multiple.2019-11-14.xml', 'start_node') execution = ptr.execution.get() names = [random_string() for _ in '123'] mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), }) handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[0], 'value_caption': names[0], 'state': 'valid', }, ]), Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[1], 'value_caption': names[1], 'state': 'valid', }, ]), Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[2], 'value_caption': names[2], 'state': 'valid', }, ]), ], }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'request_node' # request is made with correct data requests.request.assert_called_once() args, kwargs = requests.request.call_args assert args[0] == 'POST' assert args[1] == 'http://localhost/' assert kwargs['data'] == '{{"names": ["{}","{}","{}"]}}'.format(*names) assert kwargs['headers'] == { 'content-type': 'application/json', }
def test_wakeup(config, mongo, mocker): ''' the first stage in a node's lifecycle ''' mocker.patch('pika.adapters.blocking_connection.' 'BlockingChannel.basic_publish') mocker.patch('pika.adapters.blocking_connection.' 'BlockingChannel.exchange_declare') # setup stuff handler = Handler(config) pointer = make_pointer('simple.2018-02-19.xml', 'start_node') execution = pointer.execution.get() juan = User(identifier='juan').save() manager = User(identifier='juan_manager', email='*****@*****.**').save() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), 'actors': { 'start_node': 'juan' }, }) # will wakeup the second node handler.step({ 'command': 'step', 'pointer_id': pointer.id, 'user_identifier': juan.identifier, 'input': [], }) # test manager is notified pika.adapters.blocking_connection.BlockingChannel.basic_publish.assert_called_once( ) pika.adapters.blocking_connection.BlockingChannel.exchange_declare.assert_called_once( ) args = pika.adapters.blocking_connection.BlockingChannel.basic_publish.call_args[ 1] assert args['exchange'] == config['RABBIT_NOTIFY_EXCHANGE'] assert args['routing_key'] == 'email' assert json.loads(args['body']) == { 'recipient': '*****@*****.**', 'subject': '[procesos] Tarea asignada', 'template': 'assigned-task.html', 'data': { 'pointer': next(Pointer.q().filter(status='ongoing')).to_json( include=['*', 'execution']), 'cacahuate_url': config['GUI_URL'], }, } # pointer collection updated reg = next(mongo[config["POINTER_COLLECTION"]].find()) assert_near_date(reg['started_at']) assert reg['finished_at'] is None assert reg['execution']['id'] == execution.id assert reg['node'] == { 'id': 'mid_node', 'type': 'action', 'description': 'añadir información', 'name': 'Segundo paso', } assert reg['actors'] == { '_type': ':map', 'items': {}, } assert reg['actor_list'] == [] assert reg['notified_users'] == [manager.to_json()] assert reg['state'] == 'ongoing' # execution collection updated reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert reg['state']['items']['mid_node']['state'] == 'ongoing' # tasks where asigned assert manager.tasks.count() == 1 task = next(manager.tasks.q().filter(status='ongoing')) assert isinstance(task, Pointer) assert task.node_id == 'mid_node' assert task.execution.get().id == execution.id
def test_variable_proc_name_pointers(config, mongo): ''' Test pointer name's update''' handler = Handler(config) user = make_user('juan', 'Juan') xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ]) ] execution = xml.start(node, input, mongo, user.identifier) ptr = next(execution.pointers.q().filter(status='ongoing')) handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ]) ], }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', 'state': 'valid', }, ]) ], }) # now check pointers last state query = {'execution.id': execution.id} assert mongo[config["POINTER_COLLECTION"]].count_documents(query) == 3 expected_name = 'Variable name process in step 3210' expected_desc = 'Description is also variable: 1, 2, 3' cursor = mongo[config["POINTER_COLLECTION"]].find(query) for item in cursor: assert item['execution']['name'] == expected_name assert item['execution']['description'] == expected_desc
def test_call_handler_delete_process(config, mongo): handler = Handler(config) pointer = make_pointer('simple.2018-02-19.xml', 'requester') execution = pointer.execution.get() body = '{"command":"cancel", "execution_id":"%s", "pointer_id":"%s"}'\ % (execution.id, pointer.id) mongo[config["EXECUTION_COLLECTION"]].insert_one({ 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'status': 'ongoing', 'id': execution.id }) ptr_id_1 = 'RANDOMPOINTERNAME' ptr_id_2 = 'MORERANDOMNAME' ptr_id_3 = 'NOTSORANDOMNAME' mongo[config["POINTER_COLLECTION"]].insert_many([ { 'execution': { 'id': execution.id, }, 'state': 'finished', 'id': ptr_id_1, }, { 'execution': { 'id': execution.id, }, 'state': 'ongoing', 'id': ptr_id_2, }, { 'execution': { 'id': execution.id[::-1], }, 'state': 'ongoing', 'id': ptr_id_3, }, ]) handler(body) reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert reg['id'] == execution.id assert reg['status'] == "cancelled" assert_near_date(reg['finished_at']) assert list(Execution.q().filter(status='ongoing')) == [] assert list(Pointer.q().filter(status='ongoing')) == [] assert mongo[config["POINTER_COLLECTION"]].find_one({ 'id': ptr_id_1, })['state'] == 'finished' assert mongo[config["POINTER_COLLECTION"]].find_one({ 'id': ptr_id_2, })['state'] == 'cancelled' assert mongo[config["POINTER_COLLECTION"]].find_one({ 'id': ptr_id_3, })['state'] == 'ongoing'
def test_teardown(config, mongo): ''' second and last stage of a node's lifecycle ''' # test setup handler = Handler(config) p_0 = make_pointer('simple.2018-02-19.xml', 'mid_node') execution = p_0.execution.get() execution.started_at = datetime(2018, 4, 1, 21, 45) execution.save() User(identifier='juan').save() manager = User(identifier='manager').save() manager2 = User(identifier='manager2').save() assert manager not in execution.actors.all() assert execution not in manager.activities.all() manager.tasks.set([p_0]) manager2.tasks.set([p_0]) state = Xml.load(config, execution.process_name).get_state() state['items']['start_node']['state'] = 'valid' mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': state, 'values': [ { '_type': 'fgroup', 'ref': '_execution', 'forms': [{ 'ref': '_execution', 'fields': [ { '_type': 'field', 'name': 'name', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, { '_type': 'field', 'name': 'description', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, ], }], }, ], 'actors': { 'start_node': 'juan', }, }) mongo[config["POINTER_COLLECTION"]].insert_one({ 'id': p_0.id, 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'execution': { 'id': execution.id, }, 'node': { 'id': p_0.node_id, }, 'actors': { '_type': ':map', 'items': {}, }, 'actor_list': [], }) # will teardown mid_node handler.step({ 'command': 'step', 'pointer_id': p_0.id, 'user_identifier': manager.identifier, 'input': [ Form.state_json('mid_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'data', }, ]) ], }) # assertions assert Pointer.get(p_0.id).status == 'finished' ptrs = list(Pointer.q().filter(status='ongoing')) assert len(ptrs) == 1 assert ptrs[0].node_id == 'final_node' # mongo has a registry reg = next(mongo[config["POINTER_COLLECTION"]].find()) assert reg['started_at'] == datetime(2018, 4, 1, 21, 45) assert_near_date(reg['finished_at']) assert reg['execution']['id'] == execution.id assert reg['node']['id'] == p_0.node_id assert reg['actors'] == { '_type': ':map', 'items': { 'manager': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'manager', 'fullname': None, 'email': None, }, 'forms': [ Form.state_json('mid_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'data', }, ]) ], }, }, } assert reg['actor_list'] == [ { 'form': 'mid_form', 'actor': { '_type': 'user', 'fullname': None, 'identifier': 'manager', 'email': None, }, }, ] # tasks where deleted from user assert list(manager.tasks.q().filter(status='ongoing')) == [] assert list(manager2.tasks.q().filter(status='ongoing')) == [] # state reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert reg['state'] == { '_type': ':sorted_map', 'items': { 'start_node': { '_type': 'node', 'type': 'action', 'id': 'start_node', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': {}, }, 'milestone': False, 'name': 'Primer paso', 'description': 'Resolver una tarea', }, 'mid_node': { '_type': 'node', 'type': 'action', 'id': 'mid_node', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': { 'manager': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'manager', 'fullname': None, 'email': None, }, 'forms': [ Form.state_json('mid_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'data', }, ]) ], }, }, }, 'milestone': False, 'name': 'Segundo paso', 'description': 'añadir información', }, 'final_node': { '_type': 'node', 'type': 'action', 'id': 'final_node', 'state': 'ongoing', 'comment': '', 'actors': { '_type': ':map', 'items': {}, }, 'milestone': False, 'name': '', 'description': '', }, }, 'item_order': [ 'start_node', 'mid_node', 'final_node', ], } values = reg['values'] eval_context = make_context({'values': values}, {}) eval_actor_map = make_actor_map({'values': values}) expected_context = { '_env': [{}], '_execution': [{ 'name': '', 'description': '', 'get_name_display': '', 'get_description_display': '', }], 'mid_form': [{ 'data': 'yes', 'get_data_display': 'yes' }], } assert {k: list(v.all()) for k, v in eval_context.items()} == expected_context expected_actor_map = { '_execution': [{ 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, }], 'mid_form': [{ 'data': { 'actor': 'manager', }, }], } for frms in eval_actor_map.values(): for frm in frms: for fld in frm.values(): assert fld.pop('set_at') assert eval_actor_map == expected_actor_map assert reg['actors'] == { 'start_node': 'juan', 'mid_node': 'manager', } assert manager in execution.actors assert execution in manager.activities
def test_exit_interaction(config, mongo, mocker): mocker.patch('cacahuate.tasks.handle.delay') handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('exit.2018-05-03.xml', 'start_node') execution = ptr.execution.get() execution.started_at = datetime(2018, 4, 1, 21, 45) execution.save() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), 'values': [ { '_type': 'fgroup', 'ref': '_execution', 'forms': [{ 'ref': '_execution', 'fields': [ { '_type': 'field', 'name': 'name', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, { '_type': 'field', 'name': 'description', 'value': '', 'value_caption': '', 'state': 'valid', 'actor': { 'identifier': '__system__', }, 'set_at': execution.started_at, }, ], }], }, ], }) # first node handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [], }) ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id args = handle.delay.call_args[0][0] assert json.loads(args) == { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [], } # exit node handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [], }) assert list(Pointer.q().filter(status='ongoing')) == [] assert list(Execution.q().filter(status='ongoing')) == [] # state is coherent state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] del state['finished_at'] values = state.pop('values') assert state == { '_type': 'execution', 'id': execution.id, 'name': '', 'description': '', 'state': { '_type': ':sorted_map', 'items': { 'start_node': { '_type': 'node', 'type': 'action', 'id': 'start_node', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', 'email': None, }, }, }, }, 'milestone': False, 'name': '', 'description': '', }, 'exit': { '_type': 'node', 'type': 'exit', 'id': 'exit', 'state': 'valid', 'comment': '', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'forms': [], 'state': 'valid', 'user': { '_type': 'user', 'identifier': '__system__', 'fullname': 'System', 'email': None, }, }, }, }, 'milestone': False, 'name': 'Exit exit', 'description': 'Exit exit', }, 'final_node': { '_type': 'node', 'type': 'action', 'id': 'final_node', 'state': 'unfilled', 'comment': '', 'actors': { '_type': ':map', 'items': {}, }, 'milestone': False, 'name': '', 'description': '', }, }, 'item_order': ['start_node', 'exit', 'final_node'], }, 'status': 'finished', 'actors': { 'exit': '__system__', 'start_node': 'juan', }, } eval_context = make_context({'values': values}, {}) eval_actor_map = make_actor_map({'values': values}) expected_context = { '_env': [{}], '_execution': [{ 'name': '', 'get_name_display': '', 'description': '', 'get_description_display': '', }], } assert {k: list(v.all()) for k, v in eval_context.items()} == expected_context expected_actor_map = { '_execution': [{ 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, }], } for frms in eval_actor_map.values(): for frm in frms: for fld in frm.values(): assert fld.pop('set_at') assert eval_actor_map == expected_actor_map
def test_call_node_render(config, mongo, mocker): mocker.patch('cacahuate.tasks.handle.delay') handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('call-render.2020-04-24.xml', 'start_node') execution = ptr.execution.get() value = random_string() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), }) # teardown of first node and wakeup of call node handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('start_form', [ { 'name': 'data', 'name': 'data', 'value': value, 'value_caption': value, 'state': 'valid', }, ]) ], }) assert Pointer.get(ptr.id).status == 'finished' ptr = next(execution.pointers.q().filter(status='ongoing')) assert ptr.node_id == 'call' new_ptr = next(Pointer.q().filter(node_id='start_node', status='ongoing')) # aditional rabbit call for new process args = handle.delay.call_args_list[0][0][0] assert json.loads(args) == { 'command': 'step', 'pointer_id': new_ptr.id, 'user_identifier': '__system__', 'input': [ Form.state_json('start_form', [ { 'label': 'Info', 'name': 'data', 'state': 'valid', 'type': 'text', 'value': value, 'value_caption': value, 'hidden': False, }, ]) ], } # normal rabbit call args = handle.delay.call_args_list[1][0][0] json_res = json.loads(args) # check execution_id exists assert json_res['input'][0]['inputs']['items']['execution'].pop('value') assert json_res['input'][0]['inputs']['items']['execution'].pop( 'value_caption') assert json_res == { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [ Form.state_json('call', [ { 'name': 'execution', 'label': 'execution', 'type': 'text', 'hidden': False, 'state': 'valid', }, ]) ], } # mongo log registry created for new process reg = next(mongo[config["POINTER_COLLECTION"]].find({ 'id': new_ptr.id, })) assert reg['node']['id'] == 'start_node' # mongo execution registry created for new process reg = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': new_ptr.execution.get().id, })) assert reg['name'] == 'Simplest process ever started with: ' + value # teardown of the call node and end of first execution handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [], }) # old execution is gone, new is here assert Execution.get(execution.id).status == 'finished' assert Pointer.get(ptr.id).status == 'finished' execution = next(Execution.q().filter(status='ongoing')) assert execution.process_name == 'simple.2018-02-19.xml'
def test_call_node_render(config, mongo): handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('call-render.2020-04-24.xml', 'start_node') channel = MagicMock() execution = ptr.proxy.execution.get() value = random_string() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), }) # teardown of first node and wakeup of call node handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('start_form', [ { 'name': 'data', 'name': 'data', 'value': value, 'value_caption': value, }, ]) ], }, channel) assert Pointer.get(ptr.id) is None ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'call' new_ptr = next(Pointer.q().filter(node_id='start_node')) # aditional rabbit call for new process args = channel.basic_publish.call_args_list[0][1] assert args['exchange'] == '' assert args['routing_key'] == config['RABBIT_QUEUE'] assert json.loads(args['body']) == { 'command': 'step', 'pointer_id': new_ptr.id, 'user_identifier': '__system__', 'input': [ Form.state_json('start_form', [ { 'label': 'Info', 'name': 'data', 'state': 'valid', 'type': 'text', 'value': value, 'value_caption': value, 'hidden': False, }, ]) ], } # normal rabbit call args = channel.basic_publish.call_args_list[1][1] assert args['exchange'] == '' assert args['routing_key'] == config['RABBIT_QUEUE'] assert json.loads(args['body']) == { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [], } # mongo log registry created for new process reg = next(mongo[config["POINTER_COLLECTION"]].find({ 'id': new_ptr.id, })) assert reg['node']['id'] == 'start_node' # mongo execution registry created for new process reg = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': new_ptr.proxy.execution.get().id, })) assert reg['name'] == 'Simplest process ever started with: ' + value # teardown of the call node and end of first execution handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': [], }, channel) # old execution is gone, new is here assert Execution.get(execution.id) is None assert Pointer.get(ptr.id) is None execution = Execution.get_all()[0] assert execution.process_name == 'simple.2018-02-19.xml'