mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-02-05 06:04:32 +00:00
234 lines
9.6 KiB
Python
234 lines
9.6 KiB
Python
|
from SpiffWorkflow.task import TaskState
|
||
|
from SpiffWorkflow.bpmn.exceptions import WorkflowDataException
|
||
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
|
||
|
from SpiffWorkflow.bpmn.specs.data_spec import TaskDataReference
|
||
|
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException
|
||
|
|
||
|
from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase
|
||
|
|
||
|
|
||
|
class BaseTestCase(BpmnWorkflowTestCase):
|
||
|
|
||
|
def set_io_and_run_workflow(self, data, data_input=None, data_output=None, save_restore=False):
|
||
|
|
||
|
start = self.workflow.get_tasks_from_spec_name('Start')[0]
|
||
|
start.data = data
|
||
|
|
||
|
any_task = self.workflow.get_tasks_from_spec_name('any_task')[0]
|
||
|
any_task.task_spec.data_input = TaskDataReference(data_input) if data_input is not None else None
|
||
|
any_task.task_spec.data_output = TaskDataReference(data_output) if data_output is not None else None
|
||
|
|
||
|
self.workflow.do_engine_steps()
|
||
|
ready_tasks = self.workflow.get_ready_user_tasks()
|
||
|
self.assertEqual(len(ready_tasks), 3)
|
||
|
while len(ready_tasks) > 0:
|
||
|
task = ready_tasks[0]
|
||
|
self.assertEqual(task.task_spec.name, 'any_task [child]')
|
||
|
self.assertIn('input_item', task.data)
|
||
|
task.data['output_item'] = task.data['input_item'] * 2
|
||
|
task.complete()
|
||
|
if save_restore:
|
||
|
self.save_restore()
|
||
|
ready_tasks = self.workflow.get_ready_user_tasks()
|
||
|
self.workflow.refresh_waiting_tasks()
|
||
|
self.workflow.do_engine_steps()
|
||
|
self.assertTrue(self.workflow.is_completed())
|
||
|
|
||
|
def run_workflow_with_condition(self, data):
|
||
|
|
||
|
start = self.workflow.get_tasks_from_spec_name('Start')[0]
|
||
|
start.data = data
|
||
|
|
||
|
task = self.workflow.get_tasks_from_spec_name('any_task')[0]
|
||
|
task.task_spec.condition = "input_item == 2"
|
||
|
|
||
|
self.workflow.do_engine_steps()
|
||
|
ready_tasks = self.workflow.get_ready_user_tasks()
|
||
|
self.assertEqual(len(ready_tasks), 3)
|
||
|
task = [t for t in ready_tasks if t.data['input_item'] == 2][0]
|
||
|
task.data['output_item'] = task.data['input_item'] * 2
|
||
|
task.complete()
|
||
|
self.workflow.do_engine_steps()
|
||
|
self.workflow.refresh_waiting_tasks()
|
||
|
|
||
|
self.assertTrue(self.workflow.is_completed())
|
||
|
self.assertEqual(len([ t for t in ready_tasks if t.state == TaskState.CANCELLED]), 2)
|
||
|
|
||
|
|
||
|
class ParallellMultiInstanceExistingOutputTest(BaseTestCase):
|
||
|
|
||
|
def setUp(self):
|
||
|
self.spec, subprocess = self.load_workflow_spec('parallel_multiinstance_loop_input.bpmn', 'main')
|
||
|
self.workflow = BpmnWorkflow(self.spec)
|
||
|
|
||
|
def testListWithDictOutput(self):
|
||
|
data = {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': {},
|
||
|
}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': {0: 2, 1: 4, 2: 6},
|
||
|
})
|
||
|
|
||
|
def testDictWithListOutput(self):
|
||
|
data = {
|
||
|
'input_data': {'a': 1, 'b': 2, 'c': 3},
|
||
|
'output_data': [],
|
||
|
}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': {'a': 1, 'b': 2, 'c': 3},
|
||
|
'output_data': [2, 4, 6],
|
||
|
})
|
||
|
|
||
|
def testNonEmptyOutput(self):
|
||
|
with self.assertRaises(WorkflowDataException) as exc:
|
||
|
data = {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': [1, 2, 3],
|
||
|
}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertEqual(exc.exception.message,
|
||
|
"If the input is not being updated in place, the output must be empty or it must be a map (dict)")
|
||
|
|
||
|
def testInvalidOutputType(self):
|
||
|
with self.assertRaises(WorkflowDataException) as exc:
|
||
|
data = {
|
||
|
'input_data': set([1, 2, 3]),
|
||
|
'output_data': set(),
|
||
|
}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertEqual(exc.exception.message, "Only a mutable map (dict) or sequence (list) can be used for output")
|
||
|
|
||
|
|
||
|
class ParallelMultiInstanceNewOutputTest(BaseTestCase):
|
||
|
|
||
|
def setUp(self):
|
||
|
self.spec, subprocess = self.load_workflow_spec('parallel_multiinstance_loop_input.bpmn', 'main')
|
||
|
self.workflow = BpmnWorkflow(self.spec)
|
||
|
|
||
|
def testList(self):
|
||
|
data = {'input_data': [1, 2, 3]}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': [2, 4, 6]
|
||
|
})
|
||
|
|
||
|
def testListSaveRestore(self):
|
||
|
data = {'input_data': [1, 2, 3]}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data', save_restore=True)
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': [2, 4, 6]
|
||
|
})
|
||
|
|
||
|
def testDict(self):
|
||
|
data = {'input_data': {'a': 1, 'b': 2, 'c': 3} }
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': {'a': 1, 'b': 2, 'c': 3},
|
||
|
'output_data': {'a': 2, 'b': 4, 'c': 6}
|
||
|
})
|
||
|
|
||
|
def testDictSaveRestore(self):
|
||
|
data = {'input_data': {'a': 1, 'b': 2, 'c': 3} }
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data', save_restore=True)
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': {'a': 1, 'b': 2, 'c': 3},
|
||
|
'output_data': {'a': 2, 'b': 4, 'c': 6}
|
||
|
})
|
||
|
|
||
|
def testSet(self):
|
||
|
data = {'input_data': set([1, 2, 3])}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': set([1, 2, 3]),
|
||
|
'output_data': [2, 4, 6]
|
||
|
})
|
||
|
|
||
|
def testEmptyCollection(self):
|
||
|
|
||
|
start = self.workflow.get_tasks_from_spec_name('Start')[0]
|
||
|
start.data = {'input_data': []}
|
||
|
self.workflow.do_engine_steps()
|
||
|
self.assertTrue(self.workflow.is_completed())
|
||
|
self.assertDictEqual(self.workflow.data, {'input_data': [], 'output_data': []})
|
||
|
|
||
|
def testCondition(self):
|
||
|
self.run_workflow_with_condition({'input_data': [1, 2, 3]})
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'input_data': [1, 2, 3],
|
||
|
'output_data': [4]
|
||
|
})
|
||
|
|
||
|
|
||
|
class ParallelMultiInstanceUpdateInputTest(BaseTestCase):
|
||
|
|
||
|
def setUp(self):
|
||
|
self.spec, subprocess = self.load_workflow_spec('parallel_multiinstance_loop_input.bpmn', 'main')
|
||
|
self.workflow = BpmnWorkflow(self.spec)
|
||
|
|
||
|
def testList(self):
|
||
|
data = { 'input_data': [1, 2, 3]}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='input_data')
|
||
|
self.assertDictEqual(self.workflow.data, {'input_data': [2, 4, 6]})
|
||
|
|
||
|
def testDict(self):
|
||
|
data = { 'input_data': {'a': 1, 'b': 2, 'c': 3}}
|
||
|
self.set_io_and_run_workflow(data, data_input='input_data', data_output='input_data')
|
||
|
self.assertDictEqual(self.workflow.data, {'input_data': {'a': 2, 'b': 4, 'c': 6}})
|
||
|
|
||
|
|
||
|
class ParallelMultiInstanceWithCardinality(BaseTestCase):
|
||
|
|
||
|
def setUp(self) -> None:
|
||
|
self.spec, subprocess = self.load_workflow_spec('parallel_multiinstance_cardinality.bpmn', 'main')
|
||
|
self.workflow = BpmnWorkflow(self.spec)
|
||
|
|
||
|
def testCardinality(self):
|
||
|
self.set_io_and_run_workflow({}, data_output='output_data')
|
||
|
self.assertDictEqual(self.workflow.data, {'output_data': [0, 2, 4]})
|
||
|
|
||
|
def testCardinalitySaveRestore(self):
|
||
|
self.set_io_and_run_workflow({}, data_output='output_data', save_restore=True)
|
||
|
self.assertDictEqual(self.workflow.data, {'output_data': [0, 2, 4]})
|
||
|
|
||
|
def testCondition(self):
|
||
|
self.run_workflow_with_condition({})
|
||
|
self.assertDictEqual(self.workflow.data, {
|
||
|
'output_data': [4]
|
||
|
})
|
||
|
|
||
|
|
||
|
class ParallelMultiInstanceTaskTest(BpmnWorkflowTestCase):
|
||
|
|
||
|
def check_reference(self, reference, name):
|
||
|
self.assertIsInstance(reference, TaskDataReference)
|
||
|
self.assertEqual(reference.name, name)
|
||
|
|
||
|
def testParseInputOutput(self):
|
||
|
spec, subprocess = self.load_workflow_spec('parallel_multiinstance_loop_input.bpmn', 'main')
|
||
|
workflow = BpmnWorkflow(spec)
|
||
|
task_spec = workflow.get_tasks_from_spec_name('any_task')[0].task_spec
|
||
|
self.check_reference(task_spec.data_input, 'input_data')
|
||
|
self.check_reference(task_spec.data_output, 'output_data')
|
||
|
self.check_reference(task_spec.input_item, 'input_item')
|
||
|
self.check_reference(task_spec.output_item, 'output_item')
|
||
|
self.assertIsNone(task_spec.cardinality)
|
||
|
|
||
|
def testParseCardinality(self):
|
||
|
spec, subprocess = self.load_workflow_spec('parallel_multiinstance_cardinality.bpmn', 'main')
|
||
|
workflow = BpmnWorkflow(spec)
|
||
|
task_spec = workflow.get_tasks_from_spec_name('any_task')[0].task_spec
|
||
|
self.assertIsNone(task_spec.data_input)
|
||
|
self.assertEqual(task_spec.cardinality, '3')
|
||
|
|
||
|
def testInvalidBpmn(self):
|
||
|
with self.assertRaises(ValidationException) as exc:
|
||
|
spec, subprocess = self.load_workflow_spec('parallel_multiinstance_invalid.bpmn', 'main')
|
||
|
self.assertEqual(exc.exception.message,
|
||
|
'A multiinstance task must specify exactly one of cardinality or loop input data reference.')
|