def test_finish_execution(config, mongo): handler = Handler(config) p_0 = make_pointer('simple.2018-02-19.xml', 'manager') execution = p_0.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'status': 'ongoing', 'id': execution.id }) reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert execution.id == reg['id'] handler.finish_execution(execution) reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert reg['status'] == 'finished' assert_near_date(reg['finished_at'])
def test_resistance_dead_pointer(config): handler = Handler(config) # this is what we test handler(MagicMock(), MagicMock(), None, json.dumps({ 'command': 'step', 'pointer_id': 'nones', }))
def test_resistance_dead_pointer(config): handler = Handler(config) with pytest.raises(InconsistentState): handler(json.dumps({ 'command': 'step', 'pointer_id': 'nones', }))
def test_resistance_node_not_found(config, mongo): handler = Handler(config) ptr = make_pointer('wrong.2018-04-11.xml', 'start_node') exc = ptr.proxy.execution.get() user = make_user('juan', 'Juan') mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': exc.id, 'state': Xml.load(config, exc.process_name).get_state(), }) # this is what we test handler(MagicMock(), MagicMock(), None, json.dumps({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': {}, }))
def test_recover_step(config): handler = Handler(config) ptr = make_pointer('simple.2018-02-19.xml', 'mid_node') manager = make_user('juan_manager', 'Manager') pointer, user, input = \ handler.recover_step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': 'juan_manager', 'input': [[ 'auth_form', [{ 'auth': 'yes', }], ]], }) assert pointer.id == pointer.id assert user.id == manager.id
def test_compact_values(config): handler = Handler(config) names = [random_string() for _ in '123'] values = handler.compact_values([ Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[0], }, ]), Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[1], }, ]), Form.state_json('form2', [ { 'name': 'name', 'type': 'text', 'value': names[2], }, ]), ]) assert values == { 'values.form1': [ { 'name': name, } for name in names[:2] ], 'values.form2': [ { 'name': names[2], }, ], }
def test_create_pointer(config): handler = Handler(config) xml = Xml.load(config, 'simple') xmliter = iter(xml) node = Action(next(xmliter), xmliter) exc = Execution.validate( process_name='simple.2018-02-19.xml', name='nombre', name_template='nombre', description='description', description_template='description', started_at=make_date(2020, 8, 21, 4, 5, 6), status='ongoing', ).save() pointer = handler.create_pointer(node, exc) execution = pointer.proxy.execution.get() assert pointer.node_id == 'start_node' assert execution.process_name == 'simple.2018-02-19.xml' assert execution.proxy.pointers.count() == 1
def test_resistance_node_not_found(config, mongo): handler = Handler(config) ptr = make_pointer('wrong.2018-04-11.xml', 'start_node') exc = ptr.execution.get() user = make_user('juan', 'Juan') mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': exc.id, 'state': Xml.load(config, exc.process_name).get_state(), }) with pytest.raises(MisconfiguredProvider): handler( json.dumps({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': {}, }))
def test_store_data_from_response(config, mocker, mongo): expected_name = random_string() expected_age_1 = randint(0, 100) expected_age_2 = randint(0, 100) request_response = { 'params': { 'name': expected_name, }, 'items': [ [ { 'age': expected_age_1, }, { 'age': expected_age_2, }, ], ], } request_response_s = json.dumps(request_response) class ResponseMock: status_code = 200 text = request_response_s def json(self): return 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-captures.2019-08-08.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, 'request-captures').get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], } }) # teardown of first node and wakeup of request node handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('request', [ { '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 == 'request_node' # assert requests is called requests.request.assert_called_once() args, kwargs = requests.request.call_args assert args[0] == 'GET' assert args[1] == 'http://localhost/' assert kwargs['data'] == '' assert kwargs['headers'] == { 'content-type': 'application/json', } # aditional rabbit call for new process args = channel.basic_publish.call_args_list[0][1] expected_inputs = [ Form.state_json('request_node', [ { 'name': 'status_code', 'state': 'valid', 'type': 'int', 'value': 200, 'value_caption': '200', 'hidden': False, 'label': 'Status Code', }, { 'name': 'raw_response', 'state': 'valid', 'type': 'text', 'value': request_response_s, 'value_caption': request_response_s, 'hidden': False, 'label': 'Response', }, ]), Form.state_json('capture1', [ { 'name': 'name', 'state': 'valid', 'type': 'text', 'value': expected_name, 'value_caption': expected_name, 'hidden': False, 'label': 'Nombre', }, ]), Form.state_json('capture2', [ { 'name': 'age', 'state': 'valid', 'type': 'int', 'value': expected_age_1, 'value_caption': str(expected_age_1), 'hidden': False, 'label': 'Edad', }, ]), Form.state_json('capture2', [ { 'name': 'age', 'state': 'valid', 'type': 'int', 'value': expected_age_2, 'value_caption': str(expected_age_2), 'hidden': False, 'label': 'Edad', }, ]), ] 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': expected_inputs, } handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': expected_inputs, }, channel) state = mongo[config["EXECUTION_COLLECTION"]].find_one({ 'id': execution.id, }) assert state['state']['items']['request_node'] == { '_type': 'node', 'type': 'request', 'id': 'request_node', 'comment': '', 'state': 'valid', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'fullname': 'System', 'identifier': '__system__', }, 'forms': expected_inputs, }, }, }, 'milestone': False, 'name': 'Request request_node', 'description': 'Request request_node', } assert state['values'] == { '_execution': [{ 'name': '', 'description': '', }], 'capture1': [{ 'name': expected_name, }], 'capture2': [ { 'age': expected_age_1, }, { 'age': expected_age_2, }, ], 'request': [{ 'data': value, }], 'request_node': [{ 'raw_response': request_response_s, 'status_code': 200, }], }
def test_handle_request_node(config, mocker, mongo): 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.2018-05-18.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, 'request').get_state(), }) # teardown of first node and wakeup of request node handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('request', [ { '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 == 'request_node' # assert requests is called requests.request.assert_called_once() args, kwargs = requests.request.call_args assert args[0] == 'GET' assert args[1] == 'http://localhost/mirror?data=' + value assert kwargs['data'] == '{"data":"' + value + '"}' assert kwargs['headers'] == { 'content-type': 'application/json', 'x-url-data': value, } # aditional rabbit call for new process args = channel.basic_publish.call_args_list[0][1] expected_inputs = [ Form.state_json('request_node', [ { 'name': 'status_code', 'state': 'valid', 'type': 'int', 'value': 200, 'value_caption': '200', 'hidden': False, 'label': 'Status Code', }, { 'name': 'raw_response', 'state': 'valid', 'type': 'text', 'value': 'request response', 'value_caption': 'request response', 'hidden': False, 'label': 'Response', }, ]) ] 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': expected_inputs, } handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': '__system__', 'input': expected_inputs, }, channel) state = mongo[config["EXECUTION_COLLECTION"]].find_one({ 'id': execution.id, }) assert state['state']['items']['request_node'] == { '_type': 'node', 'type': 'request', 'id': 'request_node', 'comment': '', 'state': 'valid', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'fullname': 'System', 'identifier': '__system__', }, 'forms': expected_inputs, }, }, }, 'milestone': False, 'name': 'Request request_node', 'description': 'Request request_node', }
def test_wakeup(config, mongo): ''' the first stage in a node's lifecycle ''' # setup stuff handler = Handler(config) pointer = make_pointer('simple.2018-02-19.xml', 'start_node') execution = pointer.proxy.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'}, }) channel = MagicMock() # will wakeup the second node handler.call({ 'command': 'step', 'pointer_id': pointer.id, 'user_identifier': juan.identifier, 'input': [], }, channel) # test manager is notified channel.basic_publish.assert_called_once() channel.exchange_declare.assert_called_once() args = channel.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': Pointer.get_all()[0].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['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.proxy.tasks.count() == 1 task = manager.proxy.tasks.get()[0] assert isinstance(task, Pointer) assert task.node_id == 'mid_node' assert task.proxy.execution.get().id == execution.id
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') channel = MagicMock() 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', }, ]) ] execution = xml.start(node, input, mongo, channel, user.identifier) ptr = execution.proxy.pointers.get()[0] handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] 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.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] 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.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', }, ]) ], }, channel)
def test_invalidated_conditional(config, mongo): ''' a condiitonal depends on an invalidated field, if it changes during the second response it must take the second value ''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') process_filename = 'condition_invalidated.2019-10-08.xml' ptr = make_pointer(process_filename, 'start_node') execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], }, }) # initial rabbit call channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'value', 'type': 'int', 'value': 3, 'value_caption': '3', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # arrives to if_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'if_node' # if_node's condition is True args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('if_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': True, 'value_caption': 'True', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # if rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to if_validation ptr = Pointer.get_all()[0] assert ptr.node_id == 'if_validation_node' # if's call to validation channel.reset_mock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('if_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:form1.value', }], 'value_caption': '', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # returns to start_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'start_node' # second lap channel.reset_mock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'value', 'type': 'int', 'value': -3, 'value_caption': '-3', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # arrives to if_node again ptr = Pointer.get_all()[0] assert ptr.node_id == 'if_node' args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('if_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # if second rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to elif_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'elif_node' # elif node's condition is true args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('elif_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': True, 'value_caption': 'True', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # elif rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to elif_validation ptr = Pointer.get_all()[0] assert ptr.node_id == 'elif_validation_node' # elif's call to validation channel.reset_mock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('elif_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'Ugly... nope', 'value_caption': 'Ugly... nope', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:form1.value', }], 'value_caption': '', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # returns to start_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'start_node' # third lap channel.reset_mock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'value', 'type': 'int', 'value': 0, 'value_caption': '0', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # arrives to if_node again again ptr = Pointer.get_all()[0] assert ptr.node_id == 'if_node' args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('if_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # if third rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to elif_node again ptr = Pointer.get_all()[0] assert ptr.node_id == 'elif_node' args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('elif_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # elif second rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to else_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'else_node' # else node's condition is true args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('else_node', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': True, 'value_caption': 'True', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call # else rabbit call channel.reset_mock() handler.call(rabbit_call, channel) channel.basic_publish.assert_called_once() # arrives to if_validation ptr = Pointer.get_all()[0] assert ptr.node_id == 'else_validation_node' # else's call to validation channel.reset_mock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('else_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'What? No!', 'value_caption': 'What? No!', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:form1.value', }], 'value_caption': '', }, ]) ], }, channel) channel.basic_publish.assert_called_once() # returns to start_node ptr = Pointer.get_all()[0] assert ptr.node_id == 'start_node' # state is coherent state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] 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': 'What? No!', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [ Form.state_json( 'form1', [ { 'name': 'value', 'type': 'int', 'value': 0, 'value_caption': '0', 'state': 'invalid', }, ], state='invalid', ) ], 'state': 'invalid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, 'milestone': False, 'name': 'Node 1', 'description': 'the value subject to inspection', }, 'if_node': { '_type': 'node', 'type': 'if', 'id': 'if_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'forms': [ Form.state_json( 'if_node', [ { 'name': 'condition', 'value': False, 'value_caption': 'False', 'type': 'bool', 'state': 'invalid', }, ], state='invalid', ) ], 'state': 'invalid', 'user': { '_type': 'user', 'identifier': '__system__', 'fullname': 'System', }, }, }, }, 'milestone': False, 'name': 'IF if_node', 'description': 'IF if_node', }, 'if_validation_node': { '_type': 'node', 'type': 'validation', 'id': 'if_validation_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [ Form.state_json( 'if_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'invalid', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': ('I do not like it'), }, { 'name': 'inputs', 'value': [{ 'ref': ('start_node.juan.0:form1' '.value'), }], 'value_caption': '', }, ], state='invalid', ) ], 'state': 'invalid', 'user': { '_type': 'user', 'fullname': 'Juan', 'identifier': 'juan' }, } } }, 'name': 'The validation', 'description': 'This node invalidates the original value', 'milestone': False, }, 'elif_node': { '_type': 'node', 'type': 'elif', 'id': 'elif_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'state': 'invalid', 'user': { '_type': 'user', 'fullname': 'System', 'identifier': '__system__' }, 'forms': [ Form.state_json( 'elif_node', [ { 'name': 'condition', 'value': False, 'value_caption': 'False', 'type': 'bool', 'state': 'invalid', }, ], state='invalid', ) ], } } }, 'name': 'ELIF elif_node', 'description': 'ELIF elif_node', 'milestone': False, }, 'elif_validation_node': { '_type': 'node', 'type': 'validation', 'id': 'elif_validation_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'state': 'invalid', 'user': { '_type': 'user', 'fullname': 'Juan', 'identifier': 'juan' }, 'forms': [ Form.state_json( 'elif_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'invalid', }, { 'name': 'comment', 'value': 'Ugly... nope', 'value_caption': ('Ugly... nope'), }, { 'name': 'inputs', 'value': [{ 'ref': ('start_node.juan.0:form1' '.value'), }], 'value_caption': '', }, ], state='invalid', ) ], } } }, 'name': 'The validation', 'description': ('This node also invalidates the original value'), 'milestone': False, }, 'else_node': { '_type': 'node', 'type': 'else', 'id': 'else_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { '__system__': { '_type': 'actor', 'state': 'invalid', 'user': { '_type': 'user', 'fullname': 'System', 'identifier': '__system__' }, 'forms': [ Form.state_json( 'else_node', [ { 'name': 'condition', 'value': True, 'value_caption': 'True', 'type': 'bool', 'state': 'invalid', }, ], state='invalid', ) ], } } }, 'name': 'ELSE else_node', 'description': 'ELSE else_node', 'milestone': False, }, 'else_validation_node': { '_type': 'node', 'type': 'validation', 'id': 'else_validation_node', 'state': 'invalid', 'comment': 'What? No!', 'actors': { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'state': 'invalid', 'user': { '_type': 'user', 'fullname': 'Juan', 'identifier': 'juan' }, 'forms': [ Form.state_json('else_validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', 'state': 'invalid', }, { 'name': 'comment', 'value': 'What? No!', 'value_caption': 'What? No!', }, { 'name': 'inputs', 'value': [{ 'ref': ('start_node.juan.0:form1' '.value'), }], 'value_caption': '', }, ], state='invalid'), ], } } }, 'name': 'The validation', 'description': ('This node invalidates the original value, too'), 'milestone': False, } }, 'item_order': [ 'start_node', 'if_node', 'if_validation_node', 'elif_node', 'elif_validation_node', 'else_node', 'else_validation_node' ], }, 'values': { '_execution': [{ 'name': '', 'description': '', }], 'form1': [{ 'value': 0, }], 'if_node': [{ 'condition': False, }], 'if_validation_node': [ { 'response': 'reject', 'comment': 'I do not like it', 'inputs': [ { 'ref': 'start_node.juan.0:form1.value' }, ], }, ], 'elif_node': [ { 'condition': False, }, ], 'elif_validation_node': [ { 'response': 'reject', 'comment': 'Ugly... nope', 'inputs': [ { 'ref': 'start_node.juan.0:form1.value', }, ], }, ], 'else_node': [ { 'condition': True, }, ], 'else_validation_node': [ { 'response': 'reject', 'comment': 'What? No!', 'inputs': [ { 'ref': 'start_node.juan.0:form1.value', }, ], }, ], }, 'actors': { 'start_node': 'juan', 'if_node': '__system__', 'if_validation_node': 'juan', 'elif_node': '__system__', 'elif_validation_node': 'juan', 'else_node': '__system__', 'else_validation_node': 'juan', }, 'actor_list': [{ 'node': 'start_node', 'identifier': 'juan', }, { 'node': 'if_node', 'identifier': '__system__', }, { 'node': 'if_validation_node', 'identifier': 'juan', }, { 'node': 'elif_node', 'identifier': '__system__', }, { 'node': 'elif_validation_node', 'identifier': 'juan', }, { 'node': 'else_node', 'identifier': '__system__', }, { 'node': 'else_validation_node', 'identifier': 'juan', }], }
def test_anidated_conditions(config, mongo): ''' conditional node won't be executed if its condition is false ''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('anidated-conditions.2018-05-17.xml', 'a') channel = MagicMock() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': ptr.proxy.execution.get().id, 'state': Xml.load(config, 'anidated-conditions').get_state(), }) handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('a', [ { 'name': 'a', 'value': '1', 'value_caption': '1', }, ]) ], }, channel) # assertions assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'outer' # rabbit called args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('outer', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': True, 'value_caption': 'True', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call handler.call(rabbit_call, channel) # assertions assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'b' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('b', [ { 'name': 'b', 'value': '-1', 'value_caption': '-1', }, ]) ], }, channel) # assertions assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'inner1' # rabbit called args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('inner1', [ { 'name': 'condition', 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call handler.call(rabbit_call, channel) # assertions assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'f' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('f', [ { 'name': 'f', 'value': '-1', 'value_caption': '-1', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'g'
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') channel = MagicMock() execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'validation-reloaded').get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], }, }) # first call to node1 handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'task', 'value': '1', 'value_caption': '1', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node2' # first call to node2 handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form2', [ { 'name': 'task', 'value': '1', 'value_caption': '1', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node3' # first call to node3 handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form3', [ { 'name': 'task', 'value': '1', 'value_caption': '1', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node4' # first call to validation handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('node4', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', }, { 'name': 'inputs', 'value': [{ 'ref': 'node1.juan.0:form1.task', }], 'value_caption': '', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node1' # second call to node1 handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form1', [ { 'name': 'task', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node2' # second call to node2 handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form2', [ { 'name': 'task', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node4' # second call to validation handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('node4', [ { 'name': 'response', 'value': 'accept', 'value_caption': 'accept', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', }, { 'name': 'inputs', 'value': None, 'value_caption': 'None', }, ]) ], }, channel) ptr = Pointer.get_all()[0] assert ptr.node_id == 'node5' # first call to last node handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form5', [ { 'name': 'task', 'value': '1', 'value_caption': '1', }, ]) ], }, channel) assert Pointer.get_all() == [] # state is coherent state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] del state['finished_at'] 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', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, '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', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, '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', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, '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', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', }, { 'name': 'inputs', 'value': None, 'value_caption': 'None', }, ]) ], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, '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', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, 'milestone': False, 'name': 'Quinto paso', 'description': 'terminar', }, }, 'item_order': ['node1', 'node2', 'node3', 'node4', 'node5'], }, 'status': 'finished', 'values': { '_execution': [{ 'name': '', 'description': '', }], 'node4': [{ 'comment': 'I like it', 'inputs': None, 'response': 'accept', }], 'form1': [{ 'task': '2' }], 'form2': [{ 'task': '2' }], 'form3': [{ 'task': '1' }], 'form5': [{ 'task': '1' }], }, 'actors': { 'node1': 'juan', 'node2': 'juan', 'node3': 'juan', 'node4': 'juan', 'node5': 'juan', }, 'actor_list': [ { 'node': 'node1', 'identifier': 'juan', }, { 'node': 'node2', 'identifier': 'juan', }, { 'node': 'node3', 'identifier': 'juan', }, { 'node': 'node4', 'identifier': 'juan', }, { 'node': 'node5', 'identifier': 'juan', }, ], }
def test_call_handler_delete_process(config, mongo): handler = Handler(config) channel = MagicMock() method = {'delivery_tag': True} properties = "" pointer = make_pointer('simple.2018-02-19.xml', 'requester') execution_id = pointer.proxy.execution.get().id 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(channel, method, properties, body) reg = next(mongo[config["EXECUTION_COLLECTION"]].find()) assert reg['id'] == execution_id assert reg['status'] == "cancelled" assert_near_date(reg['finished_at']) assert Execution.count() == 0 assert Pointer.count() == 0 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_patch_invalidate(config, mongo): ''' patch that only invalidates ''' handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('exit_request.2018-03-20.xml', 'requester') execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'exit_request').get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], }, }) # requester fills the form channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('exit_form', [ { '_type': 'field', 'state': 'valid', 'value': 'want to pee', 'value_caption': 'want to pee', 'name': 'reason', }, ]), Form.state_json('code_form', [ { '_type': 'field', 'state': 'valid', 'value': 'kadabra', 'value_caption': 'kadabra', 'name': 'code', }, ]), ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'manager' # manager says yes channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('auth_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'auth', }, ]) ], }, channel) security_ptr = execution.proxy.pointers.get()[0] assert security_ptr.node_id == 'security' # patch request happens channel = MagicMock() handler.patch( { 'command': 'patch', 'execution_id': execution.id, 'user_identifier': user.identifier, 'comment': 'pee is not a valid reason', 'inputs': [{ 'ref': 'requester.juan.0:exit_form.reason', }], }, channel) ptr = execution.proxy.pointers.get()[0] # pointer is in the first node assert ptr.node_id == 'requester' # nodes with pointers are marked as unfilled or invalid in execution state exc_state = mongo[config['EXECUTION_COLLECTION']].find_one({ 'id': execution.id, }) assert exc_state['state']['items']['security']['state'] == 'unfilled' # dependent information is invalidated assert exc_state['state']['items']['manager']['state'] == 'invalid' # entries in pointer collection get a state of cancelled by patch request security_pointer_state = mongo[config['POINTER_COLLECTION']].find_one({ 'id': security_ptr.id, }) assert security_pointer_state['state'] == 'cancelled' execution = mongo[config["EXECUTION_COLLECTION"]].find_one({ 'id': execution.id, }) expected_values = { '_execution': [{ 'name': '', 'description': '', }], 'auth_form': [{ 'auth': 'yes' }], 'code_form': [{ 'code': 'kadabra' }], 'exit_form': [{ 'reason': 'want to pee' }], } assert execution['values'] == expected_values
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.proxy.execution.get() User(identifier='juan').save() manager = User(identifier='manager').save() manager2 = User(identifier='manager2').save() assert manager not in execution.proxy.actors.get() assert execution not in manager.proxy.activities.get() manager.proxy.tasks.set([p_0]) manager2.proxy.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': { '_execution': [{ 'name': '', 'description': '', }], }, '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': {}, }, }) channel = MagicMock() # will teardown mid_node handler.call({ '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', }, ])], }, channel) # assertions assert Pointer.get(p_0.id) is None assert Pointer.count() == 1 assert Pointer.get_all()[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, }, 'forms': [Form.state_json('mid_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'data', }, ])], }, }, } # tasks where deleted from user assert manager.proxy.tasks.count() == 0 assert manager2.proxy.tasks.count() == 0 # 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, }, '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', ], } assert reg['values'] == { '_execution': [{ 'name': '', 'description': '', }], 'mid_form': [{ 'data': 'yes', }], } assert reg['actors'] == { 'start_node': 'juan', 'mid_node': 'manager', } assert manager in execution.proxy.actors assert execution in manager.proxy.activities
def test_patch_set_value_multiple(config, mongo): ''' patch and set new data (multiple)''' handler = Handler(config) user = make_user('kysxd', 'KYSXD') ptr = make_pointer('gift-request.2020-04-05.xml', 'solicitud') 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, 'gift-request').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, }, ], }], }, ], }) # requester fills the form handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('viaticos', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'Si', 'name': 'galletas', }, ]), Form.state_json('condicionales', [ { '_type': 'field', 'state': 'valid', 'value': 'bueno', 'value_caption': 'Si', 'name': 'comportamiento', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Max Iron', 'value_caption': 'Max Iron', 'name': 'regalo', }, { '_type': 'field', 'state': 'valid', 'value': 350.0, 'value_caption': 350.0, 'name': 'costo', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Mega boy', 'value_caption': 'Mega boy', 'name': 'regalo', }, { '_type': 'field', 'state': 'valid', 'value': 120.0, 'value_caption': 120.0, 'name': 'costo', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Brobocop', 'value_caption': 'Brobocop', 'name': 'regalo', }, ]), ], }) ptr = next(execution.pointers.q().filter(status='ongoing')) assert ptr.node_id == 'if_malo' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('if_malo', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ])], }) auth_ptr = next(execution.pointers.q().filter(status='ongoing')) assert auth_ptr.node_id == 'preparacion' # patch request happens handler.patch({ 'command': 'patch', 'execution_id': execution.id, 'user_identifier': user.identifier, 'comment': 'Informacion equivocada', 'inputs': [ { 'ref': 'solicitud.kysxd.3:regalos.regalo', 'value': 'Mega bro', 'value_caption': 'Mega bro', }, { 'ref': 'solicitud.kysxd.2:regalos.regalo', 'value': 'Action bro', 'value_caption': 'Action bro', }, { 'ref': 'solicitud.kysxd.2:regalos.costo', 'value': 10.0, 'value_caption': 10.0, }, ], }) ptr = next(execution.pointers.q().filter(status='ongoing')) assert ptr.node_id == 'if_malo' # nodes with pointers are marked as unfilled or invalid in execution state e_state = mongo[config['EXECUTION_COLLECTION']].find_one({ 'id': execution.id, }) # values sent are set actor = e_state['state']['items']['solicitud']['actors']['items']['kysxd'] # sanity check for the non-multiple forms _form = actor['forms'][0] _input = _form['inputs']['items']['galletas'] assert _input['value'] == 'yes' assert _input['value_caption'] == 'Si' _form = actor['forms'][1] _input = _form['inputs']['items']['comportamiento'] assert _input['value'] == 'bueno' assert _input['value_caption'] == 'Si' # check multiforms # first _form = actor['forms'][2] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Action bro' assert _input['value_caption'] == 'Action bro' _form = actor['forms'][2] _input = _form['inputs']['items']['costo'] assert _input['value'] == 10.0 assert _input['value_caption'] == 10.0 # second _form = actor['forms'][3] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Mega bro' assert _input['value_caption'] == 'Mega bro' _form = actor['forms'][3] _input = _form['inputs']['items']['costo'] assert _input['value'] == 120.0 assert _input['value_caption'] == 120.0 # third _form = actor['forms'][4] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Brobocop' assert _input['value_caption'] == 'Brobocop' # unexistant key doesn't update assert 'costo' not in _form['inputs']['items'] values = e_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': '', }], 'viaticos': [{ 'galletas': 'yes', 'get_galletas_display': 'Si', }], 'condicionales': [{ 'comportamiento': 'bueno', 'get_comportamiento_display': 'Si', }], 'if_malo': [{ 'condition': False, 'get_condition_display': 'False', }], 'regalos': [ { 'regalo': 'Action bro', 'get_regalo_display': 'Action bro', 'costo': 10.0, 'get_costo_display': 10.0, }, { 'regalo': 'Mega bro', 'get_regalo_display': 'Mega bro', 'costo': 120.0, 'get_costo_display': 120.0, }, { 'regalo': 'Brobocop', 'get_regalo_display': 'Brobocop', }, ], } assert { k: list(v.all()) for k, v in eval_context.items() } == expected_context expected_actor_map = { '_execution': [ { 'name': { 'actor': '__system__', }, 'description': { 'actor': '__system__', }, } ], 'viaticos': [ { 'galletas': { 'actor': 'kysxd', } } ], 'condicionales': [ { 'comportamiento': { 'actor': 'kysxd' } } ], 'if_malo': [ { 'condition': { 'actor': 'kysxd' }, } ], 'regalos': [ { 'regalo': { 'actor': 'kysxd' }, 'costo': { 'actor': 'kysxd' }, }, { 'regalo': { 'actor': 'kysxd' }, 'costo': { 'actor': 'kysxd' }, }, { 'regalo': { 'actor': 'kysxd' }, } ], } 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_patch_set_value(config, mongo): ''' patch and set new data ''' handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('exit_request.2018-03-20.xml', 'requester') 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, 'exit_request').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, }, ], }], }, ], }) # requester fills the form handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('code_form', [ { '_type': 'field', 'state': 'valid', 'value': 'kadabra', 'value_caption': 'kadabra', 'name': 'code', }, ]), Form.state_json('exit_form', [ { '_type': 'field', 'state': 'valid', 'value': 'want to pee', 'value_caption': 'want to pee', 'name': 'reason', }, ]), ], }) ptr = next(execution.pointers.q().filter(status='ongoing')) assert ptr.node_id == 'manager' # manager says yes handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [Form.state_json('auth_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'auth', }, ])], }) security_ptr = next(execution.pointers.q().filter(status='ongoing')) assert security_ptr.node_id == 'security' # patch request happens handler.patch({ 'command': 'patch', 'execution_id': execution.id, 'user_identifier': user.identifier, 'comment': 'pee is not a valid reason', 'inputs': [ { 'ref': 'requester.juan.1:exit_form.reason', 'value': 'am hungry', 'value_caption': 'am hungry', 'state': 'valid', }, { 'ref': 'requester.juan.0:code_form.code', 'value': 'alakazam', 'value_caption': 'alakazam', 'state': 'valid', }, ], }) ptr = next(execution.pointers.q().filter(status='ongoing')) # pointer is in the manager's node assert ptr.node_id == 'manager' # nodes with pointers are marked as unfilled or invalid in execution state exc_state = mongo[config['EXECUTION_COLLECTION']].find_one({ 'id': execution.id, }) # values sent are set actor = exc_state['state']['items']['requester']['actors']['items']['juan'] _input_0 = actor['forms'][0]['inputs']['items']['code'] assert _input_0['value'] == 'alakazam' assert _input_0['value_caption'] == 'alakazam' _input_1 = actor['forms'][1]['inputs']['items']['reason'] assert _input_1['value'] == 'am hungry' assert _input_1['value_caption'] == 'am hungry' execution = mongo[config["EXECUTION_COLLECTION"]].find_one({ 'id': execution.id, }) values = execution.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': '', }], 'auth_form': [{}], 'code_form': [{ 'code': 'alakazam', 'get_code_display': 'alakazam', }], 'exit_form': [{ 'reason': 'am hungry', 'get_reason_display': 'am hungry', }], } assert { k: list(v.all()) for k, v in eval_context.items() } == expected_context expected_actor_map = { '_execution': [ { 'name': { 'actor': '__system__' }, 'description': { 'actor': '__system__' }, } ], 'auth_form': [{}], 'code_form': [ { 'code': { 'actor': 'juan' } } ], 'exit_form': [ { 'reason': { '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_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.proxy.execution.get() channel = MagicMock() 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.call( { '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], }, ]), Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[1], 'value_caption': names[1], }, ]), Form.state_json('form1', [ { 'name': 'name', 'type': 'text', 'value': names[2], 'value_caption': names[2], }, ]), ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] 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_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') channel = MagicMock() mongo[config["POINTER_COLLECTION"]].insert_one({ 'id': ptr.id, 'started_at': datetime(2018, 4, 1, 21, 45), 'finished_at': None, 'execution': { 'id': ptr.proxy.execution.get().id, }, 'node': { 'id': 'approval_node', }, 'actors': { '_type': ':map', 'items': {}, }, }) mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': ptr.proxy.execution.get().id, 'state': Xml.load(config, 'validation.2018-05-09').get_state(), 'actors': { 'start_node': 'juan', }, 'values': { '_execution': [{ 'name': '', 'description': '', }], }, }) # thing to test handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('approval_node', [ { 'name': 'response', 'value': 'accept', 'value_caption': 'accept', }, { 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0.task', }], 'value_caption': '', }, ]) ], }, channel) # assertions assert Pointer.get(ptr.id) is None new_ptr = Pointer.get_all()[0] 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'] == ptr.execution assert reg['node']['id'] == 'approval_node' assert reg['actors'] == { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, 'forms': [ Form.state_json('approval_node', [ { 'name': 'response', 'name': 'response', 'value': 'accept', 'value_caption': 'accept', }, { 'name': 'comment', 'name': 'comment', 'value': 'I like it', 'value_caption': 'I like it', }, { 'name': 'inputs', 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0.task', }], 'value_caption': '', }, ]) ], }, }, }
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') channel = MagicMock() execution = ptr.proxy.execution.get() 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': {}, }, }) 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', }, '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': { '_execution': [{ 'name': '', 'description': '', }], }, }) # will teardown the approval node handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('approval_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:work.task', }], 'value_caption': '', }, ]) ], }, channel) # assertions assert Pointer.get(ptr.id) is None new_ptr = Pointer.get_all()[0] assert new_ptr.node_id == 'start_node' assert new_ptr in user.proxy.tasks # data is invalidated state = next(mongo[config["EXECUTION_COLLECTION"]].find({ 'id': execution.id, })) del state['_id'] 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', }, }, }, }, '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', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.' 'juan.0:work.task', }], 'value_caption': '', }, ], state='invalid') ], 'state': 'invalid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }, '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'], }, 'values': { '_execution': [{ 'name': '', 'description': '', }], 'approval_node': [{ 'comment': 'I do not like it', 'response': 'reject', 'inputs': [{ 'ref': 'start_node.juan.0:work.task' }], }], }, 'actors': { 'approval_node': 'juan', }, 'actor_list': [{ 'node': 'approval_node', 'identifier': 'juan', }], } # 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'] == ptr.execution assert reg['node']['id'] == 'approval_node' assert reg['actors'] == { '_type': ':map', 'items': { 'juan': { '_type': 'actor', 'forms': [ Form.state_json('approval_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'comment', 'value': 'I do not like it', 'value_caption': 'I do not like it', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:work.task', }], 'value_caption': '', }, ]) ], 'state': 'valid', 'user': { '_type': 'user', 'identifier': 'juan', 'fullname': 'Juan', }, }, }, }
def test_patch_set_value_multiple(config, mongo): ''' patch and set new data (multiple)''' handler = Handler(config) user = make_user('kysxd', 'KYSXD') ptr = make_pointer('gift-request.2020-04-05.xml', 'solicitud') execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'gift-request').get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], } }) # requester fills the form channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('viaticos', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'Si', 'name': 'galletas', }, ]), Form.state_json('condicionales', [ { '_type': 'field', 'state': 'valid', 'value': 'bueno', 'value_caption': 'Si', 'name': 'comportamiento', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Max Iron', 'value_caption': 'Max Iron', 'name': 'regalo', }, { '_type': 'field', 'state': 'valid', 'value': 350.0, 'value_caption': 350.0, 'name': 'costo', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Mega boy', 'value_caption': 'Mega boy', 'name': 'regalo', }, { '_type': 'field', 'state': 'valid', 'value': 120.0, 'value_caption': 120.0, 'name': 'costo', }, ]), Form.state_json('regalos', [ { '_type': 'field', 'state': 'valid', 'value': 'Brobocop', 'value_caption': 'Brobocop', 'name': 'regalo', }, ]), ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'if_malo' channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('if_malo', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], }, channel) auth_ptr = execution.proxy.pointers.get()[0] assert auth_ptr.node_id == 'preparacion' # patch request happens channel = MagicMock() handler.patch( { 'command': 'patch', 'execution_id': execution.id, 'user_identifier': user.identifier, 'comment': 'Informacion equivocada', 'inputs': [ { 'ref': 'solicitud.kysxd.3:regalos.regalo', 'value': 'Mega bro', 'value_caption': 'Mega bro', }, { 'ref': 'solicitud.kysxd.2:regalos.regalo', 'value': 'Action bro', 'value_caption': 'Action bro', }, { 'ref': 'solicitud.kysxd.5:regalos.costo', 'value': 350.0, 'value_caption': 350.0, }, { 'ref': 'solicitud.kysxd.2:regalos.costo', 'value': 10.0, 'value_caption': 10.0, }, ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'if_malo' # nodes with pointers are marked as unfilled or invalid in execution state e_state = mongo[config['EXECUTION_COLLECTION']].find_one({ 'id': execution.id, }) # values sent are set actor = e_state['state']['items']['solicitud']['actors']['items']['kysxd'] # sanity check for the non-multiple forms _form = actor['forms'][0] _input = _form['inputs']['items']['galletas'] assert _input['value'] == 'yes' assert _input['value_caption'] == 'Si' _form = actor['forms'][1] _input = _form['inputs']['items']['comportamiento'] assert _input['value'] == 'bueno' assert _input['value_caption'] == 'Si' # check multiforms # first _form = actor['forms'][2] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Action bro' assert _input['value_caption'] == 'Action bro' _form = actor['forms'][2] _input = _form['inputs']['items']['costo'] assert _input['value'] == 10.0 assert _input['value_caption'] == 10.0 # second _form = actor['forms'][3] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Mega bro' assert _input['value_caption'] == 'Mega bro' _form = actor['forms'][3] _input = _form['inputs']['items']['costo'] assert _input['value'] == 120.0 assert _input['value_caption'] == 120.0 # third _form = actor['forms'][4] _input = _form['inputs']['items']['regalo'] assert _input['value'] == 'Brobocop' assert _input['value_caption'] == 'Brobocop' # unexistant key doesn't update assert 'costo' not in _form['inputs']['items'] expected_values = { '_execution': [{ 'name': '', 'description': '', }], 'viaticos': [{ 'galletas': 'yes' }], 'condicionales': [{ 'comportamiento': 'bueno' }], 'if_malo': [{ 'condition': False }], 'regalos': [ { 'regalo': 'Action bro', 'costo': 10.0 }, { 'regalo': 'Mega bro', 'costo': 120.0 }, { 'regalo': 'Brobocop' }, ], } assert e_state['values'] == expected_values
def test_invalidate_all_nodes(config, mongo): handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('all-nodes-invalidated.2018-05-24.xml', 'start_node') execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'all-nodes-invalidated').get_state(), }) channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('work', [ { 'name': 'task', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'request_node' args = channel.basic_publish.call_args[1] channel = MagicMock() handler.call(json.loads(args['body']), channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'call_node' args = channel.basic_publish.call_args[1] channel = MagicMock() handler.call(json.loads(args['body']), channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'if_node' args = channel.basic_publish.call_args[1] channel = MagicMock() handler.call(json.loads(args['body']), channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'validation_node' channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('validation_node', [ { 'name': 'response', 'value': 'reject', 'value_caption': 'reject', }, { 'name': 'inputs', 'value': [{ 'ref': 'start_node.juan.0:work.task', }], 'value_caption': '', }, { 'name': 'comment', 'value': '', 'value_caption': '', }, ]) ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'start_node' args = channel.basic_publish.call_args[1]
def test_patch_set_value(config, mongo): ''' patch and set new data ''' handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('exit_request.2018-03-20.xml', 'requester') execution = ptr.proxy.execution.get() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, 'exit_request').get_state(), 'values': { '_execution': [{ 'name': '', 'description': '', }], }, }) # requester fills the form channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('code_form', [ { '_type': 'field', 'state': 'valid', 'value': 'kadabra', 'value_caption': 'kadabra', 'name': 'code', }, ]), Form.state_json('exit_form', [ { '_type': 'field', 'state': 'valid', 'value': 'want to pee', 'value_caption': 'want to pee', 'name': 'reason', }, ]), ], }, channel) ptr = execution.proxy.pointers.get()[0] assert ptr.node_id == 'manager' # manager says yes channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('auth_form', [ { '_type': 'field', 'state': 'valid', 'value': 'yes', 'value_caption': 'yes', 'name': 'auth', }, ]) ], }, channel) security_ptr = execution.proxy.pointers.get()[0] assert security_ptr.node_id == 'security' # patch request happens channel = MagicMock() handler.patch( { 'command': 'patch', 'execution_id': execution.id, 'user_identifier': user.identifier, 'comment': 'pee is not a valid reason', 'inputs': [ { 'ref': 'requester.juan.1:exit_form.reason', 'value': 'am hungry', 'value_caption': 'am hungry', }, { 'ref': 'requester.juan.0:code_form.code', 'value': 'alakazam', 'value_caption': 'alakazam', }, ], }, channel) ptr = execution.proxy.pointers.get()[0] # pointer is in the manager's node assert ptr.node_id == 'manager' # nodes with pointers are marked as unfilled or invalid in execution state exc_state = mongo[config['EXECUTION_COLLECTION']].find_one({ 'id': execution.id, }) # values sent are set actor = exc_state['state']['items']['requester']['actors']['items']['juan'] _input_0 = actor['forms'][0]['inputs']['items']['code'] assert _input_0['value'] == 'alakazam' assert _input_0['value_caption'] == 'alakazam' _input_1 = actor['forms'][1]['inputs']['items']['reason'] assert _input_1['value'] == 'am hungry' assert _input_1['value_caption'] == 'am hungry' execution = mongo[config["EXECUTION_COLLECTION"]].find_one({ 'id': execution.id, }) expected_values = { '_execution': [{ 'name': '', 'description': '', }], 'auth_form': [{ 'auth': 'yes' }], 'code_form': [{ 'code': 'alakazam' }], 'exit_form': [{ 'reason': 'am hungry' }], } assert execution['values'] == expected_values
def test_ifelifelse_else(config, mongo): ''' else will be executed if preceding condition is false''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('else.2018-07-10.xml', 'start_node') channel = MagicMock() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': ptr.proxy.execution.get().id, 'state': Xml.load(config, 'else').get_state(), }) handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('secret01', [ { 'name': 'password', 'type': 'text', 'value': 'cuca', 'value_caption': 'cuca', }, ]) ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'condition01' # rabbit called channel.basic_publish.assert_called_once() args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('condition01', [ { 'name': 'condition', 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call channel = MagicMock() handler.call(rabbit_call, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'elif01' # rabbit called channel.basic_publish.assert_called_once() args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('elif01', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call channel = MagicMock() handler.call(rabbit_call, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'else01' # rabbit called channel.basic_publish.assert_called_once() args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('else01', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': True, 'value_caption': 'True', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call channel = MagicMock() handler.call(rabbit_call, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'action03' # rabbit called to notify the user channel.basic_publish.assert_called_once() args = channel.basic_publish.call_args[1] assert args['exchange'] == 'charpe_notify' channel = MagicMock() handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form01', [ { 'name': 'answer', 'value': 'answer', 'value_caption': 'answer', }, ]) ], }, channel) # execution finished assert len(Pointer.get_all()) == 0 assert len(Execution.get_all()) == 0
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_false_condition_node(config, mongo): ''' conditional node won't be executed if its condition is false ''' # test setup handler = Handler(config) user = make_user('juan', 'Juan') ptr = make_pointer('condition.2018-05-17.xml', 'start_node') execution = ptr.proxy.execution.get() channel = MagicMock() mongo[config["EXECUTION_COLLECTION"]].insert_one({ '_type': 'execution', 'id': execution.id, 'state': Xml.load(config, execution.process_name).get_state(), }) handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('mistery', [ { 'name': 'password', 'type': 'text', 'value': '123456', 'value_caption': '123456', }, ]) ], }, channel) # assertions assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'condition1' # rabbit called channel.basic_publish.assert_called_once() args = channel.basic_publish.call_args[1] rabbit_call = { 'command': 'step', 'pointer_id': ptr.id, 'input': [ Form.state_json('condition1', [ { 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': False, 'value_caption': 'False', }, ]) ], 'user_identifier': '__system__', } assert json.loads(args['body']) == rabbit_call handler.call(rabbit_call, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'condition2'
def test_variable_proc_name_pointers(config, mongo): ''' Test pointer name's update''' handler = Handler(config) user = make_user('juan', 'Juan') channel = MagicMock() 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', }, ]) ] execution = xml.start(node, input, mongo, channel, user.identifier) ptr = execution.proxy.pointers.get()[0] handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] 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.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] 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.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', }, ]) ], }, channel) # now check pointers last state cursor = mongo[config["POINTER_COLLECTION"]].find({ 'execution.id': execution.id, }) assert cursor.count() == 3 expected_name = 'Variable name process in step 3210' expected_desc = 'Description is also variable: 1, 2, 3' for item in cursor: assert item['execution']['name'] == expected_name assert item['execution']['description'] == expected_desc