Passed
Push — develop ( b8d4ca...421369 )
by Plexxi
07:01 queued 03:57
created

MistralRunnerTest.test_resume_unidentified_tasks()   B

Complexity

Conditions 1

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 1
c 3
b 1
f 0
dl 0
loc 39
rs 8.8571
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
import copy
17
import uuid
18
19
import mock
20
import six
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
# Set defaults for retry options.
34
cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
35
cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
36
cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
37
38
import st2common.bootstrap.runnersregistrar as runners_registrar
39
from st2common.constants import action as action_constants
40
from st2common.models.api.action import ActionAPI
41
from st2common.models.db.liveaction import LiveActionDB
42
from st2common.persistence.action import Action
43
from st2common.persistence.liveaction import LiveAction
44
from st2common.services import action as action_service
45
from st2common.transport.liveaction import LiveActionPublisher
46
from st2common.transport.publishers import CUDPublisher
47
from st2tests import DbTestCase
48
from st2tests.fixturesloader import FixturesLoader
49
from tests.unit.base import MockLiveActionPublisher
50
from local_runner import LocalShellRunner
51
from mistral_v2 import MistralRunner
52
53
54
TEST_FIXTURES = {
55
    'workflows': [
56
        'workflow_v2.yaml',
57
        'workbook_v2_many_workflows.yaml'
58
    ],
59
    'actions': [
60
        'workflow_v2.yaml',
61
        'workbook_v2_many_workflows.yaml',
62
        'local.yaml'
63
    ]
64
}
65
66
PACK = 'generic'
67
LOADER = FixturesLoader()
68
FIXTURES = LOADER.load_fixtures(fixtures_pack=PACK, fixtures_dict=TEST_FIXTURES)
69
70
# Workbook with multiple workflows
71
WB1_YAML_FILE_NAME = TEST_FIXTURES['workflows'][1]
72
WB1_YAML_FILE_PATH = LOADER.get_fixture_file_path_abs(PACK, 'workflows', WB1_YAML_FILE_NAME)
73
WB1_SPEC = FIXTURES['workflows'][WB1_YAML_FILE_NAME]
74
WB1_YAML = yaml.safe_dump(WB1_SPEC, default_flow_style=False)
75
WB1_NAME = '%s.%s' % (PACK, WB1_YAML_FILE_NAME.replace('.yaml', ''))
76
WB1 = workbooks.Workbook(None, {'name': WB1_NAME, 'definition': WB1_YAML})
77
WB1_MAIN_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING'}
78
WB1_MAIN_EXEC['workflow_name'] = WB1_NAME + '.main'
79
WB1_MAIN_EXEC_ERRORED = copy.deepcopy(WB1_MAIN_EXEC)
80
WB1_MAIN_EXEC_ERRORED['state'] = 'ERROR'
81
WB1_MAIN_TASK1 = {'id': str(uuid.uuid4()), 'name': 'greet', 'state': 'ERROR'}
82
WB1_MAIN_TASKS = [tasks.Task(None, WB1_MAIN_TASK1)]
83
WB1_MAIN_TASK_ID = WB1_MAIN_TASK1['id']
84
WB1_SUB1_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'task_execution_id': WB1_MAIN_TASK_ID}
85
WB1_SUB1_EXEC['workflow_name'] = WB1_NAME + '.subflow1'
86
WB1_SUB1_EXEC_ERRORED = copy.deepcopy(WB1_SUB1_EXEC)
87
WB1_SUB1_EXEC_ERRORED['state'] = 'ERROR'
88
WB1_SUB1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
89
WB1_SUB1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'ERROR'}
90
WB1_SUB1_TASKS = [tasks.Task(None, WB1_SUB1_TASK1), tasks.Task(None, WB1_SUB1_TASK2)]
91
92
# Non-workbook with a single workflow
93
WF1_YAML_FILE_NAME = TEST_FIXTURES['workflows'][0]
94
WF1_YAML_FILE_PATH = LOADER.get_fixture_file_path_abs(PACK, 'workflows', WF1_YAML_FILE_NAME)
95
WF1_SPEC = FIXTURES['workflows'][WF1_YAML_FILE_NAME]
96
WF1_YAML = yaml.safe_dump(WF1_SPEC, default_flow_style=False)
97
WF1_NAME = '%s.%s' % (PACK, WF1_YAML_FILE_NAME.replace('.yaml', ''))
98
WF1 = workflows.Workflow(None, {'name': WF1_NAME, 'definition': WF1_YAML})
99
WF1_EXEC = {'id': str(uuid.uuid4()), 'state': 'ERROR', 'workflow_name': WF1_NAME}
100
WF1_EXEC_NOT_RERUNABLE = copy.deepcopy(WF1_EXEC)
101
WF1_EXEC_NOT_RERUNABLE['state'] = 'PAUSED'
102
WF1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
103
WF1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'SUCCESS'}
104
WF1_TASKS = [tasks.Task(None, WF1_TASK1), tasks.Task(None, WF1_TASK2)]
105
106
# Action executions requirements
107
ACTION_PARAMS = {'friend': 'Rocky'}
108
109
NON_EMPTY_RESULT = 'non-empty'
110
111
112
@mock.patch.object(LocalShellRunner, 'run', mock.
113
                   MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED,
114
                                           NON_EMPTY_RESULT, None)))
115
@mock.patch.object(CUDPublisher, 'publish_update', mock.MagicMock(return_value=None))
116
@mock.patch.object(CUDPublisher, 'publish_create',
117
                   mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create))
118
@mock.patch.object(LiveActionPublisher, 'publish_state',
119
                   mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state))
120
class MistralRunnerTest(DbTestCase):
121
122
    @classmethod
123
    def setUpClass(cls):
124
        super(MistralRunnerTest, cls).setUpClass()
125
        runners_registrar.register_runner_types()
126
127
        for _, fixture in six.iteritems(FIXTURES['actions']):
128
            instance = ActionAPI(**fixture)
129
            Action.add_or_update(ActionAPI.to_model(instance))
130
131
    def setUp(self):
132
        super(MistralRunnerTest, self).setUp()
133
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')
134
135
    @mock.patch.object(
136
        workflows.WorkflowManager, 'list',
137
        mock.MagicMock(return_value=[]))
138
    @mock.patch.object(
139
        workflows.WorkflowManager, 'get',
140
        mock.MagicMock(return_value=WF1))
141
    @mock.patch.object(
142
        workflows.WorkflowManager, 'create',
143
        mock.MagicMock(return_value=[WF1]))
144
    @mock.patch.object(
145
        executions.ExecutionManager, 'create',
146
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
147
    @mock.patch.object(
148
        MistralRunner, 'resume',
149
        mock.MagicMock(
150
            return_value=(action_constants.LIVEACTION_STATUS_RUNNING,
151
                          {'tasks': []},
152
                          {'execution_id': str(uuid.uuid4())})
153
        )
154
    )
155
    def test_resume_option(self):
156
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
157
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
158
        liveaction1, execution1 = action_service.request(liveaction1)
159
        self.assertFalse(MistralRunner.resume.called)
160
161
        # Rerun the execution.
162
        context = {
163
            're-run': {
164
                'ref': execution1.id,
165
                'tasks': ['x']
166
            }
167
        }
168
169
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
170
        liveaction2, execution2 = action_service.request(liveaction2)
171
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
172
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
173
174
        task_specs = {
175
            'x': {
176
                'reset': False
177
            }
178
        }
179
180
        MistralRunner.resume.assert_called_with(ex_ref=execution1, task_specs=task_specs)
181
182
    @mock.patch.object(
183
        workflows.WorkflowManager, 'list',
184
        mock.MagicMock(return_value=[]))
185
    @mock.patch.object(
186
        workflows.WorkflowManager, 'get',
187
        mock.MagicMock(return_value=WF1))
188
    @mock.patch.object(
189
        workflows.WorkflowManager, 'create',
190
        mock.MagicMock(return_value=[WF1]))
191
    @mock.patch.object(
192
        executions.ExecutionManager, 'create',
193
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
194
    @mock.patch.object(
195
        MistralRunner, 'resume',
196
        mock.MagicMock(
197
            return_value=(action_constants.LIVEACTION_STATUS_RUNNING,
198
                          {'tasks': []},
199
                          {'execution_id': str(uuid.uuid4())})
200
        )
201
    )
202
    def test_resume_option_reset_tasks(self):
203
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
204
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
205
        liveaction1, execution1 = action_service.request(liveaction1)
206
        self.assertFalse(MistralRunner.resume.called)
207
208
        # Rerun the execution.
209
        context = {
210
            're-run': {
211
                'ref': execution1.id,
212
                'tasks': ['x', 'y'],
213
                'reset': ['y']
214
            }
215
        }
216
217
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
218
        liveaction2, execution2 = action_service.request(liveaction2)
219
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
220
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
221
222
        task_specs = {
223
            'x': {
224
                'reset': False
225
            },
226
            'y': {
227
                'reset': True
228
            }
229
        }
230
231
        MistralRunner.resume.assert_called_with(ex_ref=execution1, task_specs=task_specs)
232
233
    @mock.patch.object(
234
        workflows.WorkflowManager, 'list',
235
        mock.MagicMock(return_value=[]))
236
    @mock.patch.object(
237
        workflows.WorkflowManager, 'get',
238
        mock.MagicMock(return_value=WF1))
239
    @mock.patch.object(
240
        workflows.WorkflowManager, 'create',
241
        mock.MagicMock(return_value=[WF1]))
242
    @mock.patch.object(
243
        executions.ExecutionManager, 'create',
244
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
245
    @mock.patch.object(
246
        executions.ExecutionManager, 'get',
247
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
248
    @mock.patch.object(
249
        tasks.TaskManager, 'list',
250
        mock.MagicMock(return_value=WF1_TASKS))
251
    def test_resume_workflow_not_in_rerunable_state(self):
252
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
253
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
254
        liveaction1, execution1 = action_service.request(liveaction1)
255
256
        # Rerun the execution.
257
        context = {
258
            're-run': {
259
                'ref': execution1.id,
260
                'tasks': ['say-friend']
261
            }
262
        }
263
264
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
265
        liveaction2, execution2 = action_service.request(liveaction2)
266
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
267
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
268
        self.assertIn('not in a rerunable state', liveaction2.result.get('error'))
269
270
    @mock.patch.object(
271
        workflows.WorkflowManager, 'list',
272
        mock.MagicMock(return_value=[]))
273
    @mock.patch.object(
274
        workflows.WorkflowManager, 'get',
275
        mock.MagicMock(return_value=WF1))
276
    @mock.patch.object(
277
        workflows.WorkflowManager, 'create',
278
        mock.MagicMock(return_value=[WF1]))
279
    @mock.patch.object(
280
        executions.ExecutionManager, 'list',
281
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
282
    @mock.patch.object(
283
        executions.ExecutionManager, 'create',
284
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
285
    @mock.patch.object(
286
        executions.ExecutionManager, 'get',
287
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
288
    @mock.patch.object(
289
        tasks.TaskManager, 'list',
290
        mock.MagicMock(return_value=WF1_TASKS))
291
    def test_resume_tasks_not_in_rerunable_state(self):
292
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
293
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
294
        liveaction1, execution1 = action_service.request(liveaction1)
295
296
        # Rerun the execution.
297
        context = {
298
            're-run': {
299
                'ref': execution1.id,
300
                'tasks': ['say-friend']
301
            }
302
        }
303
304
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
305
        liveaction2, execution2 = action_service.request(liveaction2)
306
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
307
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
308
        self.assertIn('Unable to identify rerunable', liveaction2.result.get('error'))
309
310
    @mock.patch.object(
311
        workflows.WorkflowManager, 'list',
312
        mock.MagicMock(return_value=[]))
313
    @mock.patch.object(
314
        workflows.WorkflowManager, 'get',
315
        mock.MagicMock(return_value=WF1))
316
    @mock.patch.object(
317
        workflows.WorkflowManager, 'create',
318
        mock.MagicMock(return_value=[WF1]))
319
    @mock.patch.object(
320
        executions.ExecutionManager, 'list',
321
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
322
    @mock.patch.object(
323
        executions.ExecutionManager, 'create',
324
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
325
    @mock.patch.object(
326
        executions.ExecutionManager, 'get',
327
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
328
    @mock.patch.object(
329
        tasks.TaskManager, 'list',
330
        mock.MagicMock(return_value=WF1_TASKS))
331
    def test_resume_unidentified_tasks(self):
332
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
333
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
334
        liveaction1, execution1 = action_service.request(liveaction1)
335
336
        # Rerun the execution.
337
        context = {
338
            're-run': {
339
                'ref': execution1.id,
340
                'tasks': ['x']
341
            }
342
        }
343
344
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
345
        liveaction2, execution2 = action_service.request(liveaction2)
346
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
347
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
348
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
349
350
    @mock.patch.object(
351
        workflows.WorkflowManager, 'list',
352
        mock.MagicMock(return_value=[]))
353
    @mock.patch.object(
354
        workflows.WorkflowManager, 'get',
355
        mock.MagicMock(return_value=WF1))
356
    @mock.patch.object(
357
        workbooks.WorkbookManager, 'create',
358
        mock.MagicMock(return_value=WB1))
359
    @mock.patch.object(
360
        executions.ExecutionManager, 'create',
361
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
362
    @mock.patch.object(
363
        executions.ExecutionManager, 'get',
364
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
365
    @mock.patch.object(
366
        executions.ExecutionManager, 'list',
367
        mock.MagicMock(
368
            return_value=[
369
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
370
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
371
    @mock.patch.object(
372
        tasks.TaskManager, 'list',
373
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
374
    @mock.patch.object(
375
        tasks.TaskManager, 'rerun',
376
        mock.MagicMock(return_value=None))
377
    def test_resume_subworkflow_task(self):
378
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
379
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
380
        liveaction1, execution1 = action_service.request(liveaction1)
381
382
        # Rerun the execution.
383
        context = {
384
            're-run': {
385
                'ref': execution1.id,
386
                'tasks': ['greet.say-friend']
387
            }
388
        }
389
390
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
391
        liveaction2, execution2 = action_service.request(liveaction2)
392
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
393
394
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
395
396
        expected_env = {
397
            'st2_liveaction_id': str(liveaction2.id),
398
            'st2_execution_id': str(execution2.id),
399
            '__actions': {
400
                'st2.action': {
401
                    'st2_context': {
402
                        'api_url': 'http://0.0.0.0:9101/v1',
403
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
404
                        'notify': {},
405
                        'parent': {
406
                            're-run': context['re-run'],
407
                            'execution_id': str(execution2.id)
408
                        },
409
                        'skip_notify_tasks': []
410
                    }
411
                }
412
            },
413
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
414
        }
415
416
        tasks.TaskManager.rerun.assert_called_with(
417
            WB1_SUB1_TASK2['id'],
418
            reset=False,
419
            env=expected_env
420
        )
421
422
    @mock.patch.object(
423
        workflows.WorkflowManager, 'list',
424
        mock.MagicMock(return_value=[]))
425
    @mock.patch.object(
426
        workflows.WorkflowManager, 'get',
427
        mock.MagicMock(return_value=WF1))
428
    @mock.patch.object(
429
        workbooks.WorkbookManager, 'create',
430
        mock.MagicMock(return_value=WB1))
431
    @mock.patch.object(
432
        executions.ExecutionManager, 'create',
433
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
434
    @mock.patch.object(
435
        executions.ExecutionManager, 'get',
436
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
437
    @mock.patch.object(
438
        executions.ExecutionManager, 'list',
439
        mock.MagicMock(
440
            return_value=[
441
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
442
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
443
    @mock.patch.object(
444
        tasks.TaskManager, 'list',
445
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
446
    def test_resume_unidentified_subworkflow_task(self):
447
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
448
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
449
        liveaction1, execution1 = action_service.request(liveaction1)
450
451
        # Rerun the execution.
452
        context = {
453
            're-run': {
454
                'ref': execution1.id,
455
                'tasks': ['greet.x']
456
            }
457
        }
458
459
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
460
        liveaction2, execution2 = action_service.request(liveaction2)
461
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
462
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
463
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
464
465
    @mock.patch.object(
466
        workflows.WorkflowManager, 'list',
467
        mock.MagicMock(return_value=[]))
468
    @mock.patch.object(
469
        workflows.WorkflowManager, 'get',
470
        mock.MagicMock(return_value=WF1))
471
    @mock.patch.object(
472
        workbooks.WorkbookManager, 'create',
473
        mock.MagicMock(return_value=WB1))
474
    @mock.patch.object(
475
        executions.ExecutionManager, 'create',
476
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
477
    @mock.patch.object(
478
        executions.ExecutionManager, 'get',
479
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
480
    @mock.patch.object(
481
        executions.ExecutionManager, 'list',
482
        mock.MagicMock(
483
            return_value=[
484
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
485
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
486
    @mock.patch.object(
487
        tasks.TaskManager, 'list',
488
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
489
    @mock.patch.object(
490
        tasks.TaskManager, 'rerun',
491
        mock.MagicMock(return_value=None))
492
    def test_resume_and_reset_subworkflow_task(self):
493
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
494
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
495
        liveaction1, execution1 = action_service.request(liveaction1)
496
497
        # Rerun the execution.
498
        context = {
499
            're-run': {
500
                'ref': execution1.id,
501
                'tasks': ['greet.say-friend'],
502
                'reset': ['greet.say-friend']
503
            }
504
        }
505
506
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
507
        liveaction2, execution2 = action_service.request(liveaction2)
508
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
509
510
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
511
512
        expected_env = {
513
            'st2_liveaction_id': str(liveaction2.id),
514
            'st2_execution_id': str(execution2.id),
515
            '__actions': {
516
                'st2.action': {
517
                    'st2_context': {
518
                        'api_url': 'http://0.0.0.0:9101/v1',
519
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
520
                        'notify': {},
521
                        'parent': {
522
                            're-run': context['re-run'],
523
                            'execution_id': str(execution2.id)
524
                        },
525
                        'skip_notify_tasks': []
526
                    }
527
                }
528
            },
529
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
530
        }
531
532
        tasks.TaskManager.rerun.assert_called_with(
533
            WB1_SUB1_TASK2['id'],
534
            reset=True,
535
            env=expected_env
536
        )
537