Skip to content

Commit 0f044e8

Browse files
brian-muliermimfgg
andauthored
feat: prioritize back the expectations when modified (#54)
* wip: mock priority rework * wip: mock priority rework * re-add the expectations to the httpState * feat: expectations are now prioritized back when modified Co-authored-by: Jeremy Comte <[email protected]>
1 parent 3d8018f commit 0f044e8

File tree

2 files changed

+65
-45
lines changed
  • mockfaster/src/main/java/com/decathlon/tzatziki/utils
  • tzatziki-http/src/test/resources/com/decathlon/tzatziki/steps

2 files changed

+65
-45
lines changed

mockfaster/src/main/java/com/decathlon/tzatziki/utils/MockFaster.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515
import org.mockserver.integration.ClientAndServer;
1616
import org.mockserver.matchers.TimeToLive;
1717
import org.mockserver.matchers.Times;
18+
import org.mockserver.mock.Expectation;
1819
import org.mockserver.mock.HttpState;
1920
import org.mockserver.mock.action.ExpectationResponseCallback;
21+
import org.mockserver.mock.listeners.MockServerMatcherNotifier;
2022
import org.mockserver.model.*;
2123
import org.mockserver.netty.MockServerUnificationInitializer;
2224
import org.mockserver.verify.VerificationTimes;
2325

2426
import java.util.*;
2527
import java.util.concurrent.CompletableFuture;
28+
import java.util.concurrent.atomic.AtomicBoolean;
2629
import java.util.concurrent.atomic.AtomicReference;
2730
import java.util.regex.Matcher;
2831
import java.util.regex.Pattern;
@@ -50,7 +53,7 @@ protected Integer initialize() throws ConcurrentException {
5053
return CLIENT_AND_SERVER.getLocalPort();
5154
}
5255
};
53-
private static final Map<String, UpdatableExpectationResponseCallback> MOCKS = new LinkedHashMap<>();
56+
private static final Map<String, Pair<Expectation[], UpdatableExpectationResponseCallback>> MOCKS = new LinkedHashMap<>();
5457
private static final ConcurrentInitializer<HttpState> HTTP_STATE = new LazyInitializer<HttpState>() {
5558

5659
@Override
@@ -62,24 +65,39 @@ protected HttpState initialize() throws ConcurrentException {
6265
}
6366
};
6467
private static final Set<String> MOCKED_PATHS = new LinkedHashSet<>();
68+
private static int latestPriority = 0;
6569

6670
public static synchronized void add_mock(HttpRequest httpRequest, ExpectationResponseCallback callback) {
6771
HttpState httpState = unchecked(HTTP_STATE::get);
6872
httpState.getRequestMatchers().retrieveActiveExpectations(httpRequest)
6973
// we are redefining an old mock with a matches, let's see if we have old callbacks still in 404
7074
.stream()
71-
.filter(expectation -> MOCKS.get(expectation.getHttpRequest().toString()).callback.equals(NOT_FOUND))
75+
.filter(expectation -> MOCKS.get(expectation.getHttpRequest().toString()).getValue().callback.equals(NOT_FOUND))
7276
.forEach(expectation -> {
7377
httpState.getRequestMatchers().clear(expectation.getHttpRequest());
7478
MOCKS.remove(expectation.getHttpRequest().toString());
7579
log.debug("removing expectation {}", expectation.getHttpRequest());
7680
});
7781

78-
MOCKS.computeIfAbsent(httpRequest.toString(), k -> {
82+
AtomicBoolean isNew = new AtomicBoolean(false);
83+
latestPriority++;
84+
final Pair<Expectation[], UpdatableExpectationResponseCallback> expectationIdsWithCallback = MOCKS.computeIfAbsent(httpRequest.toString(), k -> {
85+
isNew.set(true);
7986
UpdatableExpectationResponseCallback updatableCallback = new UpdatableExpectationResponseCallback();
80-
CLIENT_AND_SERVER.when(httpRequest, Times.unlimited(), TimeToLive.unlimited(), MOCKS.size()).respond(updatableCallback);
81-
return updatableCallback;
82-
}).set(callback);
87+
final Expectation[] expectations = CLIENT_AND_SERVER.when(httpRequest, Times.unlimited(), TimeToLive.unlimited(), latestPriority).respond(updatableCallback);
88+
return Pair.of(expectations, updatableCallback);
89+
});
90+
expectationIdsWithCallback.getValue().set(callback);
91+
92+
if (!isNew.get()) {
93+
94+
Arrays.stream(expectationIdsWithCallback.getKey())
95+
// update the priority of the expectation
96+
.map(expectation -> expectation.withPriority(latestPriority))
97+
// re-add the expectations, this will resort the CircularPriorityQueue
98+
.forEach(expectation -> httpState.getRequestMatchers().add(expectation, MockServerMatcherNotifier.Cause.API));
99+
}
100+
83101
PATH_PATTERNS.add(Pattern.compile(httpRequest.getPath().getValue()));
84102
}
85103

@@ -269,7 +287,7 @@ public static ClientAndServer clientAndServer() {
269287
}
270288

271289
public static void reset() {
272-
MOCKS.values().forEach(callback -> callback.set(NOT_FOUND));
290+
MOCKS.values().forEach(expectationIdsWithUpdatableCallback -> expectationIdsWithUpdatableCallback.getValue().set(NOT_FOUND));
273291
unchecked(HTTP_STATE::get).getMockServerLog().reset();
274292
MOCKED_PATHS.clear();
275293
PATH_PATTERNS.clear();

tzatziki-http/src/test/resources/com/decathlon/tzatziki/steps/http.feature

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -391,43 +391,6 @@ Feature: to interact with an http service and setup mocks
391391
message: something
392392
"""
393393

394-
Scenario: expectations with and without body redefined in the same scenario
395-
Given that "http://backend/test" is mocked as:
396-
"""yml
397-
request:
398-
method: POST
399-
response:
400-
status: NOT_ACCEPTABLE
401-
"""
402-
And that "http://backend/test" is mocked as:
403-
"""yml
404-
request:
405-
method: POST
406-
body:
407-
payload: plop
408-
response:
409-
status: OK
410-
"""
411-
412-
When we post on "http://backend/test"
413-
Then we receive a status NOT_ACCEPTABLE
414-
415-
But if we post on "http://backend/test" a String "plop"
416-
Then we receive a status OK
417-
418-
And if "http://backend/test" is mocked as:
419-
"""yml
420-
request:
421-
method: POST
422-
response:
423-
status: ACCEPTED
424-
"""
425-
When we post on "http://backend/test"
426-
Then we receive a status ACCEPTED
427-
428-
But if we post on "http://backend/test" a String "plop"
429-
Then we still receive a status OK
430-
431394
Scenario: overriding expectations from a previous scenario
432395
Given that "http://backend/test" is mocked as:
433396
"""yml
@@ -1069,4 +1032,43 @@ Feature: to interact with an http service and setup mocks
10691032
Examples:
10701033
| idx |
10711034
| 1 |
1072-
| 2 |
1035+
| 2 |
1036+
1037+
Scenario: if we override an existing mock response, it should take back the priority over any in-between mocks
1038+
Given that posting on "http://services/perform" will return a status FORBIDDEN_403
1039+
Given that "http://services/perform" is mocked as:
1040+
"""yaml
1041+
request:
1042+
method: POST
1043+
headers:
1044+
Content-Type: application/json
1045+
body:
1046+
payload:
1047+
service_id: 1
1048+
response:
1049+
status: INTERNAL_SERVER_ERROR_500
1050+
headers:
1051+
Content-Type: application/json
1052+
body:
1053+
payload:
1054+
message: 'Error while performing service'
1055+
"""
1056+
Given that posting on "http://services/perform" will return a status BAD_REQUEST_400
1057+
Given that "http://services/perform" is mocked as:
1058+
"""yaml
1059+
request:
1060+
method: POST
1061+
headers:
1062+
Content-Type: application/json
1063+
body:
1064+
payload:
1065+
service_id: 1
1066+
response:
1067+
status: OK_200
1068+
"""
1069+
When we post on "http://services/perform" a Map:
1070+
"""yml
1071+
service_id: 1
1072+
"""
1073+
1074+
Then we received a status OK_200

0 commit comments

Comments
 (0)