Add a position field to BaseResource
[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                 null);
278         }
279
280         static InstanceGroupMember createMember(Action action) {
281             return new InstanceGroupMember(null, action.toString(), null, null, null, null);
282         }
283
284         static Vnf createVnf(List<VfModule> vfModules, Action action) {
285             Map<String, Map<String, VfModule>> vfModulesMap = new HashMap<>();
286             vfModulesMap.put("abc",convertToMap(vfModules));
287
288             return new Vnf(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null, vfModulesMap, null, null, null,
289                 null);
290         }
291
292         static Vnf createVnf(Action action) {
293             return new Vnf(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null,null, null, null, null,
294                 null);
295         }
296
297         static VfModule createVfModule(Action action) {
298             return new VfModule(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, false, false, null, null, null, null,
299                 null);
300         }
301
302         static Network createNetwork(Action action) {
303             return new Network(mock(ModelInfo.class), null, null, action.toString(), null, null, null, null, null, null, false, null, null, null, null,
304                 null);
305         }
306     }
307
308     @DataProvider
309     public static Object[][] testIsDescendantHasActionDataProvider() {
310         return new Object[][]{
311                 {"empty service", Create, false, createService(emptyList(), emptyList(), emptyList())},
312                 {"instance group with None", Create, false, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(emptyList(), None)))},
313                 {"instance group with Create", Create, true, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(emptyList(), Create)))},
314                 {"instance group None + member Delete", Delete, true, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Delete)), None)))},
315                 {"instance group None + member Create", Delete, false, createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Create)), None)))},
316                 {"instance group None + member Create + member Delete", Delete, true,
317                         createService(emptyList(), emptyList(), ImmutableList.of(createGroup(ImmutableList.of(createMember(Create), createMember(Delete)), None)))},
318                 {"vnf Create", Delete, false, createService(ImmutableList.of(createVnf(emptyList(), Create)), emptyList(),emptyList())},
319                 {"vnf Create", Create, true, createService(ImmutableList.of(createVnf(emptyList(), Create)), emptyList(),emptyList())},
320                 {"vnf Create null VfModules internal map", Create, false, createService(ImmutableList.of(createVnf(null, Delete)), emptyList(),emptyList())},
321                 {"vnf Delete with null VfModules", Create, false, createService(ImmutableList.of(createVnf(Delete)), emptyList(),emptyList())},
322                 {"vnf None + VfModule Create", Create, true, createService(ImmutableList.of(createVnf(ImmutableList.of(createVfModule(Create)), None)), emptyList(),emptyList())},
323                 {"vnf None + VfModule None", Create, false, createService(ImmutableList.of(createVnf(ImmutableList.of(createVfModule(None)), None)), emptyList(),emptyList())},
324                 {"network Create", Create, true, createService(emptyList(), ImmutableList.of(createNetwork(Create)), emptyList())},
325                 {"network Delete", Create, false, createService(emptyList(), ImmutableList.of(createNetwork(Delete)), emptyList())},
326         };
327     }
328
329     @Test(dataProvider = "testIsDescendantHasActionDataProvider")
330     public void testIsDescendantHasAction(String desc, Action action, boolean expectedResult, BaseResource request) {
331         //in this test, there is no meaning to constructor parameters
332         MockCommand underTest = new MockCommand(InternalState.WATCHING, Delete, Job.JobStatus.COMPLETED);
333         assertEquals(expectedResult, underTest.isDescendantHasAction(request, action));
334     }
335
336     @DataProvider
337     public static Object[][] testCallDataProvider() {
338         return new Object[][]{
339                 {"initial state with successful creating children" ,InternalState.INITIAL, Job.JobStatus.COMPLETED, InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS},
340                 {"initial state with failed creating children", InternalState.INITIAL, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
341                 {"watching state with children still in progress" ,InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS, InternalState.WATCHING, Job.JobStatus.RESOURCE_IN_PROGRESS},
342                 {"watching state with children that completed with errors" ,InternalState.WATCHING, Job.JobStatus.COMPLETED_WITH_ERRORS, null, Job.JobStatus.COMPLETED_WITH_ERRORS},
343                 {"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},
344                 {"watching state with children that has completed" ,InternalState.WATCHING, Job.JobStatus.COMPLETED, InternalState.DELETE_MYSELF, Job.JobStatus.RESOURCE_IN_PROGRESS},
345                 {"mso call state that failed" ,InternalState.DELETE_MYSELF, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
346                 //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},
347                 {"mso call state that success" ,InternalState.DELETE_MYSELF, Job.JobStatus.COMPLETED, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
348                 {"in progress return in progress" ,InternalState.IN_PROGRESS, Job.JobStatus.IN_PROGRESS, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
349                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.PAUSE, InternalState.IN_PROGRESS, Job.JobStatus.RESOURCE_IN_PROGRESS},
350                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.STOPPED, null, Job.JobStatus.STOPPED},
351                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.FAILED, null, Job.JobStatus.FAILED},
352                 {"in progress return in pause" ,InternalState.IN_PROGRESS, Job.JobStatus.COMPLETED, null, Job.JobStatus.COMPLETED},
353
354         };
355     }
356
357     @Test(dataProvider = "testCallDataProvider")
358     public void whenCallCommandWithDeletePhase_nextJobStatusAndInternalStateAreAsExpected(
359             String description, InternalState internalState, Job.JobStatus currentStateResult,
360             InternalState expectedNextState, Job.JobStatus expectedNextStatus) {
361
362         MockCommandTestingStateMachine underTest = new MockCommandTestingStateMachine(internalState, Delete, currentStateResult, true);
363         NextCommand nextCommand = underTest.call();
364         assertEquals(expectedNextStatus, nextCommand.getStatus());
365
366         //expectedNextState == null means nextCommand has no real command
367         if (expectedNextState!=null) {
368             assertEquals(expectedNextState, (nextCommand.getCommand().getData().get(INTERNAL_STATE)));
369             assertFalse(nextCommand.getStatus().isFinal());
370         }
371         else {
372             assertNull(nextCommand.getCommand());
373             assertTrue(nextCommand.getStatus().isFinal());
374         }
375     }
376
377     @DataProvider
378     public static Object[][] InProgressDataProvider() {
379         return Stream.of(Job.JobStatus.values())
380                 .map(status -> new Object[] { status })
381                 .toArray(Object[][]::new);
382     }
383
384     @Test(dataProvider = "InProgressDataProvider")
385     public void whenGetResultFromMso_InProgressReturnThem(Job.JobStatus mockedJobStatus) {
386         Job.JobStatus expectedJobStatus = (mockedJobStatus== Job.JobStatus.PAUSE) ? Job.JobStatus.IN_PROGRESS : mockedJobStatus;
387         MockCommand underTest = new MockCommand(InternalState.IN_PROGRESS, Delete, mockedJobStatus);
388         when(underTest.getInProgressStatusService().call(any(), any(), any())).thenReturn(mockedJobStatus);
389         assertEquals(expectedJobStatus, underTest.inProgress());
390     }
391
392     @DataProvider
393     public static Object[][] InProgressExceptionsDataProvider() {
394         return new Object[][]{
395                 {new ProcessingException(""), Job.JobStatus.IN_PROGRESS},
396                 {new InProgressStatusService.BadResponseFromMso(null), Job.JobStatus.IN_PROGRESS},
397                 {new GenericUncheckedException(""),Job.JobStatus.STOPPED }
398         };
399     }
400
401     @Test(dataProvider = "InProgressExceptionsDataProvider")
402     public void whenInProgressStatusServiceThrowException_InProgressReturnStatus(Exception exception, Job.JobStatus expectedJobStatus) {
403         MockCommand underTest = new MockCommand(InternalState.IN_PROGRESS, Delete, expectedJobStatus);
404         when(underTest.getInProgressStatusService().call(any(), any(), any())).thenThrow(exception);
405         assertEquals(expectedJobStatus, underTest.inProgress());
406     }
407
408     @DataProvider
409     public static Object[][] testIsNeedToDeleteMySelfDataProvider() {
410         return Stream.of(values())
411                 .map(status -> new Object[] { status })
412                 .toArray(Object[][]::new);
413     }
414
415     @Test(dataProvider = "testIsNeedToDeleteMySelfDataProvider")
416     public void testIsNeedToDeleteMySelf(Action action) {
417         boolean expectedResult = (action== Delete);
418         MockCommand underTest = new MockCommand(InternalState.DELETE_MYSELF, Delete, Job.JobStatus.IN_PROGRESS);
419         BaseResource mockedBaseResource = mock(BaseResource.class);
420         when(underTest.getSharedData().getRequest()).thenReturn(mockedBaseResource);
421         when(mockedBaseResource.getAction()).thenReturn(action);
422         assertEquals(expectedResult, underTest.isNeedToDeleteMyself());
423     }
424
425     @DataProvider
426     public static Object[][] testWatchingDataProvider() {
427         return new Object[][]{
428                 {"all children final, no failed child ", Job.JobStatus.COMPLETED, Job.JobStatus.COMPLETED},
429                 {"all children final, there is failed child ", Job.JobStatus.COMPLETED_WITH_ERRORS, Job.JobStatus.COMPLETED_WITH_ERRORS},
430                 {"not all children final", Job.JobStatus.IN_PROGRESS, Job.JobStatus.IN_PROGRESS},
431         };
432     }
433
434     @Test(dataProvider = "testWatchingDataProvider")
435     public void testWatching(String desc, Job.JobStatus childrenJobsStatus, Job.JobStatus expectedJobStatus) {
436         MockCommand underTest = new MockCommand(InternalState.WATCHING, Delete, Job.JobStatus.IN_PROGRESS);
437         when(underTest.getWatchChildrenJobsBL().retrieveChildrenJobsStatus(any())).thenReturn(childrenJobsStatus);
438         assertEquals(expectedJobStatus, underTest.watchChildren());
439     }
440
441     @DataProvider
442     public static Object[][] testCalcInitialStateDataProvider() {
443         return new Object[][]{
444                 {Delete, true, Delete, InternalState.CREATING_CHILDREN},
445                 {Delete, false, Delete, InternalState.DELETE_MYSELF},
446                 {Delete, false, Create, InternalState.TERMINAL},
447                 {Delete, true, Create, InternalState.CREATING_CHILDREN},
448                 {Create, true, Create, InternalState.CREATE_MYSELF},
449                 {Create, false, Create, InternalState.CREATE_MYSELF},
450                 {Create, false, Delete, InternalState.TERMINAL},
451                 {Create, true, Delete, InternalState.CREATING_CHILDREN},
452                 {Create, true, Resume, InternalState.RESUME_MYSELF},
453                 {Delete, false, Resume, InternalState.TERMINAL},
454         };
455     }
456
457     @Test(dataProvider = "testCalcInitialStateDataProvider")
458     public void testCalcInitialState(Action phase, boolean isDescendantHasAction, Action action, InternalState expectedState) {
459         ResourceCommand underTest = mock(ResourceCommand.class);
460         when(underTest.calcInitialState(any(), any())).thenCallRealMethod();
461         when(underTest.isDescendantHasAction(eq(phase))).thenReturn(isDescendantHasAction);
462         when(underTest.getActionType()).thenReturn(action);
463         when(underTest.isNeedToDeleteMyself()).thenCallRealMethod();
464         when(underTest.isNeedToCreateMyself()).thenCallRealMethod();
465         when(underTest.isNeedToResumeMySelf()).thenCallRealMethod();
466
467         Map<String, String> commandData = ImmutableMap.of(INTERNAL_STATE, InternalState.INITIAL.name());
468         assertEquals(expectedState, underTest.calcInitialState(commandData, phase));
469     }
470
471
472     //throw exception when call to create children
473     //create children is just example, it could be any other method that called by ResourceCommand.invokeCommand
474     public static class MockCommandThrowExceptionOnCreateChildren extends MockCommandTestingStateMachine {
475
476         private final RuntimeException exceptionToThrow;
477
478         public MockCommandThrowExceptionOnCreateChildren(RuntimeException exceptionToThrow) {
479             super(InternalState.CREATING_CHILDREN, Delete, Job.JobStatus.COMPLETED, true);
480             this.exceptionToThrow = exceptionToThrow;
481             doAnswer(returnsFirstArg()).when(this.getWatchChildrenJobsBL()).cumulateJobStatus(any(), any());
482         }
483
484         @NotNull
485         @Override
486         public Job.JobStatus createChildren() {
487             throw exceptionToThrow;
488         }
489     }
490
491     @DataProvider
492     public static Object[][] exceptionAndStateProvider() {
493         return new Object[][]{
494                 {new TryAgainException(new Exception()), Job.JobStatus.RESOURCE_IN_PROGRESS},
495                 {new AbortingException(new Exception()), Job.JobStatus.FAILED},
496         };
497     }
498
499     @Test(dataProvider = "exceptionAndStateProvider")
500     public void whenKnownExceptionThrownInCommandInvocation_thenStateIsAsExpected(RuntimeException exception, Job.JobStatus expectedNextStatus) {
501         MockCommandTestingStateMachine underTest = new MockCommandThrowExceptionOnCreateChildren(exception);
502         NextCommand nextCommand = underTest.call();
503         assertEquals(expectedNextStatus, nextCommand.getStatus());
504     }
505
506 }