1 package com.guinetik.rr.http;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import com.guinetik.rr.api.ApiException;
5 import com.guinetik.rr.request.RequestSpec;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import javax.net.ssl.SSLContext;
10 import java.util.HashMap;
11 import java.util.Map;
12 import java.util.Optional;
13 import java.util.function.BiFunction;
14 import java.util.regex.Pattern;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 public class MockRocketClient implements RocketClient {
74 private static final Logger logger = LoggerFactory.getLogger(MockRocketClient.class);
75
76
77
78
79 private static class MockRule {
80 private final String method;
81 private final Pattern urlPattern;
82 private final BiFunction<String, Object, Object> responseProducer;
83
84 public MockRule(String method, String urlPattern, BiFunction<String, Object, Object> responseProducer) {
85 this.method = method;
86 this.urlPattern = Pattern.compile(urlPattern);
87 this.responseProducer = responseProducer;
88 }
89
90 public boolean matches(String method, String url) {
91 return this.method.equalsIgnoreCase(method) && urlPattern.matcher(url).matches();
92 }
93
94 public Object produceResponse(String url, Object body) {
95 return responseProducer.apply(url, body);
96 }
97 }
98 private final ObjectMapper objectMapper = new ObjectMapper();
99 private final Map<String, String> headers = new HashMap<>();
100 private final Map<String, Integer> invocationCounts = new HashMap<>();
101 private final Map<String, Long> latencies = new HashMap<>();
102 private final Map<String, Integer> statusCodes = new HashMap<>();
103
104 private final java.util.List<MockRule> mockRules = new java.util.ArrayList<>();
105 private SSLContext sslContext;
106
107
108
109
110 public MockRocketClient() {
111 }
112
113
114
115
116
117
118
119
120 public MockRocketClient withHeader(String name, String value) {
121 headers.put(name, value);
122 return this;
123 }
124
125
126
127
128
129
130
131
132 public MockRocketClient withLatency(String urlPattern, long latencyMs) {
133 latencies.put(urlPattern, latencyMs);
134 return this;
135 }
136
137
138
139
140
141
142
143
144 public MockRocketClient withStatusCode(String urlPattern, int statusCode) {
145 statusCodes.put(urlPattern, statusCode);
146 return this;
147 }
148
149
150
151
152
153
154
155
156
157 public MockRocketClient addMockResponse(String method, String urlPattern,
158 BiFunction<String, Object, Object> responseProducer) {
159 mockRules.add(new MockRule(method, urlPattern, responseProducer));
160 return this;
161 }
162
163
164
165
166
167
168
169
170 public int getInvocationCount(String method, String urlPattern) {
171 return invocationCounts.getOrDefault(method + ":" + urlPattern, 0);
172 }
173
174
175
176
177 public void resetCounts() {
178 invocationCounts.clear();
179 }
180
181
182
183
184 private Optional<MockRule> findMatchingRule(String method, String url) {
185 return mockRules.stream()
186 .filter(rule -> rule.matches(method, url))
187 .findFirst();
188 }
189
190 @Override
191 public <Req, Res> Res execute(RequestSpec<Req, Res> requestSpec) throws RocketRestException {
192 String method = requestSpec.getMethod();
193 String url = requestSpec.getEndpoint();
194
195
196 String key = method + ":" + url;
197 invocationCounts.put(key, invocationCounts.getOrDefault(key, 0) + 1);
198
199
200 simulateLatency(url);
201
202
203 Optional<MockRule> matchingRule = findMatchingRule(method, url);
204
205 if (matchingRule.isPresent()) {
206 try {
207 Object response = matchingRule.get().produceResponse(
208 url,
209 requestSpec.getBody()
210 );
211
212
213 if (requestSpec.getResponseType().isInstance(response)) {
214 return (Res) response;
215 } else if (response != null) {
216
217 return objectMapper.convertValue(response, requestSpec.getResponseType());
218 }
219
220 throw new ApiException("Mock response could not be converted to required type: "
221 + requestSpec.getResponseType().getName());
222 } catch (Exception e) {
223 if (e instanceof ApiException) {
224 throw (ApiException) e;
225 }
226 throw new ApiException("Failed to process mock response", e);
227 }
228 }
229
230
231 logger.warn("No mock response found for {} : {}", method, url);
232 throw new ApiException("No mock response configured for " + method + ":" + url);
233 }
234
235
236
237
238 private void simulateLatency(String url) {
239 long latency = latencies.entrySet().stream()
240 .filter(entry -> Pattern.compile(entry.getKey()).matcher(url).matches())
241 .map(Map.Entry::getValue)
242 .findFirst()
243 .orElse(0L);
244
245 if (latency > 0) {
246 try {
247 Thread.sleep(latency);
248 } catch (InterruptedException e) {
249 Thread.currentThread().interrupt();
250 }
251 }
252 }
253
254 @Override
255 public void configureSsl(SSLContext sslContext) {
256 this.sslContext = sslContext;
257 }
258
259 @Override
260 public void setBaseUrl(String baseUrl) {
261
262 }
263 }