1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more |
||
2 | # contributor license agreements. See the NOTICE file distributed with |
||
3 | # this work for additional information regarding copyright ownership. |
||
4 | # The ASF licenses this file to You under the Apache License, Version 2.0 |
||
5 | # (the "License"); you may not use this file except in compliance with |
||
6 | # the License. You may obtain a copy of the License at |
||
7 | # |
||
8 | # http://www.apache.org/licenses/LICENSE-2.0 |
||
9 | # |
||
10 | # Unless required by applicable law or agreed to in writing, software |
||
11 | # distributed under the License is distributed on an "AS IS" BASIS, |
||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
13 | # See the License for the specific language governing permissions and |
||
14 | # limitations under the License. |
||
15 | |||
16 | from __future__ import absolute_import |
||
17 | |||
18 | import mock |
||
19 | import six |
||
20 | |||
21 | from orquesta import states as wf_states |
||
22 | |||
23 | import st2tests |
||
24 | |||
25 | # XXX: actionsensor import depends on config being setup. |
||
26 | import st2tests.config as tests_config |
||
27 | tests_config.parse_args() |
||
28 | |||
29 | from tests.unit import base |
||
30 | |||
31 | from st2common.bootstrap import actionsregistrar |
||
32 | from st2common.bootstrap import runnersregistrar |
||
33 | from st2common.constants import action as ac_const |
||
34 | from st2common.models.db import liveaction as lv_db_models |
||
35 | from st2common.persistence import execution as ex_db_access |
||
36 | from st2common.persistence import liveaction as lv_db_access |
||
37 | from st2common.persistence import workflow as wf_db_access |
||
38 | from st2common.runners import base as runners |
||
39 | from st2common.services import action as ac_svc |
||
40 | from st2common.services import workflows as wf_svc |
||
41 | from st2common.transport import liveaction as lv_ac_xport |
||
42 | from st2common.transport import workflow as wf_ex_xport |
||
43 | from st2common.transport import publishers |
||
44 | from st2tests.mocks import liveaction as mock_lv_ac_xport |
||
45 | from st2tests.mocks import workflow as mock_wf_ex_xport |
||
46 | |||
47 | |||
48 | TEST_PACK = 'orquesta_tests' |
||
49 | TEST_PACK_PATH = st2tests.fixturesloader.get_fixtures_packs_base_path() + '/' + TEST_PACK |
||
50 | |||
51 | PACKS = [ |
||
52 | TEST_PACK_PATH, |
||
53 | st2tests.fixturesloader.get_fixtures_packs_base_path() + '/core' |
||
54 | ] |
||
55 | |||
56 | |||
57 | @mock.patch.object( |
||
58 | publishers.CUDPublisher, |
||
59 | 'publish_update', |
||
60 | mock.MagicMock(return_value=None)) |
||
61 | @mock.patch.object( |
||
62 | lv_ac_xport.LiveActionPublisher, |
||
63 | 'publish_create', |
||
64 | mock.MagicMock(side_effect=mock_lv_ac_xport.MockLiveActionPublisher.publish_create)) |
||
65 | @mock.patch.object( |
||
66 | lv_ac_xport.LiveActionPublisher, |
||
67 | 'publish_state', |
||
68 | mock.MagicMock(side_effect=mock_lv_ac_xport.MockLiveActionPublisher.publish_state)) |
||
69 | @mock.patch.object( |
||
70 | wf_ex_xport.WorkflowExecutionPublisher, |
||
71 | 'publish_create', |
||
72 | mock.MagicMock(side_effect=mock_wf_ex_xport.MockWorkflowExecutionPublisher.publish_create)) |
||
73 | @mock.patch.object( |
||
74 | wf_ex_xport.WorkflowExecutionPublisher, |
||
75 | 'publish_state', |
||
76 | mock.MagicMock(side_effect=mock_wf_ex_xport.MockWorkflowExecutionPublisher.publish_state)) |
||
77 | class OrquestaErrorHandlingTest(st2tests.DbTestCase): |
||
78 | |||
79 | @classmethod |
||
80 | def setUpClass(cls): |
||
81 | super(OrquestaErrorHandlingTest, cls).setUpClass() |
||
82 | |||
83 | # Register runners. |
||
84 | runnersregistrar.register_runners() |
||
85 | |||
86 | # Register test pack(s). |
||
87 | actions_registrar = actionsregistrar.ActionsRegistrar( |
||
88 | use_pack_cache=False, |
||
89 | fail_on_failure=True |
||
90 | ) |
||
91 | |||
92 | for pack in PACKS: |
||
93 | actions_registrar.register_from_pack(pack) |
||
94 | |||
95 | @classmethod |
||
96 | def get_runner_class(cls, runner_name): |
||
97 | return runners.get_runner(runner_name, runner_name).__class__ |
||
98 | |||
99 | def sort_wf_runtime_errors(self, errors): |
||
100 | return sorted(errors, key=lambda x: x.get('task_id', None)) |
||
101 | |||
102 | def test_fail_inspection(self): |
||
103 | expected_errors = [ |
||
104 | { |
||
105 | 'type': 'content', |
||
106 | 'message': 'The action "std.noop" is not registered in the database.', |
||
107 | 'schema_path': 'properties.tasks.patternProperties.^\w+$.properties.action', |
||
0 ignored issues
–
show
|
|||
108 | 'spec_path': 'tasks.task3.action' |
||
109 | }, |
||
110 | { |
||
111 | 'type': 'context', |
||
112 | 'language': 'yaql', |
||
113 | 'expression': '<% ctx().foobar %>', |
||
114 | 'message': 'Variable "foobar" is referenced before assignment.', |
||
115 | 'schema_path': 'properties.tasks.patternProperties.^\w+$.properties.input', |
||
116 | 'spec_path': 'tasks.task1.input', |
||
117 | }, |
||
118 | { |
||
119 | 'type': 'expression', |
||
120 | 'language': 'yaql', |
||
121 | 'expression': '<% <% succeeded() %>', |
||
122 | 'message': ( |
||
123 | 'Parse error: unexpected \'<\' at ' |
||
124 | 'position 0 of expression \'<% succeeded()\'' |
||
125 | ), |
||
126 | 'schema_path': ( |
||
127 | 'properties.tasks.patternProperties.^\w+$.' |
||
128 | 'properties.next.items.properties.when' |
||
129 | ), |
||
130 | 'spec_path': 'tasks.task2.next[0].when' |
||
131 | }, |
||
132 | { |
||
133 | 'type': 'syntax', |
||
134 | 'message': '[{\'cmd\': \'echo <% ctx().macro %>\'}] is not of type \'object\'', |
||
135 | 'schema_path': 'properties.tasks.patternProperties.^\w+$.properties.input.type', |
||
136 | 'spec_path': 'tasks.task2.input' |
||
137 | } |
||
138 | ] |
||
139 | |||
140 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-inspection.yaml') |
||
141 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
142 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
143 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
144 | |||
145 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
146 | self.assertIn('errors', lv_ac_db.result) |
||
147 | self.assertListEqual(lv_ac_db.result['errors'], expected_errors) |
||
148 | |||
149 | def test_fail_input_rendering(self): |
||
150 | expected_errors = [ |
||
151 | { |
||
152 | 'message': 'Unknown function "#property#value"' |
||
153 | } |
||
154 | ] |
||
155 | |||
156 | expected_result = {'output': None, 'errors': expected_errors} |
||
157 | |||
158 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-input-rendering.yaml') |
||
159 | |||
160 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
161 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
162 | |||
163 | # Assert action execution for task is not started and workflow failed. |
||
164 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
165 | tk_ex_dbs = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id)) |
||
166 | self.assertEqual(len(tk_ex_dbs), 0) |
||
167 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
168 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
169 | |||
170 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
171 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
172 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
173 | |||
174 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
175 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
176 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
177 | |||
178 | def test_fail_vars_rendering(self): |
||
179 | expected_errors = [ |
||
180 | { |
||
181 | 'message': 'Unknown function "#property#value"' |
||
182 | } |
||
183 | ] |
||
184 | |||
185 | expected_result = {'output': None, 'errors': expected_errors} |
||
186 | |||
187 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-vars-rendering.yaml') |
||
188 | |||
189 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
190 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
191 | |||
192 | # Assert action execution for task is not started and workflow failed. |
||
193 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
194 | tk_ex_dbs = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id)) |
||
195 | self.assertEqual(len(tk_ex_dbs), 0) |
||
196 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
197 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
198 | |||
199 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
200 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
201 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
202 | |||
203 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
204 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
205 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
206 | |||
207 | def test_fail_start_task_action(self): |
||
208 | expected_errors = [ |
||
209 | { |
||
210 | 'message': 'Unknown function "#property#value"', |
||
211 | 'task_id': 'task1' |
||
212 | } |
||
213 | ] |
||
214 | |||
215 | expected_result = {'output': None, 'errors': expected_errors} |
||
216 | |||
217 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-start-task-action.yaml') |
||
218 | |||
219 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
220 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
221 | |||
222 | # Assert action execution for task is not started and workflow failed. |
||
223 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
224 | tk_ex_dbs = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id)) |
||
225 | self.assertEqual(len(tk_ex_dbs), 0) |
||
226 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
227 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
228 | |||
229 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
230 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
231 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
232 | |||
233 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
234 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
235 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
236 | |||
237 | def test_fail_start_task_input_expr_eval(self): |
||
238 | expected_errors = [ |
||
239 | { |
||
240 | 'message': 'Unknown function "#property#value"', |
||
241 | 'task_id': 'task1' |
||
242 | } |
||
243 | ] |
||
244 | |||
245 | expected_result = {'output': None, 'errors': expected_errors} |
||
246 | |||
247 | wf_file = 'fail-start-task-input-expr-eval.yaml' |
||
248 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, wf_file) |
||
249 | |||
250 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
251 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
252 | |||
253 | # Assert action execution for task is not started and workflow failed. |
||
254 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
255 | tk_ex_dbs = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id)) |
||
256 | self.assertEqual(len(tk_ex_dbs), 0) |
||
257 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
258 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
259 | |||
260 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
261 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
262 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
263 | |||
264 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
265 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
266 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
267 | |||
268 | def test_fail_start_task_input_value_type(self): |
||
269 | if six.PY3: |
||
270 | msg = 'Value "{\'x\': \'foobar\'}" must either be a string or None. Got "dict".' |
||
271 | else: |
||
272 | msg = 'Value "{u\'x\': u\'foobar\'}" must either be a string or None. Got "dict".' |
||
273 | |||
274 | expected_errors = [ |
||
275 | { |
||
276 | 'message': msg, |
||
277 | 'task_id': 'task1' |
||
278 | } |
||
279 | ] |
||
280 | |||
281 | expected_result = {'output': None, 'errors': expected_errors} |
||
282 | |||
283 | wf_file = 'fail-start-task-input-value-type.yaml' |
||
284 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, wf_file) |
||
285 | wf_input = {'var1': {'x': 'foobar'}} |
||
286 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name'], parameters=wf_input) |
||
287 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
288 | |||
289 | # Assert workflow and task executions failed. |
||
290 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
291 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
292 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
293 | |||
294 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
295 | self.assertEqual(tk_ex_db.status, wf_states.FAILED) |
||
296 | self.assertDictEqual(tk_ex_db.result, {'errors': expected_errors}) |
||
297 | |||
298 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
299 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
300 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
301 | |||
302 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
303 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
304 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
305 | |||
306 | def test_fail_next_task_action(self): |
||
307 | expected_errors = [ |
||
308 | { |
||
309 | 'message': 'Unknown function "#property#value"', |
||
310 | 'task_id': 'task2' |
||
311 | } |
||
312 | ] |
||
313 | |||
314 | expected_result = {'output': None, 'errors': expected_errors} |
||
315 | |||
316 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-task-action.yaml') |
||
317 | |||
318 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
319 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
320 | |||
321 | # Assert task1 is already completed. |
||
322 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
323 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
324 | tk_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk_ex_db.id))[0] |
||
325 | tk_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk_ac_ex_db.liveaction['id']) |
||
326 | self.assertEqual(tk_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
327 | |||
328 | # Manually handle action execution completion for task1 which has an error in publish. |
||
329 | wf_svc.handle_action_execution_completion(tk_ac_ex_db) |
||
330 | |||
331 | # Assert task1 succeeded but workflow failed. |
||
332 | tk_ex_db = wf_db_access.TaskExecution.get_by_id(tk_ex_db.id) |
||
333 | self.assertEqual(tk_ex_db.status, wf_states.SUCCEEDED) |
||
334 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id) |
||
335 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
336 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
337 | |||
338 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
339 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
340 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
341 | |||
342 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
343 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
344 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
345 | |||
346 | def test_fail_next_task_input_expr_eval(self): |
||
347 | expected_errors = [ |
||
348 | { |
||
349 | 'message': 'Unknown function "#property#value"', |
||
350 | 'task_id': 'task2' |
||
351 | } |
||
352 | ] |
||
353 | |||
354 | expected_result = {'output': None, 'errors': expected_errors} |
||
355 | |||
356 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-task-input-expr-eval.yaml') |
||
357 | |||
358 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
359 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
360 | |||
361 | # Assert task1 is already completed. |
||
362 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
363 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
364 | tk_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk_ex_db.id))[0] |
||
365 | tk_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk_ac_ex_db.liveaction['id']) |
||
366 | self.assertEqual(tk_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
367 | |||
368 | # Manually handle action execution completion for task1 which has an error in publish. |
||
369 | wf_svc.handle_action_execution_completion(tk_ac_ex_db) |
||
370 | |||
371 | # Assert task1 succeeded but workflow failed. |
||
372 | tk_ex_db = wf_db_access.TaskExecution.get_by_id(tk_ex_db.id) |
||
373 | self.assertEqual(tk_ex_db.status, wf_states.SUCCEEDED) |
||
374 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id) |
||
375 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
376 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
377 | |||
378 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
379 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
380 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
381 | |||
382 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
383 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
384 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
385 | |||
386 | def test_fail_next_task_input_value_type(self): |
||
387 | if six.PY3: |
||
388 | msg = 'Value "{\'x\': \'foobar\'}" must either be a string or None. Got "dict".' |
||
389 | else: |
||
390 | msg = 'Value "{u\'x\': u\'foobar\'}" must either be a string or None. Got "dict".' |
||
391 | |||
392 | expected_errors = [ |
||
393 | { |
||
394 | 'message': msg, |
||
395 | 'task_id': 'task2' |
||
396 | } |
||
397 | ] |
||
398 | |||
399 | expected_result = {'output': None, 'errors': expected_errors} |
||
400 | |||
401 | wf_file = 'fail-task-input-value-type.yaml' |
||
402 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, wf_file) |
||
403 | wf_input = {'var1': {'x': 'foobar'}} |
||
404 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name'], parameters=wf_input) |
||
405 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
406 | |||
407 | # Assert task1 is already completed and workflow execution is still running. |
||
408 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
409 | tk1_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
410 | tk1_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk1_ex_db.id))[0] |
||
411 | tk1_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk1_ac_ex_db.liveaction['id']) |
||
412 | self.assertEqual(tk1_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
413 | self.assertEqual(wf_ex_db.status, wf_states.RUNNING) |
||
414 | |||
415 | # Manually handle action execution completion for task1 which has an error in publish. |
||
416 | wf_svc.handle_action_execution_completion(tk1_ac_ex_db) |
||
417 | |||
418 | # Assert workflow execution and task2 execution failed. |
||
419 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(str(wf_ex_db.id)) |
||
420 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
421 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
422 | |||
423 | tk2_ex_db = wf_db_access.TaskExecution.query(task_id='task2')[0] |
||
424 | self.assertEqual(tk2_ex_db.status, wf_states.FAILED) |
||
425 | self.assertDictEqual(tk2_ex_db.result, {'errors': expected_errors}) |
||
426 | |||
427 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
428 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
429 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
430 | |||
431 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
432 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
433 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
434 | |||
435 | def test_fail_task_transition(self): |
||
436 | expected_errors = [ |
||
437 | { |
||
438 | 'message': ( |
||
439 | "Unable to resolve key 'foobar' in expression " |
||
440 | "'<% succeeded() and result().foobar %>' from context." |
||
441 | ), |
||
442 | 'task_transition_id': 'task2__0', |
||
443 | 'task_id': 'task1' |
||
444 | } |
||
445 | ] |
||
446 | |||
447 | expected_result = {'output': None, 'errors': expected_errors} |
||
448 | |||
449 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-task-transition.yaml') |
||
450 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
451 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
452 | |||
453 | # Assert task1 is already completed. |
||
454 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
455 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
456 | tk_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk_ex_db.id))[0] |
||
457 | tk_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk_ac_ex_db.liveaction['id']) |
||
458 | self.assertEqual(tk_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
459 | |||
460 | # Manually handle action execution completion for task1 which has an error in publish. |
||
461 | wf_svc.handle_action_execution_completion(tk_ac_ex_db) |
||
462 | |||
463 | # Assert task1 succeeded but workflow failed. |
||
464 | tk_ex_db = wf_db_access.TaskExecution.get_by_id(tk_ex_db.id) |
||
465 | self.assertEqual(tk_ex_db.status, wf_states.SUCCEEDED) |
||
466 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id) |
||
467 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
468 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
469 | |||
470 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
471 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
472 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
473 | |||
474 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
475 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
476 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
477 | |||
478 | def test_fail_task_publish(self): |
||
479 | expected_errors = [ |
||
480 | { |
||
481 | 'message': 'Unknown function "foobar"', |
||
482 | 'task_transition_id': 'task2__0', |
||
483 | 'task_id': 'task1' |
||
484 | } |
||
485 | ] |
||
486 | |||
487 | expected_result = {'output': None, 'errors': expected_errors} |
||
488 | |||
489 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-task-publish.yaml') |
||
490 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
491 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
492 | |||
493 | # Assert task1 is already completed. |
||
494 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
495 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
496 | tk_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk_ex_db.id))[0] |
||
497 | tk_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk_ac_ex_db.liveaction['id']) |
||
498 | self.assertEqual(tk_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
499 | |||
500 | # Manually handle action execution completion for task1 which has an error in publish. |
||
501 | wf_svc.handle_action_execution_completion(tk_ac_ex_db) |
||
502 | |||
503 | # Assert task1 succeeded but workflow failed. |
||
504 | tk_ex_db = wf_db_access.TaskExecution.get_by_id(tk_ex_db.id) |
||
505 | self.assertEqual(tk_ex_db.status, wf_states.SUCCEEDED) |
||
506 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id) |
||
507 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
508 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
509 | |||
510 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
511 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
512 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
513 | |||
514 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
515 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
516 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
517 | |||
518 | def test_fail_output_rendering(self): |
||
519 | expected_errors = [ |
||
520 | { |
||
521 | 'message': 'Unknown function "#property#value"' |
||
522 | } |
||
523 | ] |
||
524 | |||
525 | expected_result = {'output': None, 'errors': expected_errors} |
||
526 | |||
527 | wf_meta = base.get_wf_fixture_meta_data(TEST_PACK_PATH, 'fail-output-rendering.yaml') |
||
528 | lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) |
||
529 | lv_ac_db, ac_ex_db = ac_svc.request(lv_ac_db) |
||
530 | |||
531 | # Assert task1 is already completed. |
||
532 | wf_ex_db = wf_db_access.WorkflowExecution.query(action_execution=str(ac_ex_db.id))[0] |
||
533 | tk_ex_db = wf_db_access.TaskExecution.query(workflow_execution=str(wf_ex_db.id))[0] |
||
534 | tk_ac_ex_db = ex_db_access.ActionExecution.query(task_execution=str(tk_ex_db.id))[0] |
||
535 | tk_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk_ac_ex_db.liveaction['id']) |
||
536 | self.assertEqual(tk_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED) |
||
537 | |||
538 | # Manually handle action execution completion for task1 which has an error in publish. |
||
539 | wf_svc.handle_action_execution_completion(tk_ac_ex_db) |
||
540 | |||
541 | # Assert task1 succeeded but workflow failed. |
||
542 | tk_ex_db = wf_db_access.TaskExecution.get_by_id(tk_ex_db.id) |
||
543 | self.assertEqual(tk_ex_db.status, wf_states.SUCCEEDED) |
||
544 | wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id) |
||
545 | self.assertEqual(wf_ex_db.status, wf_states.FAILED) |
||
546 | self.assertListEqual(self.sort_wf_runtime_errors(wf_ex_db.errors), expected_errors) |
||
547 | |||
548 | lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) |
||
549 | self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
550 | self.assertDictEqual(lv_ac_db.result, expected_result) |
||
551 | |||
552 | ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id)) |
||
553 | self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_FAILED) |
||
554 | self.assertDictEqual(ac_ex_db.result, expected_result) |
||
555 |
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
r
orR
are they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.