Implant vid-app-common org.onap.vid.job (main and test)
[vid.git] / vid-app-common / src / test / java / org / onap / vid / job / command / ResourceCommandTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.job.command;
22
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import org.jetbrains.annotations.NotNull;
26 import org.onap.vid.exceptions.AbortingException;
27 import org.onap.vid.exceptions.GenericUncheckedException;
28 import org.onap.vid.exceptions.TryAgainException;
29 import org.onap.vid.job.Job;
30 import org.onap.vid.job.JobAdapter;
31 import org.onap.vid.job.JobsBrokerService;
32 import org.onap.vid.job.NextCommand;
33 import org.onap.vid.job.impl.JobSharedData;
34 import org.onap.vid.model.Action;
35 import org.onap.vid.model.serviceInstantiation.*;
36 import org.onap.vid.mso.RestMsoImplementation;
37 import org.onap.vid.mso.model.ModelInfo;
38 import org.springframework.http.HttpMethod;
39 import org.testng.annotations.DataProvider;
40 import org.testng.annotations.Test;
41
42 import javax.ws.rs.ProcessingException;
43 import java.util.*;
44 import java.util.stream.Collectors;
45 import java.util.stream.Stream;
46
47 import static java.util.Collections.emptyList;
48 import static org.mockito.AdditionalAnswers.returnsFirstArg;
49 import static org.mockito.ArgumentMatchers.any;
50 import static org.mockito.Mockito.*;
51 import static org.onap.vid.job.command.ResourceCommandKt.ACTION_PHASE;
52 import static org.onap.vid.job.command.ResourceCommandKt.INTERNAL_STATE;
53 import static org.onap.vid.job.command.ResourceCommandTest.FakeResourceCreator.*;
54 import static org.onap.vid.model.Action.*;
55 import static org.onap.vid.utils.Logging.getMethodCallerName;
56 import static org.testng.Assert.assertNull;
57 import static org.testng.Assert.assertTrue;
58 import static org.testng.AssertJUnit.assertEquals;
59 import static org.testng.AssertJUnit.assertFalse;
60
61 public class ResourceCommandTest {
62
63     public static class MockCommand extends ResourceCommand {
64
65         public MockCommand(InternalState mockState, Action mockPhase, Job.JobStatus mockedJobStatus) {
66             this(mockState, mockPhase, mockedJobStatus, false);
67         }
68
69         public MockCommand(InternalState mockState, Action mockPhase, Job.JobStatus mockedJobStatus, boolean lateInit) {
70             super(
71                     mock(RestMsoImplementation.class, RETURNS_MOCKS),
72                     mock(InProgressStatusService.class),
73                     mock(MsoResultHandlerService.class, RETURNS_MOCKS),
74                     mock(WatchChildrenJobsBL.class),
75                     mock(JobsBrokerService.class, RETURNS_MOCKS),
76                     mock(JobAdapter.class, RETURNS_MOCKS));
77             this.mockedJobStatus = mockedJobStatus;
78             this.mockState = mockState;
79             this.mockPhase = mockPhase;
80             if (!lateInit) {
81                 init();
82             }
83             when(this.getWatchChildrenJobsBL().cumulateJobStatus(any(), any())).thenReturn(mockedJobStatus);
84         }
85
86         protected void init() {
87             if (mockState == InternalState.INITIAL) {
88                 init(mock(JobSharedData.class), Collections.emptyMap());
89             } else {
90                 init(mock(JobSharedData.class), ImmutableMap.of(INTERNAL_STATE, mockState.name(), ACTION_PHASE, mockPhase.name()));
91             }
92         }
93
94         private final Job.JobStatus mockedJobStatus;
95         private final InternalState mockState;
96         private final Action mockPhase;
97
98
99         @NotNull
100         @Override
101         public Job.JobStatus createChildren() {
102             if (mockState == InternalState.CREATING_CHILDREN || (mockState == InternalState.INITIAL && mockPhase== Delete))
103                 return mockedJobStatus;
104             throw (new RuntimeException("Not expected to call "+getMethodCallerName()));
105         }
106
107         protected Job.JobStatus mockedStatusOrThrow(InternalState expectedState) {
108             if (mockState == expectedState)
109                 return mockedJobStatus;
110             throw (new RuntimeException("Not expected to call "+getMethodCallerName()));
111         }
112
113         protected MsoRestCallPlan mockedPlanOrThrow(InternalState expectedState) {
114             if (mockState == expectedState)
115                 return new MsoRestCallPlan(HttpMethod.POST, "path", Optional.empty(), Optional.empty(), "nothing");
116             throw (new RuntimeException("Not expected to call "+getMethodCallerName()));
117         }
118
119         @NotNull
120         @Override
121         public MsoRestCallPlan planCreateMyselfRestCall(@NotNull CommandParentData commandParentData, @NotNull JobAdapter.AsyncJobRequest request, @NotNull String userId, String testApi) {
122             return mockedPlanOrThrow(InternalState.CREATE_MYSELF);
123         }
124
125         @NotNull
126         @Override
127         public MsoRestCallPlan planDeleteMyselfRestCall(@NotNull CommandParentData commandParentData, @NotNull JobAdapter.AsyncJobRequest request, @NotNull String userId) {
128             return mockedPlanOrThrow(InternalState.DELETE_MYSELF);
129         }
130     }
131
132     public static class MockCommandTestingStateMachine extends MockCommand {
133
134         private final JobSharedData sharedData;
135         private final boolean isDescendantHasAction;
136
137         public MockCommandTestingStateMachine(InternalState mockState, Action mockPhase, Job.JobStatus mockedJobStatus, boolean mockedNeedToDeleteMySelf) {
138             this(mockState, mockPhase, mockedJobStatus, mockedNeedToDeleteMySelf, false, true);
139         }
140
141         public MockCommandTestingStateMachine(InternalState mockState, Action mockPhase, Job.JobStatus mockedJobStatus, boolean mockedNeedToDeleteMySelf, boolean isService, boolean isDescendantHasAction) {
142             super(mockState, mockPhase, mockedJobStatus, true);
143             this.mockedNeedToDeleteMySelf = mockedNeedToDeleteMySelf;
144             this.isService = isService;
145             this.sharedData = mock(JobSharedData.class, RETURNS_MOCKS);
146             this.isDescendantHasAction = isDescendantHasAction;
147             init();
148         }
149
150         protected final boolean mockedNeedToDeleteMySelf;
151         private final boolean isService;
152
153         @NotNull
154         @Override
155         public Job.JobStatus inProgress() {
156             return mockedStatusOrThrow(InternalState.IN_PROGRESS);
157         }
158
159         @NotNull
160         @Override
161         public Job.JobStatus watchChildren() {
162             return mockedStatusOrThrow(InternalState.WATCHING);
163         }
164
165         @Override
166         public boolean isNeedToDeleteMyself() {
167             return mockedNeedToDeleteMySelf;
168         }
169
170         @Override
171         protected boolean isServiceCommand() {
172             return isService;
173         }
174
175         @Override
176         public JobSharedData getSharedData() {
177             return sharedData;
178         }
179
180         @Override
181         protected boolean isDescendantHasAction(@NotNull Action phase) {
182             return isDescendantHasAction;
183         }
184     }
185
186     @DataProvider
187     public static Object[][] nextStateDeletePhaseProvider() {
188         return new Object[][]{
189                 {InternalState.CREATING_CHILDREN, Job.JobStatus.COMPLETED, InternalState.WATCHING},
190                 {InternalState.WATCHING, Job.JobStatus.COMPLETED, InternalState.DELETE_MYSELF},
191                 {InternalState.WATCHING, Job.JobStatus.IN_PROGRESS, InternalState.WATCHING},
192                 {InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS, InternalState.WATCHING},
193                 {InternalState.DELETE_MYSELF, Job.JobStatus.COMPLETED, InternalState.IN_PROGRESS},
194                 {InternalState.IN_PROGRESS, Job.JobStatus.COMPLETED, InternalState.TERMINAL},
195                 {InternalState.IN_PROGRESS, Job.JobStatus.IN_PROGRESS, InternalState.IN_PROGRESS},
196                 {InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS, InternalState.IN_PROGRESS},
197         };
198     }
199
200     @Test(dataProvider = "nextStateDeletePhaseProvider")
201     public void whenCalcNextStateDeletePhase_expectedStateIsReturned(
202             InternalState internalState, Job.JobStatus jobStatus, InternalState expectedState) {
203
204         //there is no meaning to the constructor inputs here
205         MockCommandTestingStateMachine underTest = new MockCommandTestingStateMachine(InternalState.TERMINAL, Delete, Job.JobStatus.FAILED, true);
206         assertEquals(expectedState, underTest.calcNextStateDeletePhase(jobStatus, internalState));
207     }
208
209     @Test
210     public void whenNoNeedToDeleteMyself_internalStateMovesFromWatchingToTerminal() {
211         MockCommandTestingStateMachine underTest = new MockCommandTestingStateMachine(InternalState.WATCHING, Delete, Job.JobStatus.COMPLETED, false);
212         assertEquals(InternalState.TERMINAL, underTest.calcNextStateDeletePhase(Job.JobStatus.COMPLETED, InternalState.WATCHING));
213     }
214
215     @DataProvider
216     public static Object[][] testShallStopJobDataProvider() {
217         return new Object[][]{
218                 {Job.JobStatus.IN_PROGRESS, None, false, false},
219                 {Job.JobStatus.COMPLETED_WITH_NO_ACTION, None, false, false},
220                 {Job.JobStatus.COMPLETED, None, false, false},
221                 {Job.JobStatus.FAILED, None, false, true},
222                 {Job.JobStatus.COMPLETED_WITH_ERRORS, None, false, true},
223                 {Job.JobStatus.COMPLETED_WITH_ERRORS, None, true, false},
224                 {Job.JobStatus.FAILED, None, true, false},
225                 {Job.JobStatus.FAILED, Delete, true, true},
226                 {Job.JobStatus.FAILED, Create, true, true},
227         };
228     }
229
230
231     @Test(dataProvider = "testShallStopJobDataProvider")
232     public void testShallStopJob(Job.JobStatus jobStatus, Action action, boolean isService, boolean expectedResult) {
233         //in this test, there is no meaning to constructor parameters besides isService
234         MockCommandTestingStateMachine underTest = new MockCommandTestingStateMachine(InternalState.WATCHING, Delete, Job.JobStatus.COMPLETED, false, isService, true);
235
236         BaseResource mockedRequest = mock(BaseResource.class);
237         when(underTest.getSharedData().getRequest()).thenReturn(mockedRequest);
238         when(mockedRequest.getAction()).thenReturn(action);
239
240         assertEquals(expectedResult, underTest.shallStopJob(jobStatus));
241     }
242
243     public static class FakeResourceCreator {
244
245         public static<T> Map<String, T> convertToMap(List<T> list) {
246             if (list==null) {
247                 return null;
248             }
249             return list.stream().collect(Collectors.toMap(x-> UUID.randomUUID().toString(), x->x));
250         }
251
252         static ServiceInstantiation createService(List<Vnf> vnfs, List<Network> networks, List<InstanceGroup> vnfGroups) {
253             return new ServiceInstantiation(mock(ModelInfo.class), null, null, null, null, null, null, null, null, null, null, null, null, null, null,
254                     convertToMap(vnfs),
255                     convertToMap(networks),
256                     convertToMap(vnfGroups),
257                     null,
258                     null, false, 1, false,false,null, null, null, null, null, null, null);
259         }
260
261         public static ServiceInstantiation createServiceWith2InstancesInEachLevel(Action action) {
262             return createService(
263                     ImmutableList.of(
264                             createVnf(ImmutableList.of(createVfModule(action), createVfModule(action)), action),
265                             createVnf(ImmutableList.of(createVfModule(action), createVfModule(action)), action)),
266                     ImmutableList.of(
267                             createNetwork(action),
268                             createNetwork(action)),
269                     ImmutableList.of(
270                             createGroup(ImmutableList.of(createMember(action), createMember(action)), action),
271                             createGroup(ImmutableList.of(createMember(action), createMember(action)), action))
272                     );
273         }
274
275         static InstanceGroup createGroup(List<InstanceGroupMember> groupMembers, Action action) {
276             return new InstanceGroup(mock(ModelInfo.class), null, action.name(), false, null, convertToMap(groupMembers), null, null, null);
277         }
278
279         static InstanceGroupMember createMember(Action action) {
280             return new InstanceGroupMember(null, action.toString(), null, null, null);
281         }
282
283         static Vnf createVnf(List<VfModule> vfModules, Action action) {
284             Map<String, Map<String, VfModule>> vfModulesMap = new HashMap<>();
285             vfModulesMap.put("abc",convertToMap(vfModules));
286
287             return new Vnf(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null, vfModulesMap, null, null, null);
288         }
289
290         static Vnf createVnf(Action action) {
291             return new Vnf(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null,null, null, null, null);
292         }
293
294         static VfModule createVfModule(Action action) {
295             return new VfModule(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, false, false, null, null, null, null);
296         }
297
298         static Network createNetwork(Action action) {
299             return new Network(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null, null, null, null);
300         }
301     }
302
303     @DataProvider
304     public static Object[][] testIsDescendantHasActionDataProvider() {
305         return new Object[][]{
306                 {"empty service", Create, false, createService(emptyList(), emptyList(), emptyList())},
307                 {"instance group with None", Create, false, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(emptyList(), None)))},
308                 {"instance group with Create", Create, true, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(emptyList(), Create)))},
309                 {"instance group None + member Delete", Delete, true, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Delete)), None)))},
310                 {"instance group None + member Create", Delete, false, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Create)), None)))},
311                 {"instance group None + member Create + member Delete", Delete, true,
312                         createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Create), createMember(Delete)), None)))},
313                 {"vnf Create", Delete, false, createService(ImmutableList.of(createVnf(emptyList(), Create)), emptyList(),emptyList())},
314                 {"vnf Create", Create, true, createService(ImmutableList.of(createVnf(emptyList(), Create)), emptyList(),emptyList())},
315                 {"vnf Create null VfModules internal map", Create, false, createService(ImmutableList.of(createVnf(null, Delete)), emptyList(),emptyList())},
316                 {"vnf Delete with null VfModules", Create, false, createService(ImmutableList.of(createVnf(Delete)), emptyList(),emptyList())},
317                 {"vnf None + VfModule Create", Create, true, createService(ImmutableList.of(createVnf(ImmutableList.of(createVfModule(Create)), None)), emptyList(),emptyList())},
318                 {"vnf None + VfModule None", Create, false, createService(ImmutableList.of(createVnf(ImmutableList.of(createVfModule(None)), None)), emptyList(),emptyList())},
319                 {"network Create", Create, true, createService(emptyList(), ImmutableList.of(createNetwork(Create)), emptyList())},
320                 {"network Delete", Create, false, createService(emptyList(), ImmutableList.of(createNetwork(Delete)), emptyList())},
321         };
322     }
323
324     @Test(dataProvider = "testIsDescendantHasActionDataProvider")
325     public void testIsDescendantHasAction(String desc, Action action, boolean expectedResult, BaseResource request) {
326         //in this test, there is no meaning to constructor parameters
327         MockCommand underTest = new MockCommand(InternalState.WATCHING, Delete, Job.JobStatus.COMPLETED);
328         assertEquals(expectedResult, underTest.isDescendantHasAction(request, action));
329     }
330
331     @DataProvider
332     public static Object[][] testCallDataProvider() {
333         return new Object[][]{
334                 {"initial state with successful creating children" ,InternalState.INITIAL, Job.JobStatus.COMPLETED, InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS},
335                 {"initial state with failed creating children", InternalState.INITIAL, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
336                 {"watching state with children still in progress" ,InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS, InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS},
337                 {"watching state with children that completed with errors" ,InternalState.WATCHING, Job.JobStatus.COMPLETED_WITH_ERRORS, null, Job.JobStatus.COMPLETED_WITH_ERRORS},
338                 {"watching state with children that completed with no action" ,InternalState.WATCHING, Job.JobStatus.COMPLETED_WITH_NO_ACTION, InternalState.DELETE_MYSELF, Job.JobStatus.RESOURCE_IN_PROGRESS},
339                 {"watching state with children that has completed" ,InternalState.WATCHING, Job.JobStatus.COMPLETED, InternalState.DELETE_MYSELF, Job.JobStatus.RESOURCE_IN_PROGRESS},
340                 {"mso call state that failed" ,InternalState.DELETE_MYSELF, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
341                 //TODO handle AAI get unique name state {"mso call state that still in progress" ,InternalState.DELETE_MYSELF, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED, false},
342                 {"mso call state that success" ,InternalState.DELETE_MYSELF, Job.JobStatus.COMPLETED, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
343                 {"in progress return in progress" ,InternalState.IN_PROGRESS, Job.JobStatus.IN_PROGRESS, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
344                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.PAUSE, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
345                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.STOPPED, null, Job.JobStatus.STOPPED},
346                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
347                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.COMPLETED, null, Job.JobStatus.COMPLETED},
348
349         };
350     }
351
352     @Test(dataProvider = "testCallDataProvider")
353     public void whenCallCommandWithDeletePhase_nextJobStatusAndInternalStateAreAsExpected(
354             String description, InternalState internalState, Job.JobStatus currentStateResult,
355             InternalState expectedNextState, Job.JobStatus expectedNextStatus) {
356
357         MockCommandTestingStateMachine underTest = new MockCommandTestingStateMachine(internalState, Delete, currentStateResult, true);
358         NextCommand nextCommand = underTest.call();
359         assertEquals(expectedNextStatus, nextCommand.getStatus());
360
361         //expectedNextState == null means nextCommand has no real command
362         if (expectedNextState!=null) {
363             assertEquals(expectedNextState, (nextCommand.getCommand().getData().get(INTERNAL_STATE)));
364             assertFalse(nextCommand.getStatus().isFinal());
365         }
366         else {
367             assertNull(nextCommand.getCommand());
368             assertTrue(nextCommand.getStatus().isFinal());
369         }
370     }
371
372     @DataProvider
373     public static Object[][] InProgressDataProvider() {
374         return Stream.of(Job.JobStatus.values())
375                 .map(status -> new Object[] { status })
376                 .toArray(Object[][]::new);
377     }
378
379     @Test(dataProvider = "InProgressDataProvider")
380     public void whenGetResultFromMso_InProgressReturnThem(Job.JobStatus mockedJobStatus) {
381         Job.JobStatus expectedJobStatus = (mockedJobStatus== Job.JobStatus.PAUSE) ? Job.JobStatus.IN_PROGRESS : mockedJobStatus;
382         MockCommand underTest = new MockCommand(InternalState.IN_PROGRESS, Delete, mockedJobStatus);
383         when(underTest.getInProgressStatusService().call(any(), any(), any())).thenReturn(mockedJobStatus);
384         assertEquals(expectedJobStatus, underTest.inProgress());
385     }
386
387     @DataProvider
388     public static Object[][] InProgressExceptionsDataProvider() {
389         return new Object[][]{
390                 {new ProcessingException(""), Job.JobStatus.IN_PROGRESS},
391                 {new InProgressStatusService.BadResponseFromMso(null), Job.JobStatus.IN_PROGRESS},
392                 {new GenericUncheckedException(""),Job.JobStatus.STOPPED }
393         };
394     }
395
396     @Test(dataProvider = "InProgressExceptionsDataProvider")
397     public void whenInProgressStatusServiceThrowException_InProgressReturnStatus(Exception exception, Job.JobStatus expectedJobStatus) {
398         MockCommand underTest = new MockCommand(InternalState.IN_PROGRESS, Delete, expectedJobStatus);
399         when(underTest.getInProgressStatusService().call(any(), any(), any())).thenThrow(exception);
400         assertEquals(expectedJobStatus, underTest.inProgress());
401     }
402
403     @DataProvider
404     public static Object[][] testIsNeedToDeleteMySelfDataProvider() {
405         return Stream.of(values())
406                 .map(status -> new Object[] { status })
407                 .toArray(Object[][]::new);
408     }
409
410     @Test(dataProvider = "testIsNeedToDeleteMySelfDataProvider")
411     public void testIsNeedToDeleteMySelf(Action action) {
412         boolean expectedResult = (action== Delete);
413         MockCommand underTest = new MockCommand(InternalState.DELETE_MYSELF, Delete, Job.JobStatus.IN_PROGRESS);
414         BaseResource mockedBaseResource = mock(BaseResource.class);
415         when(underTest.getSharedData().getRequest()).thenReturn(mockedBaseResource);
416         when(mockedBaseResource.getAction()).thenReturn(action);
417         assertEquals(expectedResult, underTest.isNeedToDeleteMyself());
418     }
419
420     @DataProvider
421     public static Object[][] testWatchingDataProvider() {
422         return new Object[][]{
423                 {"all children final, no failed child ", Job.JobStatus.COMPLETED, Job.JobStatus.COMPLETED},
424                 {"all children final, there is failed child ", Job.JobStatus.COMPLETED_WITH_ERRORS, Job.JobStatus.COMPLETED_WITH_ERRORS},
425                 {"not all children final", Job.JobStatus.IN_PROGRESS, Job.JobStatus.IN_PROGRESS},
426         };
427     }
428
429     @Test(dataProvider = "testWatchingDataProvider")
430     public void testWatching(String desc, Job.JobStatus childrenJobsStatus, Job.JobStatus expectedJobStatus) {
431         MockCommand underTest = new MockCommand(InternalState.WATCHING, Delete, Job.JobStatus.IN_PROGRESS);
432         when(underTest.getWatchChildrenJobsBL().retrieveChildrenJobsStatus(any())).thenReturn(childrenJobsStatus);
433         assertEquals(expectedJobStatus, underTest.watchChildren());
434     }
435
436     @DataProvider
437     public static Object[][] testCalcInitialStateDataProvider() {
438         return new Object[][]{
439                 {Delete, true, Delete, InternalState.CREATING_CHILDREN},
440                 {Delete, false, Delete, InternalState.DELETE_MYSELF},
441                 {Delete, false, Create, InternalState.TERMINAL},
442                 {Delete, true, Create, InternalState.CREATING_CHILDREN},
443                 {Create, true, Create, InternalState.CREATE_MYSELF},
444                 {Create, false, Create, InternalState.CREATE_MYSELF},
445                 {Create, false, Delete, InternalState.TERMINAL},
446                 {Create, true, Delete, InternalState.CREATING_CHILDREN},
447                 {Create, true, Resume, InternalState.RESUME_MYSELF},
448                 {Delete, false, Resume, InternalState.TERMINAL},
449         };
450     }
451
452     @Test(dataProvider = "testCalcInitialStateDataProvider")
453     public void testCalcInitialState(Action phase, boolean isDescendantHasAction, Action action, InternalState expectedState) {
454         ResourceCommand underTest = mock(ResourceCommand.class);
455         when(underTest.calcInitialState(any(), any())).thenCallRealMethod();
456         when(underTest.isDescendantHasAction(eq(phase))).thenReturn(isDescendantHasAction);
457         when(underTest.getActionType()).thenReturn(action);
458         when(underTest.isNeedToDeleteMyself()).thenCallRealMethod();
459         when(underTest.isNeedToCreateMyself()).thenCallRealMethod();
460         when(underTest.isNeedToResumeMySelf()).thenCallRealMethod();
461
462         Map<String, String> commandData = ImmutableMap.of(INTERNAL_STATE, InternalState.INITIAL.name());
463         assertEquals(expectedState, underTest.calcInitialState(commandData, phase));
464     }
465
466
467     //throw exception when call to create children
468     //create children is just example, it could be any other method that called by ResourceCommand.invokeCommand
469     public static class MockCommandThrowExceptionOnCreateChildren extends MockCommandTestingStateMachine {
470
471         private final RuntimeException exceptionToThrow;
472
473         public MockCommandThrowExceptionOnCreateChildren(RuntimeException exceptionToThrow) {
474             super(InternalState.CREATING_CHILDREN, Delete, Job.JobStatus.COMPLETED, true);
475             this.exceptionToThrow = exceptionToThrow;
476             doAnswer(returnsFirstArg()).when(this.getWatchChildrenJobsBL()).cumulateJobStatus(any(), any());
477         }
478
479         @NotNull
480         @Override
481         public Job.JobStatus createChildren() {
482             throw exceptionToThrow;
483         }
484     }
485
486     @DataProvider
487     public static Object[][] exceptionAndStateProvider() {
488         return new Object[][]{
489                 {new TryAgainException(new Exception()), Job.JobStatus.RESOURCE_IN_PROGRESS},
490                 {new AbortingException(new Exception()), Job.JobStatus.FAILED},
491         };
492     }
493
494     @Test(dataProvider = "exceptionAndStateProvider")
495     public void whenKnownExceptionThrownInCommandInvocation_thenStateIsAsExpected(RuntimeException exception, Job.JobStatus expectedNextStatus) {
496         MockCommandTestingStateMachine underTest = new MockCommandThrowExceptionOnCreateChildren(exception);
497         NextCommand nextCommand = underTest.call();
498         assertEquals(expectedNextStatus, nextCommand.getStatus());
499     }
500
501 }