1616package io .flamingock .cloud .planner ;
1717
1818import io .flamingock .api .StageType ;
19+ import io .flamingock .cloud .api .request .ExecutionPlanRequest ;
20+ import io .flamingock .cloud .api .request .ChangeRequest ;
1921import io .flamingock .cloud .api .response .ChangeResponse ;
2022import io .flamingock .cloud .api .response .ExecutionPlanResponse ;
2123import io .flamingock .cloud .api .response .StageResponse ;
2224import io .flamingock .cloud .api .vo .CloudChangeAction ;
2325import io .flamingock .cloud .api .vo .CloudExecutionAction ;
26+ import io .flamingock .cloud .api .vo .CloudTargetSystemAuditMarkType ;
2427import io .flamingock .cloud .lock .CloudLockService ;
2528import io .flamingock .cloud .planner .client .ExecutionPlannerClient ;
2629import io .flamingock .internal .common .core .recovery .ManualInterventionRequiredException ;
30+ import io .flamingock .internal .common .core .targets .TargetSystemAuditMarkType ;
2731import io .flamingock .internal .core .change .loaded .AbstractLoadedChange ;
2832import io .flamingock .internal .core .change .loaded .LoadedChangeBuilder ;
2933import io .flamingock .internal .core .configuration .core .CoreConfigurable ;
34+ import io .flamingock .internal .core .external .targets .mark .TargetSystemAuditMark ;
3035import io .flamingock .internal .core .external .targets .mark .TargetSystemAuditMarker ;
3136import io .flamingock .internal .core .plan .ExecutionPlan ;
3237import io .flamingock .internal .core .pipeline .loaded .stage .AbstractLoadedStage ;
3338import io .flamingock .internal .core .pipeline .loaded .stage .DefaultLoadedStage ;
3439import io .flamingock .internal .util .TimeService ;
3540import io .flamingock .internal .util .id .RunnerId ;
3641import io .flamingock .core .cloud .changes ._001__CloudChange1 ;
42+ import io .flamingock .core .cloud .changes ._002__CloudChange2 ;
3743import org .junit .jupiter .api .BeforeAll ;
3844import org .junit .jupiter .api .BeforeEach ;
3945import org .junit .jupiter .api .DisplayName ;
4046import org .junit .jupiter .api .Test ;
47+ import org .mockito .ArgumentCaptor ;
4148
4249import java .util .Arrays ;
4350import java .util .Collections ;
51+ import java .util .HashSet ;
4452import java .util .List ;
53+ import java .util .Map ;
54+ import java .util .stream .Collectors ;
4555
4656import static org .junit .jupiter .api .Assertions .*;
4757import static org .mockito .ArgumentMatchers .any ;
4858import static org .mockito .ArgumentMatchers .anyLong ;
49- import static org .mockito .ArgumentMatchers .anyString ;
5059import static org .mockito .Mockito .mock ;
60+ import static org .mockito .Mockito .verify ;
5161import static org .mockito .Mockito .when ;
5262
5363class CloudExecutionPlannerTest {
5464
5565 private static AbstractLoadedChange change1 ;
66+ private static AbstractLoadedChange change2 ;
5667
5768 private ExecutionPlannerClient client ;
58- private CloudExecutionPlanner planner ;
69+ private CoreConfigurable config ;
5970
6071 @ BeforeAll
6172 static void setupChanges () {
6273 change1 = LoadedChangeBuilder .getCodeBuilderInstance (_001__CloudChange1 .class ).build ();
74+ change2 = LoadedChangeBuilder .getCodeBuilderInstance (_002__CloudChange2 .class ).build ();
6375 }
6476
6577 @ BeforeEach
6678 void setup () {
6779 client = mock (ExecutionPlannerClient .class );
68- CoreConfigurable config = mock (CoreConfigurable .class );
80+ config = mock (CoreConfigurable .class );
6981 when (config .getLockAcquiredForMillis ()).thenReturn (60000L );
7082 when (config .getLockQuitTryingAfterMillis ()).thenReturn (30000L );
7183 when (config .getLockTryFrequencyMillis ()).thenReturn (1000L );
84+ }
7285
73- TargetSystemAuditMarker auditMarker = mock (TargetSystemAuditMarker .class );
74- when (auditMarker .listAll ()).thenReturn (Collections .emptySet ());
75-
76- planner = new CloudExecutionPlanner (
86+ private CloudExecutionPlanner buildPlanner (List <TargetSystemAuditMarker > auditMarkers ) {
87+ return new CloudExecutionPlanner (
7788 RunnerId .fromString ("test-runner" ),
7889 client ,
7990 config ,
8091 mock (CloudLockService .class ),
81- auditMarker ,
92+ auditMarkers ,
8293 TimeService .getDefault ()
8394 );
8495 }
8596
8697 @ Test
8798 @ DisplayName ("Should return ABORT plan when server returns ABORT with MANUAL_INTERVENTION changes" )
8899 void shouldReturnAbortPlanWhenServerReturnsAbort () {
100+ CloudExecutionPlanner planner = buildPlanner (Collections .emptyList ());
101+
89102 ExecutionPlanResponse response = new ExecutionPlanResponse (
90- CloudExecutionAction .ABORT ,
91- "exec-1" ,
92- null ,
103+ CloudExecutionAction .ABORT , "exec-1" , null ,
93104 Collections .singletonList (new StageResponse ("stage-1" , 0 ,
94105 Collections .singletonList (new ChangeResponse (change1 .getId (), CloudChangeAction .MANUAL_INTERVENTION ))))
95106 );
@@ -101,17 +112,16 @@ void shouldReturnAbortPlanWhenServerReturnsAbort() {
101112 ExecutionPlan plan = planner .getNextExecution (stages );
102113
103114 assertTrue (plan .isAborted ());
104- assertFalse (plan .isExecutionRequired ());
105115 assertThrows (ManualInterventionRequiredException .class , plan ::validate );
106116 }
107117
108118 @ Test
109119 @ DisplayName ("Should return ABORT plan that throws FlamingockException when server returns ABORT but no MI changes" )
110120 void shouldReturnAbortPlanWhenServerReturnsAbortWithNoMIChanges () {
121+ CloudExecutionPlanner planner = buildPlanner (Collections .emptyList ());
122+
111123 ExecutionPlanResponse response = new ExecutionPlanResponse (
112- CloudExecutionAction .ABORT ,
113- "exec-1" ,
114- null ,
124+ CloudExecutionAction .ABORT , "exec-1" , null ,
115125 Collections .singletonList (new StageResponse ("stage-1" , 0 ,
116126 Collections .singletonList (new ChangeResponse (change1 .getId (), CloudChangeAction .APPLY ))))
117127 );
@@ -125,4 +135,67 @@ void shouldReturnAbortPlanWhenServerReturnsAbortWithNoMIChanges() {
125135 assertTrue (plan .isAborted ());
126136 assertThrows (io .flamingock .internal .common .core .error .FlamingockException .class , plan ::validate );
127137 }
138+
139+ @ Test
140+ @ DisplayName ("Should include audit marks from multiple target systems in the execution request" )
141+ void shouldIncludeAuditMarksInExecutionRequest () {
142+ TargetSystemAuditMarker marker1 = mock (TargetSystemAuditMarker .class );
143+ when (marker1 .listAll ()).thenReturn (new HashSet <>(Collections .singletonList (
144+ new TargetSystemAuditMark (change1 .getId (), TargetSystemAuditMarkType .APPLIED )
145+ )));
146+
147+ TargetSystemAuditMarker marker2 = mock (TargetSystemAuditMarker .class );
148+ when (marker2 .listAll ()).thenReturn (new HashSet <>(Collections .singletonList (
149+ new TargetSystemAuditMark (change2 .getId (), TargetSystemAuditMarkType .ROLLED_BACK )
150+ )));
151+
152+ CloudExecutionPlanner planner = buildPlanner (Arrays .asList (marker1 , marker2 ));
153+
154+ ExecutionPlanResponse response = new ExecutionPlanResponse (
155+ CloudExecutionAction .CONTINUE , "exec-1" , null ,
156+ Collections .singletonList (new StageResponse ("stage-1" , 0 , Arrays .asList (
157+ new ChangeResponse (change1 .getId (), CloudChangeAction .SKIP ),
158+ new ChangeResponse (change2 .getId (), CloudChangeAction .APPLY ))))
159+ );
160+ when (client .createExecution (any (), any (), anyLong ())).thenReturn (response );
161+
162+ List <AbstractLoadedStage > stages = Collections .singletonList (
163+ new DefaultLoadedStage ("stage-1" , StageType .DEFAULT , Arrays .asList (change1 , change2 )));
164+
165+ planner .getNextExecution (stages );
166+
167+ ArgumentCaptor <ExecutionPlanRequest > requestCaptor = ArgumentCaptor .forClass (ExecutionPlanRequest .class );
168+ verify (client ).createExecution (requestCaptor .capture (), any (), anyLong ());
169+
170+ ExecutionPlanRequest request = requestCaptor .getValue ();
171+ Map <String , CloudTargetSystemAuditMarkType > marksByChangeId = request .getClientSubmission ().getStages ().get (0 ).getChanges ().stream ()
172+ .collect (Collectors .toMap (ChangeRequest ::getId , ChangeRequest ::getOngoingStatus ));
173+
174+ assertEquals (CloudTargetSystemAuditMarkType .APPLIED , marksByChangeId .get (change1 .getId ()));
175+ assertEquals (CloudTargetSystemAuditMarkType .ROLLED_BACK , marksByChangeId .get (change2 .getId ()));
176+ }
177+
178+ @ Test
179+ @ DisplayName ("Should send NONE status when no audit marks exist for a change" )
180+ void shouldSendNoneStatusWhenNoMarks () {
181+ CloudExecutionPlanner planner = buildPlanner (Collections .emptyList ());
182+
183+ ExecutionPlanResponse response = new ExecutionPlanResponse (
184+ CloudExecutionAction .CONTINUE , "exec-1" , null ,
185+ Collections .singletonList (new StageResponse ("stage-1" , 0 ,
186+ Collections .singletonList (new ChangeResponse (change1 .getId (), CloudChangeAction .SKIP ))))
187+ );
188+ when (client .createExecution (any (), any (), anyLong ())).thenReturn (response );
189+
190+ List <AbstractLoadedStage > stages = Collections .singletonList (
191+ new DefaultLoadedStage ("stage-1" , StageType .DEFAULT , Collections .singletonList (change1 )));
192+
193+ planner .getNextExecution (stages );
194+
195+ ArgumentCaptor <ExecutionPlanRequest > requestCaptor = ArgumentCaptor .forClass (ExecutionPlanRequest .class );
196+ verify (client ).createExecution (requestCaptor .capture (), any (), anyLong ());
197+
198+ ChangeRequest changeRequest = requestCaptor .getValue ().getClientSubmission ().getStages ().get (0 ).getChanges ().get (0 );
199+ assertEquals (CloudTargetSystemAuditMarkType .NONE , changeRequest .getOngoingStatus ());
200+ }
128201}
0 commit comments