STG-26 - basic test case for a looping task
Criteria : task.multi_instance_type == 'looping' to terminate, use the standard endpoint for submitting form data with a query variable of terminate_loop=true Will likely need two buttons: "Submit and quit" "Submit and add another" or something similar
This commit is contained in:
parent
7723862530
commit
1844c93919
|
@ -626,6 +626,12 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: terminate_loop
|
||||
in: query
|
||||
required: false
|
||||
description: Terminate the loop on a looping task
|
||||
schema:
|
||||
type: boolean
|
||||
put:
|
||||
operationId: crc.api.workflow.update_task
|
||||
summary: Exclusively for User Tasks, submits form data as a flat set of key/values.
|
||||
|
|
|
@ -175,7 +175,7 @@ def set_current_task(workflow_id, task_id):
|
|||
return WorkflowApiSchema().dump(workflow_api_model)
|
||||
|
||||
|
||||
def update_task(workflow_id, task_id, body):
|
||||
def update_task(workflow_id, task_id, body, terminate_loop=None):
|
||||
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
|
||||
if workflow_model is None:
|
||||
|
@ -191,6 +191,9 @@ def update_task(workflow_id, task_id, body):
|
|||
if task.state != task.READY:
|
||||
raise ApiError("invalid_state", "You may not update a task unless it is in the READY state. "
|
||||
"Consider calling a token reset to make this task Ready.")
|
||||
if terminate_loop:
|
||||
task.terminate_loop()
|
||||
|
||||
task.update_data(body)
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
|
|
|
@ -290,7 +290,7 @@ class BaseTest(unittest.TestCase):
|
|||
self.assertEqual(workflow.workflow_spec_id, workflow_api.workflow_spec_id)
|
||||
return workflow_api
|
||||
|
||||
def complete_form(self, workflow_in, task_in, dict_data, error_code=None, user_uid="dhf8r"):
|
||||
def complete_form(self, workflow_in, task_in, dict_data, error_code=None, terminate_loop=None, user_uid="dhf8r"):
|
||||
prev_completed_task_count = workflow_in.completed_tasks
|
||||
if isinstance(task_in, dict):
|
||||
task_id = task_in["id"]
|
||||
|
@ -299,11 +299,16 @@ class BaseTest(unittest.TestCase):
|
|||
|
||||
user = session.query(UserModel).filter_by(uid=user_uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/data' % (workflow_in.id, task_id),
|
||||
headers=self.logged_in_headers(user=user),
|
||||
content_type="application/json",
|
||||
data=json.dumps(dict_data))
|
||||
if terminate_loop:
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/data?terminate_loop=true' % (workflow_in.id, task_id),
|
||||
headers=self.logged_in_headers(user=user),
|
||||
content_type="application/json",
|
||||
data=json.dumps(dict_data))
|
||||
else:
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/data' % (workflow_in.id, task_id),
|
||||
headers=self.logged_in_headers(user=user),
|
||||
content_type="application/json",
|
||||
data=json.dumps(dict_data))
|
||||
if error_code:
|
||||
self.assert_failure(rv, error_code=error_code)
|
||||
return
|
||||
|
@ -316,7 +321,9 @@ class BaseTest(unittest.TestCase):
|
|||
# The total number of tasks may change over time, as users move through gateways
|
||||
# branches may be pruned. As we hit parallel Multi-Instance new tasks may be created...
|
||||
self.assertIsNotNone(workflow.total_tasks)
|
||||
self.assertEqual(prev_completed_task_count + 1, workflow.completed_tasks)
|
||||
# presumably, we also need to deal with sequential items here too . .
|
||||
if not task_in.multi_instance_type == 'looping':
|
||||
self.assertEqual(prev_completed_task_count + 1, workflow.completed_tasks)
|
||||
|
||||
# Assure a record exists in the Task Events
|
||||
task_events = session.query(TaskEventModel) \
|
||||
|
@ -335,7 +342,8 @@ class BaseTest(unittest.TestCase):
|
|||
self.assertEqual(task_in.name, event.task_name)
|
||||
self.assertEqual(task_in.title, event.task_title)
|
||||
self.assertEqual(task_in.type, event.task_type)
|
||||
self.assertEqual("COMPLETED", event.task_state)
|
||||
if not task_in.multi_instance_type == 'looping':
|
||||
self.assertEqual("COMPLETED", event.task_state)
|
||||
|
||||
# Not sure what voodoo is happening inside of marshmallow to get me in this state.
|
||||
if isinstance(task_in.multi_instance_type, MultiInstanceType):
|
||||
|
@ -344,7 +352,10 @@ class BaseTest(unittest.TestCase):
|
|||
self.assertEqual(task_in.multi_instance_type, event.mi_type)
|
||||
|
||||
self.assertEqual(task_in.multi_instance_count, event.mi_count)
|
||||
self.assertEqual(task_in.multi_instance_index, event.mi_index)
|
||||
if task_in.multi_instance_type == 'looping' and not terminate_loop:
|
||||
self.assertEqual(task_in.multi_instance_index+1, event.mi_index)
|
||||
else:
|
||||
self.assertEqual(task_in.multi_instance_index, event.mi_index)
|
||||
self.assertEqual(task_in.process_name, event.process_name)
|
||||
self.assertIsNotNone(event.date)
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1v9xfjq" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
|
||||
<bpmn:process id="looping_task" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0vlor2k</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:userTask id="GetNames" name="Get Names" camunda:formKey="GetNamesForm">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="GetNames_MICurrentVar.Name" type="string" />
|
||||
<camunda:formField id="GetNames_MICurrentVar.Nickname" type="string" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0vlor2k</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1tvod7v</bpmn:outgoing>
|
||||
<bpmn:standardLoopCharacteristics />
|
||||
</bpmn:userTask>
|
||||
<bpmn:endEvent id="Event_End">
|
||||
<bpmn:incoming>Flow_1tvod7v</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1tvod7v" sourceRef="GetNames" targetRef="Event_End" />
|
||||
<bpmn:sequenceFlow id="Flow_0vlor2k" sourceRef="StartEvent_1" targetRef="GetNames" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="looping_task">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1mpvzb7_di" bpmnElement="GetNames">
|
||||
<dc:Bounds x="250" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0dhdik5_di" bpmnElement="Event_End">
|
||||
<dc:Bounds x="402" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1tvod7v_di" bpmnElement="Flow_1tvod7v">
|
||||
<di:waypoint x="350" y="117" />
|
||||
<di:waypoint x="402" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0vlor2k_di" bpmnElement="Flow_0vlor2k">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="250" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -0,0 +1,54 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
from crc import session
|
||||
from crc.models.api_models import MultiInstanceType
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.workflow import WorkflowStatus
|
||||
from crc.services.study_service import StudyService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestWorkflowProcessorLoopingTask(BaseTest):
|
||||
"""Tests the Workflow Processor as it deals with a Looping task"""
|
||||
|
||||
def _populate_form_with_random_data(self, task):
|
||||
api_task = WorkflowService.spiff_task_to_api_task(task, add_docs_and_forms=True)
|
||||
WorkflowService.populate_form_with_random_data(task, api_task, required_only=False)
|
||||
|
||||
def get_processor(self, study_model, spec_model):
|
||||
workflow_model = StudyService._create_workflow_model(study_model, spec_model)
|
||||
return WorkflowProcessor(workflow_model)
|
||||
|
||||
def test_create_and_complete_workflow(self):
|
||||
# This depends on getting a list of investigators back from the protocol builder.
|
||||
|
||||
workflow = self.create_workflow('looping_task')
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
self.assertEqual("GetNames", task.name)
|
||||
|
||||
self.assertEqual(task.multi_instance_type, 'looping')
|
||||
self.assertEqual(1, task.multi_instance_index)
|
||||
self.complete_form(workflow,task,{'GetNames_MICurrentVar':{'Name': 'Peter Norvig', 'Nickname': 'Pete'}})
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
self.assertEqual(task.multi_instance_type,'looping')
|
||||
self.assertEqual(2, task.multi_instance_index)
|
||||
self.complete_form(workflow,
|
||||
task,
|
||||
{'GetNames_MICurrentVar':{'Name': 'Stuart Russell', 'Nickname': 'Stu'}},
|
||||
terminate_loop=True)
|
||||
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
self.assertEqual(task.name,'Event_End')
|
||||
self.assertEqual(workflow.completed_tasks,workflow.total_tasks)
|
||||
self.assertEqual(task.data, {'GetNames_MICurrentVar': 2,
|
||||
'GetNames_MIData': {'1': {'Name': 'Peter Norvig',
|
||||
'Nickname': 'Pete'},
|
||||
'2': {'Name': 'Stuart Russell',
|
||||
'Nickname': 'Stu'}}})
|
||||
|
||||
|
||||
|
|
@ -32,7 +32,8 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
'error': 'Unable to locate a user with id asd3v in LDAP'}}
|
||||
|
||||
def _populate_form_with_random_data(self, task):
|
||||
WorkflowProcessor.populate_form_with_random_data(task)
|
||||
|
||||
WorkflowService.populate_form_with_random_data(task)
|
||||
|
||||
def get_processor(self, study_model, spec_model):
|
||||
workflow_model = StudyService._create_workflow_model(study_model, spec_model)
|
||||
|
|
Loading…
Reference in New Issue