def test_split(self): test_spec = { "workflow": self.get_wf_file_path("split"), "expected_routes": [ [], # default from start ["task2__t0"], # task1 -> task2 -> task4 ["task3__t0"], # task1 -> task3 -> task4 ], "expected_task_sequence": [ "task1__r0", "task2__r0", "task3__r0", "task4__r1", "task4__r2", "task5__r1", "task5__r2", "task6__r1", "task6__r2", "task7__r1", "task7__r2", ], "expected_term_tasks": ["task7__r1", "task7__r2"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_splits_mixed(self): test_spec = { "workflow": self.get_wf_file_path("splits-mixed"), "expected_routes": [ [], ["task1__t0"], # task1 -> task3 ["task2__t0"], # task2 -> task3 ["task1__t0", "task3__t0"], # task1 -> task3 -> task4 ["task2__t0", "task3__t0"], # task2 -> task3 -> task4 ], "expected_task_sequence": [ "task1__r0", "task2__r0", "task3__r1", "task3__r2", "task4__r3", "task4__r4", "task5__r3", "task5__r4", ], "expected_term_tasks": ["task5__r3", "task5__r4"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_splits_mixed_alt_branch(self): test_spec = { "workflow": self.get_wf_file_path("splits-mixed"), "expected_routes": [ [], ["task1__t0"], # task1 -> task3 ["task2__t0"], # task2 -> task3 ["task1__t0", "task3__t0"], # task1 -> task3 -> task4 ["task2__t0", "task3__t1"], # task2 -> task3 -> task4 ], "expected_task_sequence": [ "task1__r0", "task2__r0", "task3__r1", "task3__r2", "task4__r3", "task4__r4", "task5__r3", "task5__r4", ], "mock_action_executions": [ { "task_id": "task3" }, # task3, 1 { "task_id": "task3", "status": statuses.FAILED }, # task3, 2 ], "expected_term_tasks": ["task5__r3", "task5__r4"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_fail_multiple_before_join(self): # Initial run of workflow. test_spec = { "workflow": self.get_wf_file_path("join"), # Fail task3 before join at task6. "expected_task_sequence": ["task1", "task2", "task4", "task3", "task5"], "mock_action_executions": [ { "task_id": "task3", "status": statuses.FAILED }, { "task_id": "task5", "status": statuses.FAILED }, ], "expected_term_tasks": ["task3", "task5"], "expected_workflow_status": statuses.FAILED, } rehearsal1 = rehearsing.load_test_spec(test_spec) rehearsal1.assert_conducting_sequence() # Rerun and complete workflow. test_spec = { "workflow_state": rehearsal1.conductor.serialize(), "expected_task_sequence": [ "task1", "task2", "task4", "task3", "task5", "task3", "task5", "task6", "task7", ], "expected_term_tasks": ["task7"], } rehearsal2 = rehearsing.load_test_spec(test_spec) rehearsal2.assert_conducting_sequence()
def test_error_noop_success_path(self): test_spec = { "workflow": self.get_wf_file_path("error-handling-noop"), "expected_task_sequence": ["task1", "task2", "continue"], "expected_output": {"message": "hooray!!!"}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_no_error(self): test_spec = { "workflow": self.get_wf_file_path("error-handling"), "expected_task_sequence": ["task1", "task2"], "expected_term_tasks": ["task2"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_on_complete_from_succeeded_branch(self): test_spec = { "workflow": self.get_wf_file_path("task-on-complete"), "expected_task_sequence": ["task1", "task2", "task4"], "expected_term_tasks": ["task2", "task4"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_join_count_not_satisfied(self): test_spec = { "workflow": self.get_wf_file_path("join-count"), # Mock errors at branch 2 and 3. The criteria for join task is not met. "expected_task_sequence": [ "task1", "task2", "task4", "task6", "task3", "task5", "task7", "noop__r1", "noop__r2", ], "expected_routes": [[], ["task5__t0"], ["task7__t0"]], "mock_action_executions": [ { "task_id": "task5", "status": statuses.FAILED }, { "task_id": "task7", "status": statuses.FAILED }, ], "expected_workflow_status": statuses.FAILED, "expected_errors": [ { "message": "Execution failed. See result for details.", "type": "error", "task_id": "task5", }, { "message": "Execution failed. See result for details.", "type": "error", "task_id": "task7", }, { "message": ('UnreachableJoinError: The join task|route "task8|0" ' "is partially satisfied but unreachable."), "type": "error", "route": 0, "task_id": "task8", }, ], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_branching(self): test_spec = { "workflow": self.get_wf_file_path("branching"), "expected_task_sequence": ["task1", "task2", "task4", "task3", "task5"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_join_task_transition_on_failed_both_branches_succeeded(self): test_spec = { "workflow": self.get_wf_file_path("join-on-fail"), "expected_task_sequence": ["task1", "task2", "task4", "task3", "task5"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_join_count_not_satisfied_no_inbound(self): test_spec = { "workflow": self.get_wf_file_path("join-count"), # Mock all branches failed. The criteria for join task is not met. "expected_task_sequence": [ "task1", "task2", "task4", "task6", "task3", "task5", "task7", "noop__r1", "noop__r2", ], "expected_routes": [[], ["task5__t0"], ["task7__t0"]], "mock_action_executions": [ { "task_id": "task3", "status": statuses.FAILED }, { "task_id": "task5", "status": statuses.FAILED }, { "task_id": "task7", "status": statuses.FAILED }, ], "expected_workflow_status": statuses.FAILED, "expected_errors": [ { "message": "Execution failed. See result for details.", "type": "error", "task_id": "task3", }, { "message": "Execution failed. See result for details.", "type": "error", "task_id": "task5", }, { "message": "Execution failed. See result for details.", "type": "error", "task_id": "task7", }, ], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_error_noop_failure_path(self): test_spec = { "workflow": self.get_wf_file_path("error-handling-noop"), "expected_task_sequence": ["task1", "noop"], "mock_action_executions": [ {"task_id": "task1", "status": statuses.FAILED}, ], "expected_output": {"message": "$%#&@#$!!!"}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_load_test_spec_dict_with_expected_inspection_errors(self): test_spec = { "workflow": self.get_wf_file_path("sequential"), "expected_task_sequence": ["task1", "task2", "task3", "continue"], "expected_inspection_errors": { "syntax": [{ "message": "['foobar'] is not of type 'string'", "schema_path": ("properties.tasks.patternProperties.^\\w+$." "properties.next.items.properties.when.type"), "spec_path": "tasks.task1.next[0].when", }], "context": [ { "type": "yaql", "expression": '<% ctx("__xyz") %>', "spec_path": "output[0]", "schema_path": "properties.output", "message": ('Variable "__xyz" that is prefixed with double underscores ' "is considered a private variable and cannot be referenced." ), }, ], }, } rehearsal = rehearsing.load_test_spec(test_spec) self.assertEqual( len(rehearsal.session.expected_inspection_errors.syntax), 1) syntax_error = rehearsal.session.expected_inspection_errors.syntax[0] self.assertIsNone(syntax_error.type) self.assertIsNone(syntax_error.expression) self.assertEqual(syntax_error.message, "['foobar'] is not of type 'string'") self.assertEqual(syntax_error.spec_path, "tasks.task1.next[0].when") self.assertIn("properties.tasks.patternProperties", syntax_error.schema_path) self.assertEqual( len(rehearsal.session.expected_inspection_errors.context), 1) context_error = rehearsal.session.expected_inspection_errors.context[0] self.assertEqual(context_error.type, "yaql") self.assertEqual(context_error.expression, '<% ctx("__xyz") %>') self.assertEqual(context_error.schema_path, "properties.output") self.assertIn('Variable "__xyz"', context_error.message)
def test_error_continue_success_path(self): test_spec = { "workflow": self.get_wf_file_path("error-handling-continue"), "expected_task_sequence": ["task1", "task2", "continue__r1"], "expected_routes": [ [], # default from start ["task2__t0"], # task1 -> task2 -> continue ], "expected_output": {"message": "hooray!!!"}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_on_error(self): test_spec = { "workflow": self.get_wf_file_path("error-handling"), "expected_task_sequence": ["task1", "task3"], "mock_action_executions": [{ "task_id": "task1", "status": statuses.FAILED }], "expected_term_tasks": ["task3"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_items_list_with_error(self): test_spec = { "workflow": self.get_wf_file_path("with-items-transition"), "expected_task_sequence": ["task1"], "mock_action_executions": [ {"task_id": "task1", "result": "fee", "item_id": 0}, {"task_id": "task1", "result": "fi", "item_id": 1}, {"task_id": "task1", "result": "fo", "item_id": 2, "status": statuses.FAILED}, ], "expected_workflow_status": statuses.FAILED, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_very_many_splits(self): test_spec = { "workflow": self.get_wf_file_path("splits-very-many"), "expected_routes": [ [], ["init__t1"], ["init__t0"], ["init__t0", "task2__t0"], ["init__t0", "task2__t0", "task7__t0"], ["init__t0", "task2__t0", "task7__t0", "task10__t0"], [ "init__t0", "task2__t0", "task7__t0", "task10__t0", "task13__t0" ], [ "init__t0", "task2__t0", "task7__t0", "task10__t0", "task13__t0", "task15__t0" ], [ "init__t0", "task2__t0", "task7__t0", "task10__t0", "task13__t0", "task15__t0", "task17__t0", ], ], "expected_task_sequence": [ "init__r0", "notify__r1", "task2__r2", "task5__r3", "task7__r3", "task9__r4", "task10__r4", "task11__r5", "task12__r5", "task13__r5", "task14__r6", "task15__r6", "task17__r7", "notify__r8", ], "expected_term_tasks": ["notify__r1", "notify__r8"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_on_complete_from_failed_branch(self): test_spec = { "workflow": self.get_wf_file_path("task-on-complete"), "expected_task_sequence": ["task1", "task3", "task4"], "mock_action_executions": [{ "task_id": "task1", "status": statuses.FAILED }], "expected_term_tasks": ["task3", "task4"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_items_list_with_error_and_remediation(self): test_spec = { "workflow": self.get_wf_file_path("with-items-remediate"), "expected_task_sequence": ["task1", "task2"], "mock_action_executions": [ {"task_id": "task1", "result": "fee", "item_id": 0}, {"task_id": "task1", "result": "fi", "item_id": 1}, {"task_id": "task1", "result": None, "item_id": 2, "status": statuses.FAILED}, ], "expected_output": {"items": ["fee", "fi", None]}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_error_concurrent_log_fail(self): mock_result = "All your base are belong to us!" test_spec = { "workflow": self.get_wf_file_path("error-log-fail-concurrent"), "expected_task_sequence": ["task1", "fail", "log"], "mock_action_executions": [ {"task_id": "task1", "status": statuses.FAILED, "result": mock_result}, ], "expected_workflow_status": statuses.FAILED, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_decision_tree(self): # Test branch "a" test_spec = { "workflow": self.get_wf_file_path("decision"), "inputs": { "which": "a" }, "expected_task_sequence": ["t1", "a"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence() # Test branch "b" test_spec = { "workflow": self.get_wf_file_path("decision"), "inputs": { "which": "b" }, "expected_task_sequence": ["t1", "b"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence() # Test branch "c" test_spec = { "workflow": self.get_wf_file_path("decision"), "inputs": { "which": "c" }, "expected_task_sequence": ["t1", "c"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_basic_items_list(self): test_spec = { "workflow": self.get_wf_file_path("with-items-transition"), "expected_task_sequence": ["task1", "task2"], "mock_action_executions": [ {"task_id": "task1", "result": "fee", "item_id": 0}, {"task_id": "task1", "result": "fi", "item_id": 1}, {"task_id": "task1", "result": "fo", "item_id": 2}, {"task_id": "task1", "result": "fum", "item_id": 3}, ], "expected_output": {"items": ["fee", "fi", "fo", "fum"]}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_task_transitions_split_from_succeeded_branch(self): test_spec = { "workflow": self.get_wf_file_path("task-transitions-split"), "expected_routes": [ [], # default from start ["task1__t0"], # task1 -> task2 (when #1) ["task1__t2"], # task1 -> task2 (when #3) ], "expected_task_sequence": ["task1__r0", "task2__r1", "task2__r2"], "expected_term_tasks": ["task2__r1", "task2__r2"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def process(base_path, test_spec): fixture_path = "%s/%s" % (base_path, test_spec) if not os.path.isfile(fixture_path): raise exc.WorkflowRehearsalError('The test spec "%s" does not exist.' % fixture_path) rehearsal = rehearsing.load_test_spec(fixture_path=test_spec, base_path=base_path) LOG.info('The test spec "%s" is successfully loaded.' % fixture_path) rehearsal.assert_conducting_sequence() LOG.info( "Completed running test and the workflow execution matches the test spec." )
def test_load_test_spec_dict_with_expected_errors(self): test_spec = { "workflow": self.get_wf_file_path("sequential"), "expected_task_sequence": ["task1", "task2"], "mock_action_executions": [{ "task_id": "task2", "status": statuses.FAILED }], "expected_errors": [ { "type": "error", "message": "Unknown exception at task2." }, { "type": "error", "message": "Stuff happens.", "task_id": "task2" }, ], } rehearsal = rehearsing.load_test_spec(test_spec) self.assertEqual(len(rehearsal.session.mock_action_executions), 1) self.assertEqual(rehearsal.session.mock_action_executions[0].task_id, "task2") self.assertEqual(len(rehearsal.session.expected_errors), 2) self.assertEqual(rehearsal.session.expected_errors[0].type, "error") self.assertEqual(rehearsal.session.expected_errors[0].message, "Unknown exception at task2.") self.assertIsNone(rehearsal.session.expected_errors[0].task_id) self.assertIsNone(rehearsal.session.expected_errors[0].route) self.assertIsNone( rehearsal.session.expected_errors[0].task_transition_id) self.assertIsNone(rehearsal.session.expected_errors[0].result) self.assertIsNone(rehearsal.session.expected_errors[0].data) self.assertEqual(rehearsal.session.expected_errors[1].type, "error") self.assertEqual(rehearsal.session.expected_errors[1].message, "Stuff happens.") self.assertEqual(rehearsal.session.expected_errors[1].task_id, "task2") self.assertIsNone(rehearsal.session.expected_errors[1].route) self.assertIsNone( rehearsal.session.expected_errors[1].task_transition_id) self.assertIsNone(rehearsal.session.expected_errors[1].result) self.assertIsNone(rehearsal.session.expected_errors[1].data)
def test_join_context(self): test_spec = { "workflow": self.get_wf_file_path("join-context"), "expected_task_sequence": [ "task1", "task2", "task4", "task6", "task8", "task3", "task5", "task7", "task9", "task10", ], "mock_action_executions": [ { "task_id": "task2", "result": "Fee fi" }, { "task_id": "task4", "result": "I smell the blood of an English man" }, { "task_id": "task3", "result": "fo fum" }, { "task_id": "task7", "result": "Be alive, or be he dead" }, ], "expected_output": { "messages": [ "Fee fi fo fum", "I smell the blood of an English man", "Be alive, or be he dead", "I'll grind his bones to make my bread", ] }, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_error_continue_failure_path(self): test_spec = { "workflow": self.get_wf_file_path("error-handling-continue"), "expected_task_sequence": ["task1", "continue__r1"], "expected_routes": [ [], # default from start ["task1__t0"], # task1 -> continue ], "mock_action_executions": [ {"task_id": "task1", "status": statuses.FAILED}, ], "expected_workflow_status": statuses.FAILED, "expected_output": {"message": "$%#&@#$!!!"}, } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_splits_multiple_transition(self): test_spec = { "workflow": self.get_wf_file_path("splits-transition"), "expected_routes": [ [], ["task1__t0"], # task1 -> task3 ["task2__t0"], # task2 -> task3 ["task1__t0", "task3__t0"], # task1 -> task3 -> task4 (when #1) ["task1__t0", "task3__t1"], # task1 -> task3 -> task4 (when #2) ["task2__t0", "task3__t0"], # task2 -> task3 -> task4 (when #1) ["task2__t0", "task3__t1"], # task2 -> task3 -> task4 (when #2) ], "expected_task_sequence": [ "task1__r0", "task2__r0", "task3__r1", "task3__r2", "task4__r3", "task4__r4", "task4__r5", "task4__r6", "task5__r3", "task5__r4", "task5__r5", "task5__r6", "task6__r3", "task6__r4", "task6__r5", "task6__r6", "task7__r3", "task7__r4", "task7__r5", "task7__r6", ], "expected_term_tasks": ["task7__r3", "task7__r4", "task7__r5", "task7__r6"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_join_task_transition_on_completed_both_branches_succeeded(self): test_spec = { "workflow": self.get_wf_file_path("join-on-complete"), # The tasks before the join, task3 and task5, both succeeded. "expected_task_sequence": [ "task1", "task2", "task4", "task3", "task5", "task6", "task7", ], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()
def test_task_transitions_split_from_failed_branch(self): test_spec = { "workflow": self.get_wf_file_path("task-transitions-split"), "expected_routes": [ [], # default from start ["task1__t0"], # task1 -> task2 (when #1) ["task1__t1"], # task1 -> task2 (when #2) ], "expected_task_sequence": ["task1__r0", "task2__r1", "task2__r2"], "mock_action_executions": [{ "task_id": "task1", "status": statuses.FAILED }], "expected_term_tasks": ["task2__r1", "task2__r2"], } rehearsal = rehearsing.load_test_spec(test_spec) rehearsal.assert_conducting_sequence()