Skip to content

Commit d8b2aad

Browse files
authored
test: add test CloudExecutionPlanMapperTest (#888)
1 parent 06dc5ce commit d8b2aad

1 file changed

Lines changed: 183 additions & 0 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright 2026 Flamingock (https://www.flamingock.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* 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+
package io.flamingock.cloud.planner;
17+
18+
import io.flamingock.api.StageType;
19+
import io.flamingock.cloud.api.response.ChangeResponse;
20+
import io.flamingock.cloud.api.response.ExecutionPlanResponse;
21+
import io.flamingock.cloud.api.response.StageResponse;
22+
import io.flamingock.cloud.api.vo.CloudChangeAction;
23+
import io.flamingock.cloud.api.vo.CloudExecutionAction;
24+
import io.flamingock.internal.common.core.recovery.ManualInterventionRequiredException;
25+
import io.flamingock.internal.common.core.recovery.action.ChangeAction;
26+
import io.flamingock.internal.core.change.loaded.AbstractLoadedChange;
27+
import io.flamingock.internal.core.change.loaded.LoadedChangeBuilder;
28+
import io.flamingock.internal.core.change.executable.ExecutableChange;
29+
import io.flamingock.internal.core.pipeline.execution.ExecutableStage;
30+
import io.flamingock.internal.core.pipeline.loaded.stage.AbstractLoadedStage;
31+
import io.flamingock.internal.core.pipeline.loaded.stage.DefaultLoadedStage;
32+
import io.flamingock.internal.core.plan.ExecutionPlan;
33+
import io.flamingock.core.cloud.changes._001__CloudChange1;
34+
import io.flamingock.core.cloud.changes._002__CloudChange2;
35+
import org.junit.jupiter.api.BeforeAll;
36+
import org.junit.jupiter.api.DisplayName;
37+
import org.junit.jupiter.api.Test;
38+
39+
import java.util.Arrays;
40+
import java.util.Collections;
41+
import java.util.List;
42+
import java.util.Map;
43+
import java.util.stream.Collectors;
44+
45+
import static org.junit.jupiter.api.Assertions.*;
46+
47+
class CloudExecutionPlanMapperTest {
48+
49+
private static AbstractLoadedChange change1;
50+
private static AbstractLoadedChange change2;
51+
52+
@BeforeAll
53+
static void setup() {
54+
change1 = LoadedChangeBuilder.getCodeBuilderInstance(_001__CloudChange1.class).build();
55+
change2 = LoadedChangeBuilder.getCodeBuilderInstance(_002__CloudChange2.class).build();
56+
}
57+
58+
@Test
59+
@DisplayName("Should map APPLY action from cloud response to internal APPLY")
60+
void shouldMapApplyAction() {
61+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1));
62+
ExecutionPlanResponse response = buildResponse(
63+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.APPLY))
64+
);
65+
66+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
67+
68+
assertEquals(1, result.size());
69+
ExecutableChange execChange = result.get(0).getChanges().get(0);
70+
assertEquals(ChangeAction.APPLY, execChange.getAction());
71+
}
72+
73+
@Test
74+
@DisplayName("Should map SKIP action from cloud response to internal SKIP")
75+
void shouldMapSkipAction() {
76+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1));
77+
ExecutionPlanResponse response = buildResponse(
78+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.SKIP))
79+
);
80+
81+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
82+
83+
assertEquals(1, result.size());
84+
ExecutableChange execChange = result.get(0).getChanges().get(0);
85+
assertEquals(ChangeAction.SKIP, execChange.getAction());
86+
}
87+
88+
@Test
89+
@DisplayName("Should map MANUAL_INTERVENTION action from cloud response to internal MANUAL_INTERVENTION")
90+
void shouldMapManualInterventionAction() {
91+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1));
92+
ExecutionPlanResponse response = buildResponse(
93+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.MANUAL_INTERVENTION))
94+
);
95+
96+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
97+
98+
assertEquals(1, result.size());
99+
ExecutableChange execChange = result.get(0).getChanges().get(0);
100+
assertEquals(ChangeAction.MANUAL_INTERVENTION, execChange.getAction());
101+
}
102+
103+
@Test
104+
@DisplayName("Should default to SKIP when change is not present in the cloud response")
105+
void shouldDefaultToSkipWhenChangeNotInResponse() {
106+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1, change2));
107+
ExecutionPlanResponse response = buildResponse(
108+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.APPLY))
109+
);
110+
111+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
112+
113+
Map<String, ChangeAction> actions = result.get(0).getChanges().stream()
114+
.collect(Collectors.toMap(ExecutableChange::getId, ExecutableChange::getAction));
115+
assertEquals(ChangeAction.APPLY, actions.get(change1.getId()));
116+
assertEquals(ChangeAction.SKIP, actions.get(change2.getId()));
117+
}
118+
119+
@Test
120+
@DisplayName("Should correctly map mixed actions in a single stage")
121+
void shouldMapMixedActions() {
122+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1, change2));
123+
ExecutionPlanResponse response = buildResponse(
124+
buildStageResponse("stage-1", 0,
125+
changeResponse(change1.getId(), CloudChangeAction.APPLY),
126+
changeResponse(change2.getId(), CloudChangeAction.MANUAL_INTERVENTION))
127+
);
128+
129+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
130+
131+
Map<String, ChangeAction> actions = result.get(0).getChanges().stream()
132+
.collect(Collectors.toMap(ExecutableChange::getId, ExecutableChange::getAction));
133+
assertEquals(ChangeAction.APPLY, actions.get(change1.getId()));
134+
assertEquals(ChangeAction.MANUAL_INTERVENTION, actions.get(change2.getId()));
135+
}
136+
137+
@Test
138+
@DisplayName("Should only include stages that are present in the cloud response")
139+
void shouldFilterStagesNotInResponse() {
140+
List<AbstractLoadedStage> loadedStages = Arrays.asList(
141+
buildStage("stage-1", change1),
142+
buildStage("stage-2", change2)
143+
);
144+
ExecutionPlanResponse response = buildResponse(
145+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.APPLY))
146+
);
147+
148+
List<ExecutableStage> result = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
149+
150+
assertEquals(1, result.size());
151+
assertEquals("stage-1", result.get(0).getName());
152+
}
153+
154+
@Test
155+
@DisplayName("Should throw ManualInterventionRequiredException when validate() is called on plan with MANUAL_INTERVENTION")
156+
void shouldThrowManualInterventionOnValidate() {
157+
List<AbstractLoadedStage> loadedStages = Arrays.asList(buildStage("stage-1", change1));
158+
ExecutionPlanResponse response = buildResponse(
159+
buildStageResponse("stage-1", 0, changeResponse(change1.getId(), CloudChangeAction.MANUAL_INTERVENTION))
160+
);
161+
162+
List<ExecutableStage> stages = CloudExecutionPlanMapper.getExecutableStages(response, loadedStages);
163+
ExecutionPlan plan = ExecutionPlan.newExecution("exec-1", null, stages);
164+
165+
assertThrows(ManualInterventionRequiredException.class, plan::validate);
166+
}
167+
168+
private static DefaultLoadedStage buildStage(String name, AbstractLoadedChange... changes) {
169+
return new DefaultLoadedStage(name, StageType.DEFAULT, Arrays.asList(changes));
170+
}
171+
172+
private static ExecutionPlanResponse buildResponse(StageResponse... stages) {
173+
return new ExecutionPlanResponse(CloudExecutionAction.EXECUTE, "exec-1", null, Arrays.asList(stages));
174+
}
175+
176+
private static StageResponse buildStageResponse(String name, int order, ChangeResponse... changes) {
177+
return new StageResponse(name, order, Arrays.asList(changes));
178+
}
179+
180+
private static ChangeResponse changeResponse(String id, CloudChangeAction action) {
181+
return new ChangeResponse(id, action);
182+
}
183+
}

0 commit comments

Comments
 (0)