Skip to content

Commit 1588559

Browse files
committed
SnapshotStubMappingPostProcessor correctly handle scenarios with transformed stubs
Revises SnapshotStubMappingPostProcessor to run stub transformers first. That way, if the transformer results in StubMapping requests being equal they are properly deduplicated or turned into scenarios. Cleans up process method to make clearer the three phases of processing. Additionally: - Converts SnapshotStubMappingPostProcessorTest.TEST_STUB_MAPPINGS to instance variable to avoid sharing mutable state between test cases which caused the suite to be order dependent. Fixes: wiremock#2137
1 parent 0668338 commit 1588559

File tree

2 files changed

+74
-26
lines changed

2 files changed

+74
-26
lines changed

src/main/java/com/github/tomakehurst/wiremock/recording/SnapshotStubMappingPostProcessor.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818
import com.github.tomakehurst.wiremock.matching.RequestPattern;
1919
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
2020
import com.google.common.collect.HashMultiset;
21-
import com.google.common.collect.Lists;
2221
import com.google.common.collect.Multiset;
2322
import java.util.ArrayList;
2423
import java.util.List;
2524

2625
/**
27-
* Performs stateful post-processing tasks on stub mappings generated from ServeEvents: 1. Detect
28-
* duplicate requests and either discard them or turn them into scenarios 2. Extract response bodies
29-
* to a separate file, if applicable 3. Run any applicable StubMappingTransformers against the stub
30-
* mappings
26+
* Performs stateful post-processing tasks on stub mappings generated from ServeEvents:
27+
* <ol>
28+
* <li>Run any applicable StubMappingTransformers against the stub mappings.</li>
29+
* <li>Detect duplicate requests and either discard them or turn them into scenarios.</li>
30+
* <li>Extract response bodies to a separate file, if applicable.</li>
31+
* </ol>
3132
*/
3233
public class SnapshotStubMappingPostProcessor {
3334
private final boolean shouldRecordRepeatsAsScenarios;
@@ -47,30 +48,45 @@ public SnapshotStubMappingPostProcessor(
4748
}
4849

4950
public List<StubMapping> process(Iterable<StubMapping> stubMappings) {
50-
final Multiset<RequestPattern> requestCounts = HashMultiset.create();
51-
final List<StubMapping> processedStubMappings = new ArrayList<>();
52-
51+
// 1. Run any applicable StubMappingTransformers against the stub mappings.
52+
ArrayList<StubMapping> transformedStubMappings = new ArrayList<>();
5353
for (StubMapping stubMapping : stubMappings) {
54-
requestCounts.add(stubMapping.getRequest());
54+
transformedStubMappings.add(transformerRunner.apply(stubMapping));
55+
}
56+
57+
// 2. Detect duplicate requests and either discard them or turn them into scenarios.
58+
Multiset<RequestPattern> requestCounts = HashMultiset.create();
59+
List<StubMapping> processedStubMappings = new ArrayList<>();
60+
for (StubMapping transformedStubMapping : transformedStubMappings) {
61+
requestCounts.add(transformedStubMapping.getRequest());
5562

5663
// Skip duplicate requests if shouldRecordRepeatsAsScenarios is not enabled
57-
if (requestCounts.count(stubMapping.getRequest()) > 1 && !shouldRecordRepeatsAsScenarios) {
64+
if (requestCounts.count(transformedStubMapping.getRequest()) > 1 && !shouldRecordRepeatsAsScenarios) {
5865
continue;
5966
}
6067

61-
if (bodyExtractMatcher != null
62-
&& bodyExtractMatcher.match(stubMapping.getResponse()).isExactMatch()) {
63-
bodyExtractor.extractInPlace(stubMapping);
64-
}
65-
66-
processedStubMappings.add(stubMapping);
68+
processedStubMappings.add(transformedStubMapping);
6769
}
6870

6971
if (shouldRecordRepeatsAsScenarios) {
7072
new ScenarioProcessor().putRepeatedRequestsInScenarios(processedStubMappings);
7173
}
7274

73-
// Run any stub mapping transformer extensions
74-
return Lists.transform(processedStubMappings, transformerRunner);
75+
// 3. Extract response bodies to a separate file, if applicable.
76+
extractStubMappingBodies(processedStubMappings);
77+
78+
return processedStubMappings;
79+
}
80+
81+
private void extractStubMappingBodies(List<StubMapping> stubMappings) {
82+
if (bodyExtractMatcher == null) {
83+
return;
84+
}
85+
86+
for (StubMapping stubMapping : stubMappings) {
87+
if (bodyExtractMatcher.match(stubMapping.getResponse()).isExactMatch()) {
88+
bodyExtractor.extractInPlace(stubMapping);
89+
}
90+
}
7591
}
7692
}

src/test/java/com/github/tomakehurst/wiremock/recording/SnapshotStubMappingPostProcessorTest.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,38 @@
1818
import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;
1919
import static org.hamcrest.MatcherAssert.assertThat;
2020
import static org.hamcrest.Matchers.*;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
import static org.junit.jupiter.api.Assertions.assertFalse;
2123

2224
import com.github.tomakehurst.wiremock.client.WireMock;
2325
import com.github.tomakehurst.wiremock.http.ResponseDefinition;
2426
import com.github.tomakehurst.wiremock.matching.MatchResult;
2527
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
2628
import com.google.common.collect.ImmutableList;
2729
import java.util.List;
30+
2831
import org.junit.jupiter.api.Test;
2932

3033
public class SnapshotStubMappingPostProcessorTest {
31-
private static final List<StubMapping> TEST_STUB_MAPPINGS =
34+
// NOTE: testStubMappings is not deeply immutable, as StubMappings are mutable, and to preserve hermeticity must be
35+
// an instance rather than a class variable.
36+
private final List<StubMapping> testStubMappings =
3237
ImmutableList.of(
3338
WireMock.get("/foo").build(), WireMock.get("/bar").build(), WireMock.get("/foo").build());
3439

3540
@Test
36-
public void processFiltersRepeatedRequestsWhenNotRecordingScenarios() {
41+
public void process_notRecordingScenarios_filtersRepeatedRequests() {
3742
final List<StubMapping> actual =
3843
new SnapshotStubMappingPostProcessor(false, noopTransformerRunner(), null, null)
39-
.process(TEST_STUB_MAPPINGS);
44+
.process(testStubMappings);
4045

4146
assertThat(actual, hasSize(2));
4247
assertThat(actual.get(0).getRequest().getUrl(), equalTo("/foo"));
4348
assertThat(actual.get(1).getRequest().getUrl(), equalTo("/bar"));
4449
}
4550

4651
@Test
47-
public void processRunsTransformers() {
52+
public void process_withTransformer() {
4853
SnapshotStubMappingTransformerRunner transformerRunner =
4954
new SnapshotStubMappingTransformerRunner(null) {
5055
@Override
@@ -58,21 +63,48 @@ public StubMapping apply(StubMapping stubMapping) {
5863

5964
final List<StubMapping> actual =
6065
new SnapshotStubMappingPostProcessor(false, transformerRunner, null, null)
61-
.process(TEST_STUB_MAPPINGS);
66+
.process(testStubMappings);
6267

6368
assertThat(actual, hasSize(2));
6469
assertThat(actual.get(0).getRequest().getUrl(), equalTo("/foo/transformed"));
6570
assertThat(actual.get(1).getRequest().getUrl(), equalTo("/bar/transformed"));
6671
}
6772

73+
@Test
74+
public void process_withShouldRecordRepeatsAsScenariosAndTransformer_runsTransformerBeforeScenarioProcessor() {
75+
SnapshotStubMappingTransformerRunner transformerRunner =
76+
new SnapshotStubMappingTransformerRunner(null) {
77+
@Override
78+
public StubMapping apply(StubMapping stubMapping) {
79+
// Return StubMapping with "/transformed" at the end of the original URL
80+
String url = stubMapping.getRequest().getUrl();
81+
return new StubMapping(
82+
newRequestPattern().withUrl(url + "/transformed").build(), ResponseDefinition.ok());
83+
}
84+
};
85+
86+
final List<StubMapping> actual =
87+
new SnapshotStubMappingPostProcessor(true, transformerRunner, null, null)
88+
.process(testStubMappings);
89+
90+
assertThat(actual, hasSize(3));
91+
assertThat(actual.get(0).getRequest().getUrl(), equalTo("/foo/transformed"));
92+
assertThat(actual.get(1).getRequest().getUrl(), equalTo("/bar/transformed"));
93+
assertThat(actual.get(2).getRequest().getUrl(), equalTo("/foo/transformed"));
94+
95+
assertTrue(actual.get(0).isInScenario());
96+
assertFalse(actual.get(1).isInScenario());
97+
assertTrue(actual.get(2).isInScenario());
98+
}
99+
68100
@Test
69-
public void processExtractsBodiesWhenMatched() {
101+
public void process_withBodyExtractMatcherAndBodyExtractor_extractsBodiesWhenMatched() {
70102
final ResponseDefinitionBodyMatcher bodyMatcher =
71103
new ResponseDefinitionBodyMatcher(0, 0) {
72104
@Override
73105
public MatchResult match(ResponseDefinition responseDefinition) {
74106
// Only match the second stub mapping
75-
return responseDefinition == TEST_STUB_MAPPINGS.get(1).getResponse()
107+
return responseDefinition == testStubMappings.get(1).getResponse()
76108
? MatchResult.exactMatch()
77109
: MatchResult.noMatch();
78110
}
@@ -89,7 +121,7 @@ public void extractInPlace(StubMapping stubMapping) {
89121
final List<StubMapping> actual =
90122
new SnapshotStubMappingPostProcessor(
91123
false, noopTransformerRunner(), bodyMatcher, bodyExtractor)
92-
.process(TEST_STUB_MAPPINGS);
124+
.process(testStubMappings);
93125

94126
assertThat(actual, hasSize(2));
95127
// Should've only modified second stub mapping

0 commit comments

Comments
 (0)