1 package de.juplo.httpresources;
2
3
4 import static de.juplo.httpresources.ConfigurableNginxContainer.DEFAULT_NGINX_PORT;
5 import static org.mockserver.model.HttpClassCallback.callback;
6 import static org.mockserver.model.HttpOverrideForwardedRequest.forwardOverriddenRequest;
7 import static org.mockserver.model.HttpRequest.request;
8 import static org.mockserver.model.HttpResponse.response;
9
10 import org.junit.jupiter.api.AfterEach;
11 import org.junit.jupiter.api.BeforeEach;
12 import org.mockserver.integration.ClientAndServer;
13 import org.mockserver.matchers.TimeToLive;
14 import org.mockserver.matchers.Times;
15 import org.mockserver.mock.action.ExpectationForwardAndResponseCallback;
16 import org.mockserver.model.HttpOverrideForwardedRequest;
17 import org.mockserver.model.HttpRequest;
18 import org.mockserver.model.HttpResponse;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.cache.Cache;
23 import org.springframework.cache.concurrent.ConcurrentMapCache;
24 import org.springframework.context.annotation.Bean;
25 import org.springframework.context.annotation.Configuration;
26 import org.springframework.http.HttpStatus;
27 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
28 import org.springframework.util.StringUtils;
29 import org.testcontainers.containers.NginxContainer;
30 import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
31 import org.testcontainers.shaded.org.apache.commons.lang.NotImplementedException;
32
33 import java.time.*;
34 import java.time.format.DateTimeFormatter;
35 import java.util.Locale;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public abstract class IntegrationTestBase
54 {
55 private final static Logger LOG =
56 LoggerFactory.getLogger(IntegrationTestBase.class);
57
58 public static ClockStub CLOCK = new ClockStub();
59 public static DateTimeFormatter formatter =
60 DateTimeFormatter
61 .ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
62 .withZone(ZoneId.of("GMT"));
63
64 public static final NginxContainer NGINX;
65
66 static
67 {
68 NGINX =
69 new ConfigurableNginxContainer()
70 .withConfiguration("src/test/resources/nginx/nginx.conf")
71 .withCustomContent("src/test/resources/remote/");
72 NGINX.waitingFor(new HttpWaitStrategy()).start();
73 }
74
75
76 public static String NGINIX_IP()
77 {
78 return NGINX.getContainerIpAddress();
79 }
80
81 public static int NGINX_PORT()
82 {
83 return NGINX.getMappedPort(DEFAULT_NGINX_PORT);
84 }
85
86 public static String NGINX_URI()
87 {
88 return "http://" + NGINIX_IP() + ":" + NGINX_PORT();
89 }
90
91 public static HttpRequest FETCH(String path)
92 {
93 return request().withPath(path);
94 }
95
96 public static HttpResponse RESPONSE()
97 {
98 CLOCK.tick();
99 HttpResponse response =
100 response().withHeader("Date", formatter.format(CLOCK.instant()));
101 CLOCK.tick();
102 return response;
103 }
104
105 public static HttpOverrideForwardedRequest NGINX()
106 {
107 return NGINX(null);
108 }
109
110 public static HttpOverrideForwardedRequest NGINX(String path)
111 {
112 HttpRequest request = request().withSocketAddress(NGINIX_IP(),NGINX_PORT());
113 if (StringUtils.hasText(path))
114 request = request.withPath(path);
115 return forwardOverriddenRequest(request, RESPONSE());
116 }
117
118 public static HttpResponse NOT_FOUND()
119 {
120 return RESPONSE().withStatusCode(HttpStatus.NOT_FOUND.value());
121 }
122
123 public static HttpResponse INTERNAL_SERVER_ERROR()
124 {
125 return RESPONSE().withStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
126 }
127
128
129 @Configuration
130 public static class IntegrationTestConfiguration
131 {
132 @Bean(destroyMethod = "stop")
133 public ClientAndServer server()
134 {
135 return ClientAndServer.startClientAndServer();
136 }
137
138 @Bean
139 public HttpResourceFetcher fetcher(Cache cache)
140 {
141 return
142 new HttpResourceFetcher(
143 new HttpComponentsClientHttpRequestFactory(),
144 cache,
145 CLOCK);
146 }
147
148 @Bean
149 public Cache cache()
150 {
151 return new ConcurrentMapCache("TEST");
152 }
153
154 @Bean
155 public HttpResources httpResources(HttpResourceFetcher fetcher)
156 {
157 return new HttpResources(fetcher, CLOCK);
158 }
159 }
160
161 @Autowired
162 public ClientAndServer server;
163
164
165
166
167
168
169 @BeforeEach
170 public void prepareMockServer()
171 {
172 CLOCK.now = ZonedDateTime.now();
173 server
174 .when(request(), Times.unlimited(), TimeToLive.unlimited(), Integer.MIN_VALUE)
175 .forward(callback().withCallbackClass(Callback.class));
176 }
177
178 @AfterEach
179 public void clearMockServer()
180 {
181 server.reset();
182 }
183
184
185 public int getMockServerPort()
186 {
187 return server.getLocalPort();
188 }
189
190 public String getMockServerUri()
191 {
192 return "http://localhost:" + getMockServerPort();
193 }
194
195
196 public String address(String path)
197 {
198 return getMockServerUri() + path;
199 }
200
201 public static class Callback implements ExpectationForwardAndResponseCallback
202 {
203 @Override
204 public HttpRequest handle(HttpRequest request) throws Exception
205 {
206 return request.withSocketAddress(NGINIX_IP(),NGINX_PORT());
207 }
208
209 @Override
210 public HttpResponse handle(HttpRequest request, HttpResponse response) throws Exception
211 {
212 CLOCK.tick();
213 HttpResponse modifiedResponse = response.replaceHeader("Date", formatter.format(CLOCK.instant()));
214 CLOCK.tick();
215 return modifiedResponse;
216 }
217 }
218
219 public static class ClockStub extends Clock
220 {
221 public final static Duration DEFAULT_TICK = Duration.ofMillis(100);
222
223 public ZonedDateTime now = ZonedDateTime.now();
224
225
226 public void tick()
227 {
228 now = now.plus(DEFAULT_TICK);
229 }
230
231 public void timetravel(Duration duration)
232 {
233 now = now.plus(duration);
234 }
235
236 @Override
237 public ZoneId getZone()
238 {
239 return now.getZone();
240 }
241
242 @Override
243 public Clock withZone(ZoneId zone)
244 {
245 throw new NotImplementedException();
246 }
247
248 @Override
249 public Instant instant()
250 {
251 return now.toInstant();
252 }
253 };
254 }