Passed
Pull Request — master (#4000)
by W
05:52
created

MistralRunnerTest.setUp()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
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
import copy
18
import uuid
19
20
import mock
21
import yaml
22
23
from mistralclient.api.v2 import executions
24
from mistralclient.api.v2 import tasks
25
from mistralclient.api.v2 import workbooks
26
from mistralclient.api.v2 import workflows
27
from oslo_config import cfg
28
29
# XXX: actionsensor import depends on config being setup.
30
import st2tests.config as tests_config
31
tests_config.parse_args()
32
33
from mistral_v2.mistral_v2 import MistralRunner
34
from st2common.bootstrap import actionsregistrar
35
from st2common.bootstrap import runnersregistrar
36
from st2common.constants import action as action_constants
37
from st2common.models.db.liveaction import LiveActionDB
38
from st2common.persistence.liveaction import LiveAction
39
from st2common.runners import base as runners
40
from st2common.services import action as action_service
41
from st2common.transport.liveaction import LiveActionPublisher
42
from st2common.transport.publishers import CUDPublisher
43
from st2common.util import loader
44
from st2tests import DbTestCase
45
from st2tests import fixturesloader
46
from st2tests.mocks.liveaction import MockLiveActionPublisher
47
48
49
TEST_FIXTURES = {
50
    'workflows': [
51
        'workflow_v2.yaml',
52
        'workbook_v2_many_workflows.yaml'
53
    ],
54
    'actions': [
55
        'workflow_v2.yaml',
56
        'workbook_v2_many_workflows.yaml'
57
    ]
58
}
59
60
TEST_PACK = 'mistral_tests'
61
TEST_PACK_PATH = fixturesloader.get_fixtures_packs_base_path() + '/' + TEST_PACK
62
63
PACKS = [
64
    TEST_PACK_PATH,
65
    fixturesloader.get_fixtures_packs_base_path() + '/core'
66
]
67
68
# Action executions requirements
69
ACTION_PARAMS = {'friend': 'Rocky'}
70
NON_EMPTY_RESULT = 'non-empty'
71
72
# Workbook with multiple workflows
73
WB1_META_FILE_NAME = TEST_FIXTURES['workflows'][1]
74
WB1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WB1_META_FILE_NAME
75
WB1_META_CONTENT = loader.load_meta_file(WB1_META_FILE_PATH)
76
WB1_NAME = WB1_META_CONTENT['pack'] + '.' + WB1_META_CONTENT['name']
77
WB1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WB1_META_CONTENT['entry_point']
78
WB1_ENTRY_POINT_X = WB1_ENTRY_POINT.replace(WB1_META_FILE_NAME, 'xformed_' + WB1_META_FILE_NAME)
79
WB1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WB1_ENTRY_POINT_X))
80
WB1_YAML = yaml.safe_dump(WB1_SPEC, default_flow_style=False)
81
WB1 = workbooks.Workbook(None, {'name': WB1_NAME, 'definition': WB1_YAML})
82
WB1_MAIN_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING'}
83
WB1_MAIN_EXEC['workflow_name'] = WB1_NAME + '.main'
84
WB1_MAIN_EXEC_ERRORED = copy.deepcopy(WB1_MAIN_EXEC)
85
WB1_MAIN_EXEC_ERRORED['state'] = 'ERROR'
86
WB1_MAIN_TASK1 = {'id': str(uuid.uuid4()), 'name': 'greet', 'state': 'ERROR'}
87
WB1_MAIN_TASKS = [tasks.Task(None, WB1_MAIN_TASK1)]
88
WB1_MAIN_TASK_ID = WB1_MAIN_TASK1['id']
89
WB1_SUB1_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'task_execution_id': WB1_MAIN_TASK_ID}
90
WB1_SUB1_EXEC['workflow_name'] = WB1_NAME + '.subflow1'
91
WB1_SUB1_EXEC_ERRORED = copy.deepcopy(WB1_SUB1_EXEC)
92
WB1_SUB1_EXEC_ERRORED['state'] = 'ERROR'
93
WB1_SUB1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
94
WB1_SUB1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'ERROR'}
95
WB1_SUB1_TASKS = [tasks.Task(None, WB1_SUB1_TASK1), tasks.Task(None, WB1_SUB1_TASK2)]
96
97
# Non-workbook with a single workflow
98
WF1_META_FILE_NAME = TEST_FIXTURES['workflows'][0]
99
WF1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF1_META_FILE_NAME
100
WF1_META_CONTENT = loader.load_meta_file(WF1_META_FILE_PATH)
101
WF1_NAME = WF1_META_CONTENT['pack'] + '.' + WF1_META_CONTENT['name']
102
WF1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF1_META_CONTENT['entry_point']
103
WF1_ENTRY_POINT_X = WF1_ENTRY_POINT.replace(WF1_META_FILE_NAME, 'xformed_' + WF1_META_FILE_NAME)
104
WF1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF1_ENTRY_POINT_X))
105
WF1_YAML = yaml.safe_dump(WF1_SPEC, default_flow_style=False)
106
WF1 = workflows.Workflow(None, {'name': WF1_NAME, 'definition': WF1_YAML})
107
WF1_EXEC = {'id': str(uuid.uuid4()), 'state': 'ERROR', 'workflow_name': WF1_NAME}
108
WF1_EXEC_NOT_RERUNABLE = copy.deepcopy(WF1_EXEC)
109
WF1_EXEC_NOT_RERUNABLE['state'] = 'PAUSED'
110
WF1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
111
WF1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'SUCCESS'}
112
WF1_TASKS = [tasks.Task(None, WF1_TASK1), tasks.Task(None, WF1_TASK2)]
113
114
115
@mock.patch.object(
116
    CUDPublisher,
117
    'publish_update',
118
    mock.MagicMock(return_value=None))
119
@mock.patch.object(
120
    CUDPublisher,
121
    'publish_create',
122
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create))
123
@mock.patch.object(
124
    LiveActionPublisher,
125
    'publish_state',
126
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state))
127
class MistralRunnerTest(DbTestCase):
128
129
    @classmethod
130
    def setUpClass(cls):
131
        super(MistralRunnerTest, cls).setUpClass()
132
133
        # Override the retry configuration here otherwise st2tests.config.parse_args
134
        # in DbTestCase.setUpClass will reset these overrides.
135
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
136
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
137
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
138
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')
139
140
        # Register runners.
141
        runnersregistrar.register_runners()
142
143
        # Register test pack(s).
144
        actions_registrar = actionsregistrar.ActionsRegistrar(
145
            use_pack_cache=False,
146
            fail_on_failure=True
147
        )
148
149
        for pack in PACKS:
150
            actions_registrar.register_from_pack(pack)
151
152
    def setUp(self):
153
        super(MistralRunnerTest, self).setUp()
154
155
        # Mock the local runner run method.
156
        local_runner_cls = self.get_runner_class('local_runner', 'local_shell_command_runner')
157
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_EMPTY_RESULT, None)
158
        local_runner_cls.run = mock.Mock(return_value=local_run_result)
159
160
    @classmethod
161
    def get_runner_class(cls, package_name, module_name):
162
        return runners.get_runner(package_name, module_name).__class__
163
164
    @mock.patch.object(
165
        workflows.WorkflowManager, 'list',
166
        mock.MagicMock(return_value=[]))
167
    @mock.patch.object(
168
        workflows.WorkflowManager, 'get',
169
        mock.MagicMock(return_value=WF1))
170
    @mock.patch.object(
171
        workflows.WorkflowManager, 'create',
172
        mock.MagicMock(return_value=[WF1]))
173
    @mock.patch.object(
174
        executions.ExecutionManager, 'create',
175
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
176
    def test_resume_option(self):
177
        patched_mistral_runner = self.get_runner_class('mistral_v2', 'mistral_v2')
178
179
        mock_resume_result = (
180
            action_constants.LIVEACTION_STATUS_RUNNING,
181
            {'tasks': []},
182
            {'execution_id': str(uuid.uuid4())}
183
        )
184
185
        with mock.patch.object(patched_mistral_runner, 'resume_workflow',
186
                               mock.MagicMock(return_value=mock_resume_result)):
187
188
            liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
189
            liveaction1, execution1 = action_service.request(liveaction1)
190
            self.assertFalse(patched_mistral_runner.resume_workflow.called)
191
192
            # Rerun the execution.
193
            context = {
194
                're-run': {
195
                    'ref': execution1.id,
196
                    'tasks': ['x']
197
                }
198
            }
199
200
            liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
201
            liveaction2, execution2 = action_service.request(liveaction2)
202
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
203
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
204
205
            task_specs = {
206
                'x': {
207
                    'reset': False
208
                }
209
            }
210
211
            patched_mistral_runner.resume_workflow.assert_called_with(
212
                ex_ref=execution1,
213
                task_specs=task_specs
214
            )
215
216
    @mock.patch.object(
217
        workflows.WorkflowManager, 'list',
218
        mock.MagicMock(return_value=[]))
219
    @mock.patch.object(
220
        workflows.WorkflowManager, 'get',
221
        mock.MagicMock(return_value=WF1))
222
    @mock.patch.object(
223
        workflows.WorkflowManager, 'create',
224
        mock.MagicMock(return_value=[WF1]))
225
    @mock.patch.object(
226
        executions.ExecutionManager, 'create',
227
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
228
    def test_resume_option_reset_tasks(self):
229
        patched_mistral_runner = self.get_runner_class('mistral_v2', 'mistral_v2')
230
231
        mock_resume_result = (
232
            action_constants.LIVEACTION_STATUS_RUNNING,
233
            {'tasks': []},
234
            {'execution_id': str(uuid.uuid4())}
235
        )
236
237
        with mock.patch.object(patched_mistral_runner, 'resume_workflow',
238
                               mock.MagicMock(return_value=mock_resume_result)):
239
240
            liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
241
            liveaction1, execution1 = action_service.request(liveaction1)
242
            self.assertFalse(patched_mistral_runner.resume_workflow.called)
243
244
            # Rerun the execution.
245
            context = {
246
                're-run': {
247
                    'ref': execution1.id,
248
                    'tasks': ['x', 'y'],
249
                    'reset': ['y']
250
                }
251
            }
252
253
            liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
254
            liveaction2, execution2 = action_service.request(liveaction2)
255
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
256
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
257
258
            task_specs = {
259
                'x': {
260
                    'reset': False
261
                },
262
                'y': {
263
                    'reset': True
264
                }
265
            }
266
267
            patched_mistral_runner.resume_workflow.assert_called_with(
268
                ex_ref=execution1,
269
                task_specs=task_specs
270
            )
271
272
    @mock.patch.object(
273
        workflows.WorkflowManager, 'list',
274
        mock.MagicMock(return_value=[]))
275
    @mock.patch.object(
276
        workflows.WorkflowManager, 'get',
277
        mock.MagicMock(return_value=WF1))
278
    @mock.patch.object(
279
        workflows.WorkflowManager, 'create',
280
        mock.MagicMock(return_value=[WF1]))
281
    @mock.patch.object(
282
        executions.ExecutionManager, 'create',
283
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
284
    @mock.patch.object(
285
        executions.ExecutionManager, 'get',
286
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
287
    @mock.patch.object(
288
        tasks.TaskManager, 'list',
289
        mock.MagicMock(return_value=WF1_TASKS))
290
    def test_resume_workflow_not_in_rerunable_state(self):
291
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
292
        liveaction1, execution1 = action_service.request(liveaction1)
293
294
        # Rerun the execution.
295
        context = {
296
            're-run': {
297
                'ref': execution1.id,
298
                'tasks': ['say-friend']
299
            }
300
        }
301
302
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
303
        liveaction2, execution2 = action_service.request(liveaction2)
304
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
305
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
306
        self.assertIn('not in a rerunable state', liveaction2.result.get('error'))
307
308
    @mock.patch.object(
309
        workflows.WorkflowManager, 'list',
310
        mock.MagicMock(return_value=[]))
311
    @mock.patch.object(
312
        workflows.WorkflowManager, 'get',
313
        mock.MagicMock(return_value=WF1))
314
    @mock.patch.object(
315
        workflows.WorkflowManager, 'create',
316
        mock.MagicMock(return_value=[WF1]))
317
    @mock.patch.object(
318
        executions.ExecutionManager, 'list',
319
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
320
    @mock.patch.object(
321
        executions.ExecutionManager, 'create',
322
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
323
    @mock.patch.object(
324
        executions.ExecutionManager, 'get',
325
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
326
    @mock.patch.object(
327
        tasks.TaskManager, 'list',
328
        mock.MagicMock(return_value=WF1_TASKS))
329
    def test_resume_tasks_not_in_rerunable_state(self):
330
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
331
        liveaction1, execution1 = action_service.request(liveaction1)
332
333
        # Rerun the execution.
334
        context = {
335
            're-run': {
336
                'ref': execution1.id,
337
                'tasks': ['say-friend']
338
            }
339
        }
340
341
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
342
        liveaction2, execution2 = action_service.request(liveaction2)
343
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
344
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
345
        self.assertIn('Unable to identify rerunable', liveaction2.result.get('error'))
346
347
    @mock.patch.object(
348
        workflows.WorkflowManager, 'list',
349
        mock.MagicMock(return_value=[]))
350
    @mock.patch.object(
351
        workflows.WorkflowManager, 'get',
352
        mock.MagicMock(return_value=WF1))
353
    @mock.patch.object(
354
        workflows.WorkflowManager, 'create',
355
        mock.MagicMock(return_value=[WF1]))
356
    @mock.patch.object(
357
        executions.ExecutionManager, 'list',
358
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
359
    @mock.patch.object(
360
        executions.ExecutionManager, 'create',
361
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
362
    @mock.patch.object(
363
        executions.ExecutionManager, 'get',
364
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
365
    @mock.patch.object(
366
        tasks.TaskManager, 'list',
367
        mock.MagicMock(return_value=WF1_TASKS))
368
    def test_resume_unidentified_tasks(self):
369
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
370
        liveaction1, execution1 = action_service.request(liveaction1)
371
372
        # Rerun the execution.
373
        context = {
374
            're-run': {
375
                'ref': execution1.id,
376
                'tasks': ['x']
377
            }
378
        }
379
380
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
381
        liveaction2, execution2 = action_service.request(liveaction2)
382
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
383
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
384
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
385
386
    @mock.patch.object(
387
        workflows.WorkflowManager, 'list',
388
        mock.MagicMock(return_value=[]))
389
    @mock.patch.object(
390
        workflows.WorkflowManager, 'get',
391
        mock.MagicMock(return_value=WF1))
392
    @mock.patch.object(
393
        workbooks.WorkbookManager, 'create',
394
        mock.MagicMock(return_value=WB1))
395
    @mock.patch.object(
396
        executions.ExecutionManager, 'create',
397
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
398
    @mock.patch.object(
399
        executions.ExecutionManager, 'update',
400
        mock.MagicMock(side_effect=[
401
            executions.Execution(None, WB1_MAIN_EXEC),
402
            executions.Execution(None, WB1_SUB1_EXEC)
403
        ]))
404
    @mock.patch.object(
405
        executions.ExecutionManager, 'get',
406
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
407
    @mock.patch.object(
408
        executions.ExecutionManager, 'list',
409
        mock.MagicMock(side_effect=[
410
            [
411
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
412
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
413
            ],
414
            [
415
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
416
            ]
417
        ]))
418
    @mock.patch.object(
419
        tasks.TaskManager, 'list',
420
        mock.MagicMock(side_effect=[
421
            WB1_MAIN_TASKS,     # First call of _get_tasks at mistral_v2 runner
422
            WB1_SUB1_TASKS,     # Recursive call of the first _get_tasks
423
            WB1_MAIN_TASKS,     # tasks.list in _update_workflow_env at mistral_v2 runner
424
            []                  # Resursive call of _update_workflow_env
425
        ]))
426
    @mock.patch.object(
427
        tasks.TaskManager, 'rerun',
428
        mock.MagicMock(return_value=None))
429
    def test_resume_subworkflow_task(self):
430
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
431
        liveaction1, execution1 = action_service.request(liveaction1)
432
433
        # Rerun the execution.
434
        context = {
435
            're-run': {
436
                'ref': execution1.id,
437
                'tasks': ['greet.say-friend']
438
            }
439
        }
440
441
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
442
        liveaction2, execution2 = action_service.request(liveaction2)
443
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
444
445
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
446
447
        expected_env = {
448
            'st2_liveaction_id': str(liveaction2.id),
449
            'st2_execution_id': str(execution2.id),
450
            '__actions': {
451
                'st2.action': {
452
                    'st2_context': {
453
                        'api_url': 'http://0.0.0.0:9101/v1',
454
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
455
                        'notify': {},
456
                        'parent': {
457
                            'pack': 'mistral_tests',
458
                            're-run': context['re-run'],
459
                            'execution_id': str(execution2.id)
460
                        },
461
                        'skip_notify_tasks': []
462
                    }
463
                }
464
            },
465
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
466
        }
467
468
        tasks.TaskManager.rerun.assert_called_with(
469
            WB1_SUB1_TASK2['id'],
470
            reset=False,
471
            env=expected_env
472
        )
473
474
    @mock.patch.object(
475
        workflows.WorkflowManager, 'list',
476
        mock.MagicMock(return_value=[]))
477
    @mock.patch.object(
478
        workflows.WorkflowManager, 'get',
479
        mock.MagicMock(return_value=WF1))
480
    @mock.patch.object(
481
        workbooks.WorkbookManager, 'create',
482
        mock.MagicMock(return_value=WB1))
483
    @mock.patch.object(
484
        executions.ExecutionManager, 'create',
485
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
486
    @mock.patch.object(
487
        executions.ExecutionManager, 'get',
488
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
489
    @mock.patch.object(
490
        executions.ExecutionManager, 'list',
491
        mock.MagicMock(
492
            return_value=[
493
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
494
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
495
    @mock.patch.object(
496
        tasks.TaskManager, 'list',
497
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
498
    def test_resume_unidentified_subworkflow_task(self):
499
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
500
        liveaction1, execution1 = action_service.request(liveaction1)
501
502
        # Rerun the execution.
503
        context = {
504
            're-run': {
505
                'ref': execution1.id,
506
                'tasks': ['greet.x']
507
            }
508
        }
509
510
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
511
        liveaction2, execution2 = action_service.request(liveaction2)
512
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
513
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
514
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
515
516
    @mock.patch.object(
517
        workflows.WorkflowManager, 'list',
518
        mock.MagicMock(return_value=[]))
519
    @mock.patch.object(
520
        workflows.WorkflowManager, 'get',
521
        mock.MagicMock(return_value=WF1))
522
    @mock.patch.object(
523
        workbooks.WorkbookManager, 'create',
524
        mock.MagicMock(return_value=WB1))
525
    @mock.patch.object(
526
        executions.ExecutionManager, 'create',
527
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
528
    @mock.patch.object(
529
        executions.ExecutionManager, 'update',
530
        mock.MagicMock(side_effect=[
531
            executions.Execution(None, WB1_MAIN_EXEC),
532
            executions.Execution(None, WB1_SUB1_EXEC)
533
        ]))
534
    @mock.patch.object(
535
        executions.ExecutionManager, 'get',
536
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
537
    @mock.patch.object(
538
        executions.ExecutionManager, 'list',
539
        mock.MagicMock(side_effect=[
540
            [
541
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
542
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
543
            ],
544
            [
545
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
546
            ]
547
        ]))
548
    @mock.patch.object(
549
        tasks.TaskManager, 'list',
550
        mock.MagicMock(side_effect=[
551
            WB1_MAIN_TASKS,     # First call of _get_tasks at mistral_v2 runner
552
            WB1_SUB1_TASKS,     # Recursive call of the first _get_tasks
553
            WB1_MAIN_TASKS,     # tasks.list in _update_workflow_env at mistral_v2 runner
554
            []                  # Resursive call of _update_workflow_env
555
        ]))
556
    @mock.patch.object(
557
        tasks.TaskManager, 'rerun',
558
        mock.MagicMock(return_value=None))
559
    def test_resume_and_reset_subworkflow_task(self):
560
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
561
        liveaction1, execution1 = action_service.request(liveaction1)
562
563
        # Rerun the execution.
564
        context = {
565
            're-run': {
566
                'ref': execution1.id,
567
                'tasks': ['greet.say-friend'],
568
                'reset': ['greet.say-friend']
569
            }
570
        }
571
572
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
573
        liveaction2, execution2 = action_service.request(liveaction2)
574
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
575
576
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
577
578
        expected_env = {
579
            'st2_liveaction_id': str(liveaction2.id),
580
            'st2_execution_id': str(execution2.id),
581
            '__actions': {
582
                'st2.action': {
583
                    'st2_context': {
584
                        'api_url': 'http://0.0.0.0:9101/v1',
585
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
586
                        'notify': {},
587
                        'parent': {
588
                            'pack': 'mistral_tests',
589
                            're-run': context['re-run'],
590
                            'execution_id': str(execution2.id)
591
                        },
592
                        'skip_notify_tasks': []
593
                    }
594
                }
595
            },
596
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
597
        }
598
599
        tasks.TaskManager.rerun.assert_called_with(
600
            WB1_SUB1_TASK2['id'],
601
            reset=True,
602
            env=expected_env
603
        )
604