Test Failed
Pull Request — master (#4197)
by W
03:53
created

MistralRunnerPauseResumeTest.test_pause()   A

Complexity

Conditions 1

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 36
rs 9.016
c 0
b 0
f 0
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 workflows
25
from oslo_config import cfg
26
27
# XXX: actionsensor import depends on config being setup.
28
import st2tests.config as tests_config
29
tests_config.parse_args()
30
31
from mistral_v2.mistral_v2 import MistralRunner
32
from st2common.bootstrap import actionsregistrar
33
from st2common.bootstrap import runnersregistrar
34
from st2common.constants import action as action_constants
35
from st2common.models.db.execution import ActionExecutionDB
36
from st2common.models.db.liveaction import LiveActionDB
37
from st2common.persistence.liveaction import LiveAction
38
from st2common.runners import base as runners
39
from st2common.services import action as action_service
40
from st2common.transport.liveaction import LiveActionPublisher
41
from st2common.transport.publishers import CUDPublisher
42
from st2common.util import loader
43
from st2tests import DbTestCase
44
from st2tests import fixturesloader
45
from st2tests.mocks.liveaction import MockLiveActionPublisher
46
47
48
TEST_PACK = 'mistral_tests'
49
TEST_PACK_PATH = fixturesloader.get_fixtures_packs_base_path() + '/' + TEST_PACK
50
51
PACKS = [
52
    TEST_PACK_PATH,
53
    fixturesloader.get_fixtures_packs_base_path() + '/core'
54
]
55
56
# Action executions requirements
57
ACTION_PARAMS = {'friend': 'Rocky'}
58
NON_EMPTY_RESULT = 'non-empty'
59
60
# Non-workbook with a single workflow
61
WF1_META_FILE_NAME = 'workflow_v2.yaml'
62
WF1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF1_META_FILE_NAME
63
WF1_META_CONTENT = loader.load_meta_file(WF1_META_FILE_PATH)
64
WF1_NAME = WF1_META_CONTENT['pack'] + '.' + WF1_META_CONTENT['name']
65
WF1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF1_META_CONTENT['entry_point']
66
WF1_ENTRY_POINT_X = WF1_ENTRY_POINT.replace(WF1_META_FILE_NAME, 'xformed_' + WF1_META_FILE_NAME)
67
WF1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF1_ENTRY_POINT_X))
68
WF1_YAML = yaml.safe_dump(WF1_SPEC, default_flow_style=False)
69
WF1 = workflows.Workflow(None, {'name': WF1_NAME, 'definition': WF1_YAML})
70
WF1_OLD = workflows.Workflow(None, {'name': WF1_NAME, 'definition': ''})
71
WF1_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'workflow_name': WF1_NAME}
72
WF1_EXEC_PAUSED = copy.deepcopy(WF1_EXEC)
73
WF1_EXEC_PAUSED['state'] = 'PAUSED'
74
75
# Workflow with a subworkflow action
76
WF2_META_FILE_NAME = 'workflow_v2_call_workflow_action.yaml'
77
WF2_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF2_META_FILE_NAME
78
WF2_META_CONTENT = loader.load_meta_file(WF2_META_FILE_PATH)
79
WF2_NAME = WF2_META_CONTENT['pack'] + '.' + WF2_META_CONTENT['name']
80
WF2_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF2_META_CONTENT['entry_point']
81
WF2_ENTRY_POINT_X = WF2_ENTRY_POINT.replace(WF2_META_FILE_NAME, 'xformed_' + WF2_META_FILE_NAME)
82
WF2_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF2_ENTRY_POINT_X))
83
WF2_YAML = yaml.safe_dump(WF2_SPEC, default_flow_style=False)
84
WF2 = workflows.Workflow(None, {'name': WF2_NAME, 'definition': WF2_YAML})
85
WF2_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'workflow_name': WF2_NAME}
86
WF2_EXEC_PAUSED = copy.deepcopy(WF2_EXEC)
87
WF2_EXEC_PAUSED['state'] = 'PAUSED'
88
89
90
@mock.patch.object(
91
    CUDPublisher,
92
    'publish_update',
93
    mock.MagicMock(return_value=None))
94
@mock.patch.object(
95
    CUDPublisher,
96
    'publish_create',
97
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create))
98
@mock.patch.object(
99
    LiveActionPublisher,
100
    'publish_state',
101
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state))
102
class MistralRunnerPauseResumeTest(DbTestCase):
103
104
    @classmethod
105
    def setUpClass(cls):
106
        super(MistralRunnerPauseResumeTest, cls).setUpClass()
107
108
        # Override the retry configuration here otherwise st2tests.config.parse_args
109
        # in DbTestCase.setUpClass will reset these overrides.
110
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
111
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
112
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
113
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')
114
115
        # Register runners.
116
        runnersregistrar.register_runners()
117
118
        # Register test pack(s).
119
        actions_registrar = actionsregistrar.ActionsRegistrar(
120
            use_pack_cache=False,
121
            fail_on_failure=True
122
        )
123
124
        for pack in PACKS:
125
            actions_registrar.register_from_pack(pack)
126
127
    @classmethod
128
    def get_runner_class(cls, runner_name):
129
        return runners.get_runner(runner_name, runner_name).__class__
130
131
    @mock.patch.object(
132
        workflows.WorkflowManager, 'list',
133
        mock.MagicMock(return_value=[]))
134
    @mock.patch.object(
135
        workflows.WorkflowManager, 'get',
136
        mock.MagicMock(return_value=WF1))
137
    @mock.patch.object(
138
        workflows.WorkflowManager, 'create',
139
        mock.MagicMock(return_value=[WF1]))
140
    @mock.patch.object(
141
        executions.ExecutionManager, 'create',
142
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
143
    @mock.patch.object(
144
        executions.ExecutionManager, 'update',
145
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_PAUSED)))
146
    @mock.patch.object(
147
        action_service, 'is_children_active',
148
        mock.MagicMock(return_value=True))
149
    def test_pause(self):
150
        # Launch the workflow execution.
151
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
152
        liveaction, execution = action_service.request(liveaction)
153
        liveaction = LiveAction.get_by_id(str(liveaction.id))
154
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
155
156
        mistral_context = liveaction.context.get('mistral', None)
157
        self.assertIsNotNone(mistral_context)
158
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
159
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
160
161
        # Pause the workflow execution.
162
        requester = cfg.CONF.system_user.user
163
        liveaction, execution = action_service.request_pause(liveaction, requester)
164
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
165
        liveaction = LiveAction.get_by_id(str(liveaction.id))
166
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSING)
167
168
    @mock.patch.object(
169
        workflows.WorkflowManager, 'list',
170
        mock.MagicMock(return_value=[]))
171
    @mock.patch.object(
172
        workflows.WorkflowManager, 'get',
173
        mock.MagicMock(return_value=WF1))
174
    @mock.patch.object(
175
        workflows.WorkflowManager, 'create',
176
        mock.MagicMock(return_value=[WF1]))
177
    @mock.patch.object(
178
        executions.ExecutionManager, 'create',
179
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
180
    @mock.patch.object(
181
        executions.ExecutionManager, 'update',
182
        mock.MagicMock(side_effect=[
183
            executions.Execution(None, WF1_EXEC_PAUSED),
184
            executions.Execution(None, WF1_EXEC)]))
185
    @mock.patch.object(
186
        action_service, 'is_children_active',
187
        mock.MagicMock(return_value=True))
188
    def test_resume(self):
189
        # Launch the workflow execution.
190
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
191
        liveaction, execution = action_service.request(liveaction)
192
        liveaction = LiveAction.get_by_id(str(liveaction.id))
193
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
194
195
        mistral_context = liveaction.context.get('mistral', None)
196
        self.assertIsNotNone(mistral_context)
197
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
198
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
199
200
        # Pause the workflow execution.
201
        requester = cfg.CONF.system_user.user
202
        liveaction, execution = action_service.request_pause(liveaction, requester)
203
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
204
        liveaction = LiveAction.get_by_id(str(liveaction.id))
205
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSING)
206
207
        # Manually update the liveaction from pausing to paused. The paused state
208
        # is usually updated by the mistral querier.
209
        action_service.update_status(liveaction, action_constants.LIVEACTION_STATUS_PAUSED)
210
        liveaction = LiveAction.get_by_id(str(liveaction.id))
211
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSED)
212
213
        # Resume the workflow execution.
214
        liveaction, execution = action_service.request_resume(liveaction, requester)
215
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'RUNNING')
216
        liveaction = LiveAction.get_by_id(str(liveaction.id))
217
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
218
219
    @mock.patch.object(
220
        workflows.WorkflowManager, 'list',
221
        mock.MagicMock(return_value=[]))
222
    @mock.patch.object(
223
        workflows.WorkflowManager, 'get',
224
        mock.MagicMock(side_effect=[WF2, WF1]))
225
    @mock.patch.object(
226
        workflows.WorkflowManager, 'create',
227
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
228
    @mock.patch.object(
229
        executions.ExecutionManager, 'create',
230
        mock.MagicMock(side_effect=[
231
            executions.Execution(None, WF2_EXEC),
232
            executions.Execution(None, WF1_EXEC)]))
233
    @mock.patch.object(
234
        executions.ExecutionManager, 'update',
235
        mock.MagicMock(side_effect=[
236
            executions.Execution(None, WF2_EXEC_PAUSED),
237
            executions.Execution(None, WF1_EXEC_PAUSED),
238
            executions.Execution(None, WF2_EXEC),
239
            executions.Execution(None, WF1_EXEC)]))
240
    @mock.patch.object(
241
        action_service, 'is_children_active',
242
        mock.MagicMock(return_value=True))
243
    def test_resume_subworkflow_action(self):
244
        requester = cfg.CONF.system_user.user
245
246
        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
247
        liveaction1, execution1 = action_service.request(liveaction1)
248
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
249
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)
250
251
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
252
        liveaction2, execution2 = action_service.request(liveaction2)
253
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
254
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
255
256
        # Mock the children of the parent execution to make this
257
        # test case has subworkflow execution.
258
        with mock.patch.object(
259
                ActionExecutionDB, 'children',
260
                new_callable=mock.PropertyMock) as action_ex_children_mock:
261
            action_ex_children_mock.return_value = [execution2.id]
262
263
            mistral_context = liveaction1.context.get('mistral', None)
264
            self.assertIsNotNone(mistral_context)
265
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
266
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))
267
268
            # Pause the parent liveaction and check that the request is cascaded down.
269
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)
270
271
            self.assertTrue(executions.ExecutionManager.update.called)
272
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)
273
274
            calls = [
275
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
276
                mock.call(WF1_EXEC.get('id'), 'PAUSED')
277
            ]
278
279
            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
280
281
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
282
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSING)
283
284
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
285
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSING)
286
287
            # Manually set the liveaction status to PAUSED.
288
            action_service.update_status(liveaction2, action_constants.LIVEACTION_STATUS_PAUSED)
289
            action_service.update_status(liveaction1, action_constants.LIVEACTION_STATUS_PAUSED)
290
291
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
292
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSED)
293
294
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
295
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSED)
296
297
            # Resume the parent liveaction and check that the request is cascaded down.
298
            liveaction1, execution1 = action_service.request_resume(liveaction1, requester)
299
300
            # Includes the previous calls.
301
            self.assertTrue(executions.ExecutionManager.update.called)
302
            self.assertEqual(executions.ExecutionManager.update.call_count, 4)
303
304
            calls = [
305
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
306
                mock.call(WF1_EXEC.get('id'), 'PAUSED'),
307
                mock.call(WF2_EXEC.get('id'), 'RUNNING'),
308
                mock.call(WF1_EXEC.get('id'), 'RUNNING')
309
            ]
310
311
            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
312
313
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
314
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)
315
316
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
317
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
318
319
    @mock.patch.object(
320
        workflows.WorkflowManager, 'list',
321
        mock.MagicMock(return_value=[]))
322
    @mock.patch.object(
323
        workflows.WorkflowManager, 'get',
324
        mock.MagicMock(side_effect=[WF2, WF1]))
325
    @mock.patch.object(
326
        workflows.WorkflowManager, 'create',
327
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
328
    @mock.patch.object(
329
        executions.ExecutionManager, 'create',
330
        mock.MagicMock(side_effect=[
331
            executions.Execution(None, WF2_EXEC),
332
            executions.Execution(None, WF1_EXEC)]))
333
    @mock.patch.object(
334
        executions.ExecutionManager, 'update',
335
        mock.MagicMock(side_effect=[
336
            executions.Execution(None, WF2_EXEC_PAUSED),
337
            executions.Execution(None, WF1_EXEC_PAUSED),
338
            executions.Execution(None, WF2_EXEC),
339
            executions.Execution(None, WF1_EXEC)]))
340
    def test_pause_missing_subworkflow_action(self):
341
        requester = cfg.CONF.system_user.user
342
343
        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
344
        liveaction1, execution1 = action_service.request(liveaction1)
345
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
346
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)
347
348
        # Mock the children of the parent execution to make this
349
        # test case has subworkflow execution.
350
        with mock.patch.object(
351
                ActionExecutionDB, 'children',
352
                new_callable=mock.PropertyMock) as action_ex_children_mock:
353
            action_ex_children_mock.return_value = [uuid.uuid4().hex]
354
355
            mistral_context = liveaction1.context.get('mistral', None)
356
            self.assertIsNotNone(mistral_context)
357
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
358
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))
359
360
            # Pause the parent liveaction and check that the request is cascaded down.
361
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)
362
363
            self.assertTrue(executions.ExecutionManager.update.called)
364
            self.assertEqual(executions.ExecutionManager.update.call_count, 1)
365
366
            calls = [
367
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
368
            ]
369
370
            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
371
372
            # The workflow execution will fail because the liveaction for the subworkflow
373
            # should not be missing and we do not know what state it is in.
374
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
375
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_FAILED)
376
            self.assertIn('not a valid ObjectId', liveaction1.result.get('error', ''))
377
378
    @mock.patch.object(
379
        workflows.WorkflowManager, 'list',
380
        mock.MagicMock(return_value=[]))
381
    @mock.patch.object(
382
        workflows.WorkflowManager, 'get',
383
        mock.MagicMock(side_effect=[WF2, WF1]))
384
    @mock.patch.object(
385
        workflows.WorkflowManager, 'create',
386
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
387
    @mock.patch.object(
388
        executions.ExecutionManager, 'create',
389
        mock.MagicMock(side_effect=[
390
            executions.Execution(None, WF2_EXEC),
391
            executions.Execution(None, WF1_EXEC)]))
392
    @mock.patch.object(
393
        executions.ExecutionManager, 'update',
394
        mock.MagicMock(side_effect=[
395
            executions.Execution(None, WF2_EXEC_PAUSED),
396
            executions.Execution(None, WF1_EXEC_PAUSED),
397
            executions.Execution(None, WF2_EXEC),
398
            executions.Execution(None, WF1_EXEC)]))
399
    @mock.patch.object(
400
        action_service, 'is_children_active',
401
        mock.MagicMock(return_value=True))
402
    def test_resume_missing_subworkflow_action(self):
403
        requester = cfg.CONF.system_user.user
404
405
        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
406
        liveaction1, execution1 = action_service.request(liveaction1)
407
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
408
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)
409
410
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
411
        liveaction2, execution2 = action_service.request(liveaction2)
412
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
413
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
414
415
        # Mock the children of the parent execution to make this
416
        # test case has subworkflow execution.
417
        with mock.patch.object(
418
                ActionExecutionDB, 'children',
419
                new_callable=mock.PropertyMock) as action_ex_children_mock:
420
            action_ex_children_mock.return_value = [execution2.id]
421
422
            mistral_context = liveaction1.context.get('mistral', None)
423
            self.assertIsNotNone(mistral_context)
424
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
425
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))
426
427
            # Pause the parent liveaction and check that the request is cascaded down.
428
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)
429
430
            self.assertTrue(executions.ExecutionManager.update.called)
431
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)
432
433
            calls = [
434
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
435
                mock.call(WF1_EXEC.get('id'), 'PAUSED')
436
            ]
437
438
            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
439
440
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
441
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSING)
442
443
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
444
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSING)
445
446
            # Manually set the liveaction status to PAUSED.
447
            action_service.update_status(liveaction2, action_constants.LIVEACTION_STATUS_PAUSED)
448
            action_service.update_status(liveaction1, action_constants.LIVEACTION_STATUS_PAUSED)
449
450
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
451
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSED)
452
453
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
454
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSED)
455
456
        # Mock the children of the parent execution to make this
457
        # test case has subworkflow execution.
458
        with mock.patch.object(
459
                ActionExecutionDB, 'children',
460
                new_callable=mock.PropertyMock) as action_ex_children_mock:
461
            action_ex_children_mock.return_value = [uuid.uuid4().hex]
462
463
            # Resume the parent liveaction and check that the request is cascaded down.
464
            liveaction1, execution1 = action_service.request_resume(liveaction1, requester)
465
466
            # Includes the previous calls.
467
            self.assertTrue(executions.ExecutionManager.update.called)
468
            self.assertEqual(executions.ExecutionManager.update.call_count, 3)
469
470
            calls = [
471
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
472
                mock.call(WF1_EXEC.get('id'), 'PAUSED'),
473
                mock.call(WF2_EXEC.get('id'), 'RUNNING'),
474
            ]
475
476
            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
477
478
            # The workflow execution will fail because the liveaction for the subworkflow
479
            # should not be missing and we do not know what state it is in.
480
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
481
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_FAILED)
482
            self.assertIn('not a valid ObjectId', liveaction1.result.get('error', ''))
483