Skip to content

Commit a3ff4f1

Browse files
authored
fix(engine): Snapshot report's inputs/outputs in engine after each step execution (#136)
1 parent df068b1 commit a3ff4f1

File tree

24 files changed

+458
-248
lines changed

24 files changed

+458
-248
lines changed

chutney/engine/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
<groupId>org.springframework</groupId>
3737
<artifactId>spring-core</artifactId>
3838
</dependency>
39+
<dependency>
40+
<groupId>org.jdom</groupId>
41+
<artifactId>jdom2</artifactId>
42+
</dependency>
3943
<dependency>
4044
<groupId>org.springframework.security</groupId>
4145
<artifactId>spring-security-core</artifactId>

chutney/engine/src/main/java/com/chutneytesting/ExecutionConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public class ExecutionConfiguration {
6565

6666
private final SpelFunctions spelFunctions;
6767
private final Set<StepExecutionStrategy> stepExecutionStrategies;
68-
6968
private final Long reporterTTL;
7069

7170
public ExecutionConfiguration() {
@@ -105,6 +104,7 @@ public ExecutionEngine executionEngine() {
105104
return executionEngine;
106105
}
107106

107+
108108
private ActionTemplateLoader createActionTemplateLoaderV2() {
109109
return new DefaultActionTemplateLoader<>(
110110
"chutney.actions",

chutney/engine/src/main/java/com/chutneytesting/engine/api/execution/StepExecutionReportMapper.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.chutneytesting.engine.api.execution;
1818

1919
import static java.util.Collections.EMPTY_MAP;
20+
import static java.util.Optional.ofNullable;
2021

2122
import com.chutneytesting.engine.domain.execution.report.Status;
2223
import com.chutneytesting.engine.domain.execution.report.StepExecutionReport;
@@ -38,7 +39,7 @@ static StepExecutionReportDto toDto(StepExecutionReport report) {
3839
report.information,
3940
report.errors,
4041
report.steps.stream().map(StepExecutionReportMapper::toDto).collect(Collectors.toList()),
41-
StepContextMapper.toDto(report.scenarioContext, report.evaluatedInputs, report.stepResults),
42+
StepContextMapper.toDto(report.scenarioContext, report.evaluatedInputsSnapshot, report.stepResultsSnapshot),
4243
report.type,
4344
report.targetName,
4445
report.targetUrl,
@@ -49,11 +50,11 @@ static StepExecutionReportDto toDto(StepExecutionReport report) {
4950
static class StepContextMapper {
5051

5152
@SuppressWarnings("unchecked")
52-
static StepExecutionReportDto.StepContextDto toDto(Map<String, Object> scenarioContext, Map<String, Object> evaluatedInput, Map<String, Object> stepResults) {
53+
static StepExecutionReportDto.StepContextDto toDto(Map<String, Object> scenarioContext, Map<String, Object> evaluatedInputSnapshot, Map<String, Object> stepResultsSnapshot) {
5354
return new StepExecutionReportDto.StepContextDto(
54-
scenarioContext != null ? scenarioContext : EMPTY_MAP,
55-
evaluatedInput != null ? evaluatedInput : EMPTY_MAP,
56-
stepResults != null ? stepResults : EMPTY_MAP
55+
ofNullable(scenarioContext).orElse(EMPTY_MAP),
56+
ofNullable(evaluatedInputSnapshot).orElse(EMPTY_MAP),
57+
ofNullable(stepResultsSnapshot).orElse(EMPTY_MAP)
5758
);
5859
}
5960

chutney/engine/src/main/java/com/chutneytesting/engine/domain/execution/engine/step/Step.java

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@
2828
import com.chutneytesting.engine.domain.execution.ScenarioExecution;
2929
import com.chutneytesting.engine.domain.execution.StepDefinition;
3030
import com.chutneytesting.engine.domain.execution.engine.StepExecutor;
31-
import com.chutneytesting.engine.domain.execution.engine.evaluation.EvaluationException;
3231
import com.chutneytesting.engine.domain.execution.engine.evaluation.StepDataEvaluator;
3332
import com.chutneytesting.engine.domain.execution.engine.scenario.ScenarioContext;
34-
import com.chutneytesting.engine.domain.execution.engine.scenario.ScenarioContextImpl;
3533
import com.chutneytesting.engine.domain.execution.event.BeginStepExecutionEvent;
3634
import com.chutneytesting.engine.domain.execution.event.EndStepExecutionEvent;
3735
import com.chutneytesting.engine.domain.execution.event.PauseStepExecutionEvent;
@@ -40,12 +38,10 @@
4038
import com.chutneytesting.engine.domain.execution.strategies.StepStrategyDefinition;
4139
import com.chutneytesting.tools.Try;
4240
import com.google.common.collect.Lists;
43-
import com.google.common.collect.Maps;
4441
import java.time.Duration;
4542
import java.time.Instant;
4643
import java.util.Collections;
4744
import java.util.HashMap;
48-
import java.util.LinkedHashMap;
4945
import java.util.List;
5046
import java.util.Map;
5147
import java.util.Optional;
@@ -67,7 +63,6 @@ public class Step {
6763
private Target target;
6864
private final StepExecutor executor;
6965
private final StepDataEvaluator dataEvaluator;
70-
7166
private StepContext stepContext;
7267

7368
public Step(StepDataEvaluator dataEvaluator, StepDefinition definition, StepExecutor executor, List<Step> steps) {
@@ -329,10 +324,6 @@ public void addStepExecution(Step step) {
329324
this.steps.add(step);
330325
}
331326

332-
public void addStepExecution(List<Step> steps) {
333-
steps.forEach(this::addStepExecution);
334-
}
335-
336327
public Map<String, Object> getEvaluatedInputs() {
337328
return unmodifiableMap(this.stepContext.getEvaluatedInputs());
338329
}
@@ -345,77 +336,16 @@ public Map<String, Object> getStepOutputs() {
345336
return unmodifiableMap(this.stepContext.getStepOutputs());
346337
}
347338

348-
public void removeStepExecution() {
349-
this.steps.clear();
339+
public Map<String, Object> getStepContextInputSnapshot() {
340+
return this.stepContext.getStepContextSnapshot().getInputsSnapshot();
350341
}
351342

343+
public Map<String, Object> getStepContextOutputSnapshot() {
344+
return this.stepContext.getStepContextSnapshot().getOutputsSnapshot();
345+
}
352346

353-
private static class StepContext {
354-
355-
private final ScenarioContext scenarioContext;
356-
private final Map<String, Object> localContext;
357-
private final Map<String, Object> evaluatedInputs;
358-
private final Map<String, Object> stepOutputs;
359-
360-
private StepContext() {
361-
this(new ScenarioContextImpl(), new LinkedHashMap<>(), new LinkedHashMap<>());
362-
}
363-
364-
private StepContext(ScenarioContext scenarioContext, Map<String, Object> localContext, Map<String, Object> evaluatedInputs) throws EvaluationException {
365-
this(scenarioContext, localContext, evaluatedInputs, new LinkedHashMap<>());
366-
}
367-
368-
private StepContext(ScenarioContext scenarioContext, Map<String, Object> localContext, Map<String, Object> evaluatedInputs, Map<String, Object> stepOutputs) {
369-
this.scenarioContext = scenarioContext;
370-
this.localContext = localContext;
371-
this.evaluatedInputs = evaluatedInputs;
372-
this.stepOutputs = stepOutputs;
373-
}
374-
375-
private Map<String, Object> evaluationContext() {
376-
final Map<String, Object> allResults = Maps.newLinkedHashMap(scenarioContext);
377-
allResults.putAll(localContext);
378-
allResults.putAll(stepOutputs);
379-
return allResults;
380-
}
381-
382-
private ScenarioContext getScenarioContext() {
383-
return scenarioContext;
384-
}
385-
386-
private Map<String, Object> getEvaluatedInputs() {
387-
return ofNullable(evaluatedInputs).orElse(emptyMap());
388-
}
389-
390-
@SafeVarargs
391-
private void addLocalContext(Map.Entry<String, Object>... entries) {
392-
this.addLocalContext(Map.ofEntries(entries));
393-
}
394-
395-
private void addLocalContext(Map<String, Object> localContext) {
396-
if (localContext != null) {
397-
this.localContext.putAll(localContext);
398-
}
399-
}
400-
401-
private void addStepOutputs(Map<String, Object> stepOutputs) {
402-
if (stepOutputs != null) {
403-
this.stepOutputs.putAll(stepOutputs);
404-
}
405-
}
406-
407-
private void addScenarioContext(Map<String, Object> context) {
408-
if (context != null) {
409-
this.scenarioContext.putAll(context);
410-
}
411-
}
412-
413-
private Map<String, Object> getStepOutputs() {
414-
return ofNullable(stepOutputs).orElse(emptyMap());
415-
}
416-
417-
private StepContext copy() {
418-
return new StepContext(scenarioContext.unmodifiable(), unmodifiableMap(localContext), unmodifiableMap(evaluatedInputs), unmodifiableMap(stepOutputs));
419-
}
347+
public void removeStepExecution() {
348+
this.steps.clear();
420349
}
350+
421351
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2017-2023 Enedis
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+
*/
17+
18+
package com.chutneytesting.engine.domain.execution.engine.step;
19+
20+
import static java.util.Collections.emptyMap;
21+
import static java.util.Collections.unmodifiableMap;
22+
import static java.util.Optional.ofNullable;
23+
24+
import com.chutneytesting.engine.domain.execution.engine.evaluation.EvaluationException;
25+
import com.chutneytesting.engine.domain.execution.engine.scenario.ScenarioContext;
26+
import com.chutneytesting.engine.domain.execution.engine.scenario.ScenarioContextImpl;
27+
import com.google.common.collect.Maps;
28+
import java.util.LinkedHashMap;
29+
import java.util.Map;
30+
31+
class StepContext {
32+
33+
private final ScenarioContext scenarioContext;
34+
private final Map<String, Object> localContext;
35+
private final Map<String, Object> evaluatedInputs;
36+
private final Map<String, Object> stepOutputs;
37+
private StepContextSnapshot stepContextSnapshot;
38+
39+
StepContext() {
40+
this(new ScenarioContextImpl(), new LinkedHashMap<>(), new LinkedHashMap<>());
41+
}
42+
43+
StepContext(ScenarioContext scenarioContext, Map<String, Object> localContext, Map<String, Object> evaluatedInputs) throws EvaluationException {
44+
this(scenarioContext, localContext, evaluatedInputs, new LinkedHashMap<>());
45+
}
46+
47+
private StepContext(ScenarioContext scenarioContext, Map<String, Object> localContext, Map<String, Object> evaluatedInputs, Map<String, Object> stepOutputs) {
48+
this.scenarioContext = scenarioContext;
49+
this.localContext = localContext;
50+
this.evaluatedInputs = evaluatedInputs;
51+
this.stepOutputs = stepOutputs;
52+
this.stepContextSnapshot = new StepContextSnapshot();
53+
}
54+
55+
private StepContext copySnapshotsInputOutput() {
56+
this.stepContextSnapshot = new StepContextSnapshot(evaluatedInputs, stepOutputs);
57+
return this;
58+
}
59+
60+
public StepContextSnapshot getStepContextSnapshot() {
61+
return stepContextSnapshot;
62+
}
63+
64+
Map<String, Object> evaluationContext() {
65+
final Map<String, Object> allResults = Maps.newLinkedHashMap(scenarioContext);
66+
allResults.putAll(localContext);
67+
allResults.putAll(stepOutputs);
68+
return allResults;
69+
}
70+
71+
ScenarioContext getScenarioContext() {
72+
return scenarioContext;
73+
}
74+
75+
Map<String, Object> getEvaluatedInputs() {
76+
return ofNullable(evaluatedInputs).orElse(emptyMap());
77+
}
78+
79+
void addStepOutputs(Map<String, Object> stepOutputs) {
80+
if (stepOutputs != null) {
81+
this.stepOutputs.putAll(stepOutputs);
82+
}
83+
}
84+
85+
void addScenarioContext(Map<String, Object> context) {
86+
if (context != null) {
87+
this.scenarioContext.putAll(context);
88+
}
89+
}
90+
91+
Map<String, Object> getStepOutputs() {
92+
return ofNullable(stepOutputs).orElse(emptyMap());
93+
}
94+
95+
StepContext copy() {
96+
return new StepContext(scenarioContext.unmodifiable(), unmodifiableMap(localContext), unmodifiableMap(evaluatedInputs), unmodifiableMap(stepOutputs)).copySnapshotsInputOutput();
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2017-2023 Enedis
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+
*/
17+
18+
package com.chutneytesting.engine.domain.execution.engine.step;
19+
20+
import static java.util.Collections.emptyMap;
21+
import static java.util.Collections.unmodifiableMap;
22+
23+
import com.chutneytesting.engine.domain.execution.engine.step.jackson.ReportObjectMapperConfiguration;
24+
import com.fasterxml.jackson.core.JsonProcessingException;
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
28+
class StepContextSnapshot {
29+
private final Map<String, Object> inputsSnapshot;
30+
private final Map<String, Object> outputsSnapshot;
31+
32+
public StepContextSnapshot() {
33+
this.inputsSnapshot = emptyMap();
34+
this.outputsSnapshot = emptyMap();
35+
}
36+
37+
public StepContextSnapshot(Map<String, Object> inputsSnapshot, Map<String, Object> outputsSnapshot) {
38+
this.inputsSnapshot = mapStringObjectToString(inputsSnapshot);
39+
this.outputsSnapshot = mapStringObjectToString(outputsSnapshot);
40+
}
41+
42+
public Map<String, Object> getInputsSnapshot() {
43+
return unmodifiableMap(inputsSnapshot);
44+
}
45+
46+
public Map<String, Object> getOutputsSnapshot() {
47+
return unmodifiableMap(outputsSnapshot);
48+
}
49+
50+
private Map<String, Object> mapStringObjectToString(Map<String, Object> originalMap) {
51+
Map<String, Object> stringMap = new HashMap<>();
52+
originalMap.forEach((key, value) -> {
53+
try {
54+
String stringObject = ReportObjectMapperConfiguration.reportObjectMapper().writeValueAsString(value);
55+
Object jsonObject = ReportObjectMapperConfiguration.reportObjectMapper().readTree(stringObject);
56+
stringMap.put(key, jsonObject);
57+
} catch (JsonProcessingException e) {
58+
throw new RuntimeException(e);
59+
}
60+
});
61+
return stringMap;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2017-2023 Enedis
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+
*/
17+
18+
package com.chutneytesting.engine.domain.execution.engine.step.jackson;
19+
20+
import com.fasterxml.jackson.core.JsonGenerator;
21+
import com.fasterxml.jackson.databind.SerializerProvider;
22+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
23+
import java.io.IOException;
24+
import java.io.Serial;
25+
import org.jdom2.Element;
26+
import org.jdom2.output.Format;
27+
import org.jdom2.output.XMLOutputter;
28+
29+
public class JDomElementSerializer extends StdSerializer<Element> {
30+
31+
@Serial
32+
private static final long serialVersionUID = 1L;
33+
34+
public JDomElementSerializer() {
35+
this(null);
36+
}
37+
38+
protected JDomElementSerializer(Class<Element> t) {
39+
super(t);
40+
}
41+
42+
@Override
43+
public void serialize(Element element, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
44+
String xmlString = new XMLOutputter(Format.getCompactFormat()).outputString(element);
45+
jsonGenerator.writeObject(xmlString);
46+
}
47+
}

0 commit comments

Comments
 (0)