support call activities in process model test runner w/ burnettk
This commit is contained in:
parent
f2439848c0
commit
acaf3a3c24
|
@ -39,16 +39,26 @@ class MissingBpmnFileForTestCaseError(Exception):
|
||||||
@dataclass
|
@dataclass
|
||||||
class TestCaseResult:
|
class TestCaseResult:
|
||||||
passed: bool
|
passed: bool
|
||||||
|
bpmn_file: str
|
||||||
test_case_name: str
|
test_case_name: str
|
||||||
error: Optional[str] = None
|
error: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_NSMAP = {
|
||||||
|
'bpmn': 'http://www.omg.org/spec/BPMN/20100524/MODEL',
|
||||||
|
'bpmndi': 'http://www.omg.org/spec/BPMN/20100524/DI',
|
||||||
|
'dc': 'http://www.omg.org/spec/DD/20100524/DC',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# input:
|
# input:
|
||||||
# json_file:
|
# json_file:
|
||||||
# {
|
# {
|
||||||
# [TEST_CASE_NAME]: {
|
# [TEST_CASE_NAME]: {
|
||||||
# "tasks": {
|
# "tasks": {
|
||||||
# [BPMN_TASK_IDENTIIFER]: [DATA]
|
# [BPMN_TASK_IDENTIIFER]: {
|
||||||
|
# "data": [DATA]
|
||||||
|
# }
|
||||||
# },
|
# },
|
||||||
# "expected_output_json": [DATA]
|
# "expected_output_json": [DATA]
|
||||||
# }
|
# }
|
||||||
|
@ -62,17 +72,23 @@ class ProcessModelTestRunner:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
process_model_directory_path: str,
|
process_model_directory_path: str,
|
||||||
instantiate_executer_callback: Callable[[str], Any],
|
process_model_directory_for_test_discovery: Optional[str] = None,
|
||||||
execute_task_callback: Callable[[Any, Optional[dict]], Any],
|
instantiate_executer_callback: Optional[Callable[[str], Any]] = None,
|
||||||
get_next_task_callback: Callable[[Any], Any],
|
execute_task_callback: Optional[Callable[[Any, Optional[dict]], Any]] = None,
|
||||||
|
get_next_task_callback: Optional[Callable[[Any], Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.process_model_directory_path = process_model_directory_path
|
self.process_model_directory_path = process_model_directory_path
|
||||||
self.test_mappings = self._discover_process_model_directories()
|
self.process_model_directory_for_test_discovery = process_model_directory_for_test_discovery or process_model_directory_path
|
||||||
self.instantiate_executer_callback = instantiate_executer_callback
|
self.instantiate_executer_callback = instantiate_executer_callback
|
||||||
self.execute_task_callback = execute_task_callback
|
self.execute_task_callback = execute_task_callback
|
||||||
self.get_next_task_callback = get_next_task_callback
|
self.get_next_task_callback = get_next_task_callback
|
||||||
|
|
||||||
self.test_case_results: list[TestCaseResult] = []
|
self.test_case_results: list[TestCaseResult] = []
|
||||||
|
self.bpmn_processes_to_file_mappings: dict[str, str] = {}
|
||||||
|
self.bpmn_files_to_called_element_mappings: dict[str, list[str]] = {}
|
||||||
|
|
||||||
|
self.test_mappings = self._discover_process_model_test_cases()
|
||||||
|
self._discover_process_model_processes()
|
||||||
|
|
||||||
def all_test_cases_passed(self) -> bool:
|
def all_test_cases_passed(self) -> bool:
|
||||||
failed_tests = [t for t in self.test_case_results if t.passed is False]
|
failed_tests = [t for t in self.test_case_results if t.passed is False]
|
||||||
|
@ -87,59 +103,161 @@ class ProcessModelTestRunner:
|
||||||
try:
|
try:
|
||||||
self.run_test_case(bpmn_file, test_case_name, test_case_contents)
|
self.run_test_case(bpmn_file, test_case_name, test_case_contents)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.test_case_results.append(
|
self._add_test_result(False, bpmn_file, test_case_name, f"Syntax error: {str(ex)}")
|
||||||
TestCaseResult(
|
|
||||||
passed=False,
|
|
||||||
test_case_name=test_case_name,
|
|
||||||
error=f"Syntax error: {str(ex)}",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def run_test_case(self, bpmn_file: str, test_case_name: str, test_case_contents: dict) -> None:
|
def run_test_case(self, bpmn_file: str, test_case_name: str, test_case_contents: dict) -> None:
|
||||||
bpmn_process_instance = self.instantiate_executer_callback(bpmn_file)
|
bpmn_process_instance = self._instantiate_executer(bpmn_file)
|
||||||
next_task = self.get_next_task_callback(bpmn_process_instance)
|
next_task = self._get_next_task(bpmn_process_instance)
|
||||||
while next_task is not None:
|
while next_task is not None:
|
||||||
test_case_json = None
|
test_case_task_properties = None
|
||||||
if "tasks" in test_case_contents:
|
if "tasks" in test_case_contents:
|
||||||
if next_task.task_spec.bpmn_id in test_case_contents["tasks"]:
|
if next_task.task_spec.bpmn_id in test_case_contents["tasks"]:
|
||||||
test_case_json = test_case_contents["tasks"][next_task.task_spec.bpmn_id]
|
test_case_task_properties = test_case_contents["tasks"][next_task.task_spec.bpmn_id]
|
||||||
|
|
||||||
task_type = next_task.task_spec.__class__.__name__
|
task_type = next_task.task_spec.__class__.__name__
|
||||||
if task_type in ["ServiceTask", "UserTask"] and test_case_json is None:
|
if task_type in ["ServiceTask", "UserTask", "CallActivity"] and test_case_task_properties is None:
|
||||||
raise UnrunnableTestCaseError(
|
raise UnrunnableTestCaseError(
|
||||||
f"Cannot run test case '{test_case_name}'. It requires task data for"
|
f"Cannot run test case '{test_case_name}'. It requires task data for"
|
||||||
f" {next_task.task_spec.bpmn_id} because it is of type '{task_type}'"
|
f" {next_task.task_spec.bpmn_id} because it is of type '{task_type}'"
|
||||||
)
|
)
|
||||||
self.execute_task_callback(next_task, test_case_json)
|
self._execute_task(next_task, test_case_task_properties)
|
||||||
next_task = self.get_next_task_callback(bpmn_process_instance)
|
next_task = self._get_next_task(bpmn_process_instance)
|
||||||
test_passed = test_case_contents["expected_output_json"] == bpmn_process_instance.data
|
test_passed = test_case_contents["expected_output_json"] == bpmn_process_instance.data
|
||||||
self.test_case_results.append(
|
error_message = None
|
||||||
TestCaseResult(
|
if test_passed is False:
|
||||||
passed=test_passed,
|
error_message = (
|
||||||
test_case_name=test_case_name,
|
f"Expected output did not match actual output:"
|
||||||
)
|
f"\nexpected: {test_case_contents['expected_output_json']}"
|
||||||
|
f"\nactual: {bpmn_process_instance.data}"
|
||||||
)
|
)
|
||||||
|
self._add_test_result(test_passed, bpmn_file, test_case_name, error_message)
|
||||||
|
|
||||||
def _discover_process_model_directories(
|
def _discover_process_model_test_cases(
|
||||||
self,
|
self,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
test_mappings = {}
|
test_mappings = {}
|
||||||
|
|
||||||
json_test_file_glob = os.path.join(self.process_model_directory_path, "**", "test_*.json")
|
json_test_file_glob = os.path.join(self.process_model_directory_for_test_discovery, "**", "test_*.json")
|
||||||
|
|
||||||
for file in glob.glob(json_test_file_glob, recursive=True):
|
for file in glob.glob(json_test_file_glob, recursive=True):
|
||||||
file_dir = os.path.dirname(file)
|
file_norm = os.path.normpath(file)
|
||||||
json_file_name = os.path.basename(file)
|
file_dir = os.path.dirname(file_norm)
|
||||||
|
json_file_name = os.path.basename(file_norm)
|
||||||
bpmn_file_name = re.sub(r"^test_(.*)\.json", r"\1.bpmn", json_file_name)
|
bpmn_file_name = re.sub(r"^test_(.*)\.json", r"\1.bpmn", json_file_name)
|
||||||
bpmn_file_path = os.path.join(file_dir, bpmn_file_name)
|
bpmn_file_path = os.path.join(file_dir, bpmn_file_name)
|
||||||
if os.path.isfile(bpmn_file_path):
|
if os.path.isfile(bpmn_file_path):
|
||||||
test_mappings[file] = bpmn_file_path
|
test_mappings[file_norm] = bpmn_file_path
|
||||||
else:
|
else:
|
||||||
raise MissingBpmnFileForTestCaseError(
|
raise MissingBpmnFileForTestCaseError(
|
||||||
f"Cannot find a matching bpmn file for test case json file: '{file}'"
|
f"Cannot find a matching bpmn file for test case json file: '{file_norm}'"
|
||||||
)
|
)
|
||||||
return test_mappings
|
return test_mappings
|
||||||
|
|
||||||
|
def _discover_process_model_processes(
|
||||||
|
self,
|
||||||
|
) -> None:
|
||||||
|
process_model_bpmn_file_glob = os.path.join(self.process_model_directory_path, "**", "*.bpmn")
|
||||||
|
|
||||||
|
for file in glob.glob(process_model_bpmn_file_glob, recursive=True):
|
||||||
|
file_norm = os.path.normpath(file)
|
||||||
|
if file_norm not in self.bpmn_files_to_called_element_mappings:
|
||||||
|
self.bpmn_files_to_called_element_mappings[file_norm] = []
|
||||||
|
with open(file_norm, 'rb') as f:
|
||||||
|
file_contents = f.read()
|
||||||
|
etree_xml_parser = etree.XMLParser(resolve_entities=False)
|
||||||
|
root = etree.fromstring(file_contents, parser=etree_xml_parser)
|
||||||
|
call_activities = root.findall('.//bpmn:callActivity', namespaces=DEFAULT_NSMAP)
|
||||||
|
for call_activity in call_activities:
|
||||||
|
called_element = call_activity.attrib['calledElement']
|
||||||
|
self.bpmn_files_to_called_element_mappings[file_norm].append(called_element)
|
||||||
|
bpmn_process_element = root.find('.//bpmn:process[@isExecutable="true"]', namespaces=DEFAULT_NSMAP)
|
||||||
|
bpmn_process_identifier = bpmn_process_element.attrib['id']
|
||||||
|
self.bpmn_processes_to_file_mappings[bpmn_process_identifier] = file_norm
|
||||||
|
|
||||||
|
def _execute_task(self, spiff_task: SpiffTask, test_case_task_properties: Optional[dict]) -> None:
|
||||||
|
if self.execute_task_callback:
|
||||||
|
self.execute_task_callback(spiff_task, test_case_task_properties)
|
||||||
|
self._default_execute_task(spiff_task, test_case_task_properties)
|
||||||
|
|
||||||
|
def _get_next_task(self, bpmn_process_instance: BpmnWorkflow) -> Optional[SpiffTask]:
|
||||||
|
if self.get_next_task_callback:
|
||||||
|
return self.get_next_task_callback(bpmn_process_instance)
|
||||||
|
return self._default_get_next_task(bpmn_process_instance)
|
||||||
|
|
||||||
|
def _instantiate_executer(self, bpmn_file: str) -> BpmnWorkflow:
|
||||||
|
if self.instantiate_executer_callback:
|
||||||
|
return self.instantiate_executer_callback(bpmn_file)
|
||||||
|
return self._default_instantiate_executer(bpmn_file)
|
||||||
|
|
||||||
|
def _get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
||||||
|
tasks = list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual])
|
||||||
|
if len(tasks) > 0:
|
||||||
|
tasks = [tasks[0]]
|
||||||
|
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
def _default_get_next_task(self, bpmn_process_instance: BpmnWorkflow) -> Optional[SpiffTask]:
|
||||||
|
engine_steps = self._get_ready_engine_steps(bpmn_process_instance)
|
||||||
|
if len(engine_steps) > 0:
|
||||||
|
return engine_steps[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _default_execute_task(self, spiff_task: SpiffTask, test_case_task_properties: Optional[dict]) -> None:
|
||||||
|
if spiff_task.task_spec.manual:
|
||||||
|
if test_case_task_properties and 'data' in test_case_task_properties:
|
||||||
|
spiff_task.update_data(test_case_task_properties['data'])
|
||||||
|
spiff_task.complete()
|
||||||
|
else:
|
||||||
|
spiff_task.run()
|
||||||
|
|
||||||
|
def _find_related_bpmn_files(self, bpmn_file: str) -> list[str]:
|
||||||
|
related_bpmn_files = []
|
||||||
|
if bpmn_file in self.bpmn_files_to_called_element_mappings:
|
||||||
|
for bpmn_process_identifier in self.bpmn_files_to_called_element_mappings[bpmn_file]:
|
||||||
|
if bpmn_process_identifier in self.bpmn_processes_to_file_mappings:
|
||||||
|
new_file = self.bpmn_processes_to_file_mappings[bpmn_process_identifier]
|
||||||
|
related_bpmn_files.append(new_file)
|
||||||
|
related_bpmn_files.extend(self._find_related_bpmn_files(new_file))
|
||||||
|
return related_bpmn_files
|
||||||
|
|
||||||
|
def _get_etree_from_bpmn_file(self, bpmn_file: str) -> etree.Element:
|
||||||
|
data = None
|
||||||
|
with open(bpmn_file, "rb") as f_handle:
|
||||||
|
data = f_handle.read()
|
||||||
|
etree_xml_parser = etree.XMLParser(resolve_entities=False)
|
||||||
|
return etree.fromstring(data, parser=etree_xml_parser)
|
||||||
|
|
||||||
|
def _default_instantiate_executer(self, bpmn_file: str) -> BpmnWorkflow:
|
||||||
|
parser = MyCustomParser()
|
||||||
|
bpmn_file_etree = self._get_etree_from_bpmn_file(bpmn_file)
|
||||||
|
parser.add_bpmn_xml(bpmn_file_etree, filename=os.path.basename(bpmn_file))
|
||||||
|
all_related = self._find_related_bpmn_files(bpmn_file)
|
||||||
|
for related_file in all_related:
|
||||||
|
related_file_etree = self._get_etree_from_bpmn_file(related_file)
|
||||||
|
parser.add_bpmn_xml(related_file_etree, filename=os.path.basename(related_file))
|
||||||
|
sub_parsers = list(parser.process_parsers.values())
|
||||||
|
executable_process = None
|
||||||
|
for sub_parser in sub_parsers:
|
||||||
|
if sub_parser.process_executable:
|
||||||
|
executable_process = sub_parser.bpmn_id
|
||||||
|
if executable_process is None:
|
||||||
|
raise BpmnFileMissingExecutableProcessError(
|
||||||
|
f"Executable process cannot be found in {bpmn_file}. Test cannot run."
|
||||||
|
)
|
||||||
|
bpmn_process_spec = parser.get_spec(executable_process)
|
||||||
|
bpmn_process_instance = BpmnWorkflow(bpmn_process_spec)
|
||||||
|
return bpmn_process_instance
|
||||||
|
|
||||||
|
def _add_test_result(self, passed: bool, bpmn_file: str, test_case_name: str, error: Optional[str] = None) -> None:
|
||||||
|
bpmn_file_relative = os.path.relpath(bpmn_file, start=self.process_model_directory_path)
|
||||||
|
test_result = TestCaseResult(
|
||||||
|
passed=passed,
|
||||||
|
bpmn_file=bpmn_file_relative,
|
||||||
|
test_case_name=test_case_name,
|
||||||
|
error=error,
|
||||||
|
)
|
||||||
|
self.test_case_results.append(test_result)
|
||||||
|
|
||||||
|
|
||||||
class BpmnFileMissingExecutableProcessError(Exception):
|
class BpmnFileMissingExecutableProcessError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -149,9 +267,9 @@ class ProcessModelTestRunnerService:
|
||||||
def __init__(self, process_model_directory_path: str) -> None:
|
def __init__(self, process_model_directory_path: str) -> None:
|
||||||
self.process_model_test_runner = ProcessModelTestRunner(
|
self.process_model_test_runner = ProcessModelTestRunner(
|
||||||
process_model_directory_path,
|
process_model_directory_path,
|
||||||
instantiate_executer_callback=self._instantiate_executer_callback,
|
# instantiate_executer_callback=self._instantiate_executer_callback,
|
||||||
execute_task_callback=self._execute_task_callback,
|
# execute_task_callback=self._execute_task_callback,
|
||||||
get_next_task_callback=self._get_next_task_callback,
|
# get_next_task_callback=self._get_next_task_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
@ -168,7 +286,6 @@ class ProcessModelTestRunnerService:
|
||||||
|
|
||||||
def _get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
def _get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
||||||
tasks = list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual])
|
tasks = list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual])
|
||||||
|
|
||||||
if len(tasks) > 0:
|
if len(tasks) > 0:
|
||||||
tasks = [tasks[0]]
|
tasks = [tasks[0]]
|
||||||
|
|
||||||
|
@ -179,7 +296,8 @@ class ProcessModelTestRunnerService:
|
||||||
data = None
|
data = None
|
||||||
with open(bpmn_file, "rb") as f_handle:
|
with open(bpmn_file, "rb") as f_handle:
|
||||||
data = f_handle.read()
|
data = f_handle.read()
|
||||||
bpmn: etree.Element = SpecFileService.get_etree_from_xml_bytes(data)
|
etree_xml_parser = etree.XMLParser(resolve_entities=False)
|
||||||
|
bpmn = etree.fromstring(data, parser=etree_xml_parser)
|
||||||
parser.add_bpmn_xml(bpmn, filename=os.path.basename(bpmn_file))
|
parser.add_bpmn_xml(bpmn, filename=os.path.basename(bpmn_file))
|
||||||
sub_parsers = list(parser.process_parsers.values())
|
sub_parsers = list(parser.process_parsers.values())
|
||||||
executable_process = None
|
executable_process = None
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?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:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||||
|
<bpmn:process id="BasicCallActivityProcess" isExecutable="true">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>Flow_0ext5lt</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0ext5lt" sourceRef="StartEvent_1" targetRef="Activity_0irfg4l" />
|
||||||
|
<bpmn:endEvent id="Event_0bz40ol">
|
||||||
|
<bpmn:incoming>Flow_1hzwssi</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_1hzwssi" sourceRef="Activity_0irfg4l" targetRef="Event_0bz40ol" />
|
||||||
|
<bpmn:callActivity id="Activity_0irfg4l" name="Call Activity" calledElement="BasicManualTaskProcess">
|
||||||
|
<!-- <bpmn:callActivity id="Activity_0irfg4l" name="Call Activity" calledElement="BasicManualTaskProcess2"> -->
|
||||||
|
<bpmn:incoming>Flow_0ext5lt</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_1hzwssi</bpmn:outgoing>
|
||||||
|
</bpmn:callActivity>
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_test_a42_A_4_2_bd2e724">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_0bz40ol_di" bpmnElement="Event_0bz40ol">
|
||||||
|
<dc:Bounds x="422" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_0148g78_di" bpmnElement="Activity_0irfg4l">
|
||||||
|
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||||
|
<bpmndi:BPMNLabel />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0ext5lt_di" bpmnElement="Flow_0ext5lt">
|
||||||
|
<di:waypoint x="215" y="177" />
|
||||||
|
<di:waypoint x="270" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_1hzwssi_di" bpmnElement="Flow_1hzwssi">
|
||||||
|
<di:waypoint x="370" y="177" />
|
||||||
|
<di:waypoint x="422" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"description": "",
|
||||||
|
"display_name": "Basic Call Activity",
|
||||||
|
"exception_notification_addresses": [],
|
||||||
|
"fault_or_suspend_on_exception": "fault",
|
||||||
|
"files": [],
|
||||||
|
"primary_file_name": "basic_call_activity.bpmn",
|
||||||
|
"primary_process_id": "BasicCallActivityProcess"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"test_case_one": {
|
||||||
|
"expected_output_json": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?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:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||||
|
<bpmn:process id="BasicManualTaskProcess" isExecutable="true">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>Flow_0gz6i84</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0gz6i84" sourceRef="StartEvent_1" targetRef="manual_task_one" />
|
||||||
|
<bpmn:endEvent id="Event_0ynlmo7">
|
||||||
|
<bpmn:incoming>Flow_0ikklg6</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0ikklg6" sourceRef="manual_task_one" targetRef="Event_0ynlmo7" />
|
||||||
|
<bpmn:manualTask id="manual_task_one" name="Manual">
|
||||||
|
<bpmn:incoming>Flow_0gz6i84</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_0ikklg6</bpmn:outgoing>
|
||||||
|
</bpmn:manualTask>
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="BasicManualTaskProcess">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_0ynlmo7_di" bpmnElement="Event_0ynlmo7">
|
||||||
|
<dc:Bounds x="432" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_0sji2rz_di" bpmnElement="manualt_task_one">
|
||||||
|
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||||
|
<bpmndi:BPMNLabel />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0gz6i84_di" bpmnElement="Flow_0gz6i84">
|
||||||
|
<di:waypoint x="215" y="177" />
|
||||||
|
<di:waypoint x="270" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0ikklg6_di" bpmnElement="Flow_0ikklg6">
|
||||||
|
<di:waypoint x="370" y="177" />
|
||||||
|
<di:waypoint x="432" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"description": "Baisc Manual Task",
|
||||||
|
"display_name": "Baisc Manual Task",
|
||||||
|
"exception_notification_addresses": [],
|
||||||
|
"fault_or_suspend_on_exception": "fault",
|
||||||
|
"files": [],
|
||||||
|
"primary_file_name": "baisc_manual_task.bpmn",
|
||||||
|
"primary_process_id": "BasicManualTaskProcess"
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"test_case_one": {
|
||||||
|
"tasks": {
|
||||||
|
"manual_task_one": {
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expected_output_json": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||||
|
|
||||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||||
from spiffworkflow_backend.services.file_system_service import FileSystemService
|
from spiffworkflow_backend.services.file_system_service import FileSystemService
|
||||||
from spiffworkflow_backend.services.process_model_test_runner_service import ProcessModelTestRunnerService
|
from spiffworkflow_backend.services.process_model_test_runner_service import ProcessModelTestRunner, ProcessModelTestRunnerService
|
||||||
|
|
||||||
|
|
||||||
class TestProcessModelTestRunnerService(BaseTest):
|
class TestProcessModelTestRunnerService(BaseTest):
|
||||||
|
@ -23,7 +23,7 @@ class TestProcessModelTestRunnerService(BaseTest):
|
||||||
os.path.join(FileSystemService.root_path(), "basic_script_task")
|
os.path.join(FileSystemService.root_path(), "basic_script_task")
|
||||||
)
|
)
|
||||||
test_runner_service.run()
|
test_runner_service.run()
|
||||||
assert test_runner_service.process_model_test_runner.all_test_cases_passed()
|
assert test_runner_service.process_model_test_runner.all_test_cases_passed(), test_runner_service.process_model_test_runner.test_case_results
|
||||||
|
|
||||||
def test_can_test_multiple_process_models(
|
def test_can_test_multiple_process_models(
|
||||||
self,
|
self,
|
||||||
|
@ -35,6 +35,19 @@ class TestProcessModelTestRunnerService(BaseTest):
|
||||||
test_runner_service.run()
|
test_runner_service.run()
|
||||||
assert test_runner_service.process_model_test_runner.all_test_cases_passed() is False
|
assert test_runner_service.process_model_test_runner.all_test_cases_passed() is False
|
||||||
|
|
||||||
|
def test_can_test_process_model_call_activity(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_mocked_root_path: Any,
|
||||||
|
) -> None:
|
||||||
|
test_runner_service = ProcessModelTestRunner(
|
||||||
|
process_model_directory_path=FileSystemService.root_path(),
|
||||||
|
process_model_directory_for_test_discovery=os.path.join(FileSystemService.root_path(), "basic_call_activity")
|
||||||
|
)
|
||||||
|
test_runner_service.run()
|
||||||
|
assert test_runner_service.all_test_cases_passed() is True, test_runner_service.test_case_results
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def with_mocked_root_path(self, mocker: MockerFixture) -> None:
|
def with_mocked_root_path(self, mocker: MockerFixture) -> None:
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
|
|
Loading…
Reference in New Issue