Ignore:
Timestamp:
2020-10-11T00:39:35+02:00 (4 years ago)
Author:
Don-vip
Message:

see #15102 - see #16637 - get rid of real HTTP calls to https://httpbin(go).org in unit tests, mock them

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/test/functional/org/openstreetmap/josm/tools/HttpClientTest.java

    r17152 r17154  
    22package org.openstreetmap.josm.tools;
    33
     4import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
     5import static com.github.tomakehurst.wiremock.client.WireMock.binaryEqualTo;
     6import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
     7import static com.github.tomakehurst.wiremock.client.WireMock.get;
     8import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
     9import static com.github.tomakehurst.wiremock.client.WireMock.post;
     10import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
     11import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
    412import static org.hamcrest.CoreMatchers.containsString;
    513import static org.hamcrest.CoreMatchers.is;
     
    1220import java.io.BufferedReader;
    1321import java.io.IOException;
    14 import java.io.InputStream;
     22import java.net.MalformedURLException;
    1523import java.net.URL;
    1624import java.nio.charset.StandardCharsets;
    1725import java.util.Collections;
     26import java.util.Map;
    1827import java.util.logging.Handler;
    1928import java.util.logging.LogRecord;
    2029import java.util.regex.Matcher;
    21 
    22 import javax.json.JsonObject;
    23 import javax.json.JsonReader;
    24 import javax.json.spi.JsonProvider;
     30import java.util.stream.Collectors;
    2531
    2632import org.junit.Assert;
     
    3238import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    3339import org.openstreetmap.josm.testutils.JOSMTestRules;
     40import org.openstreetmap.josm.tools.HttpClient.Response;
     41
     42import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
     43import com.github.tomakehurst.wiremock.http.HttpHeader;
     44import com.github.tomakehurst.wiremock.http.HttpHeaders;
     45import com.github.tomakehurst.wiremock.junit.WireMockRule;
     46import com.github.tomakehurst.wiremock.matching.UrlPattern;
    3447
    3548import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    3649
    3750/**
    38  * Tests the {@link HttpClient} using the webservice <a href="https://httpbin.org/">https://httpbin.org/</a>.
     51 * Tests the {@link HttpClient}.
    3952 */
    4053public class HttpClientTest {
     
    4760    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(15000);
    4861
     62    /**
     63     * mocked local http server
     64     */
     65    @Rule
     66    public WireMockRule localServer = new WireMockRule(WireMockConfiguration.options().dynamicPort());
     67
    4968    private ProgressMonitor progress;
    5069
     
    7190    @Before
    7291    public void setUp() {
     92        localServer.resetAll();
    7393        progress = TestUtils.newTestProgressMonitor();
    7494        captured = null;
     
    83103    @Test
    84104    public void testConstructorGetterSetter() throws IOException {
    85         final HttpClient client = HttpClient.create(new URL("https://httpbin.org/"));
    86         assertThat(client.getURL(), is(new URL("https://httpbin.org/")));
     105        final URL localUrl = url("");
     106        final HttpClient client = HttpClient.create(localUrl);
     107        assertThat(client.getURL(), is(localUrl));
    87108        assertThat(client.getRequestMethod(), is("GET"));
    88109        assertThat(client.getRequestHeader("Accept"), is("*/*"));
     
    104125    @Test
    105126    public void testGet() throws IOException {
    106         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/get?foo=bar")).connect(progress);
     127        final UrlPattern pattern = urlEqualTo("/get?foo=bar");
     128        localServer.stubFor(get(pattern).willReturn(aResponse().withStatusMessage("OK")
     129                .withHeader("Content-Type", "application/json; encoding=utf-8")));
     130        final Response response = connect("/get?foo=bar");
    107131        assertThat(response.getRequestMethod(), is("GET"));
    108132        assertThat(response.getResponseCode(), is(200));
    109133        assertThat(response.getResponseMessage(), equalToIgnoringCase("OK"));
    110         assertThat(response.getContentType(), is("application/json"));
    111         assertThat(response.getHeaderField("Content-Type"), is("application/json"));
    112         assertThat(response.getHeaderField("Content-TYPE"), is("application/json"));
    113         assertThat(response.getHeaderFields().get("Content-Type"), is(Collections.singletonList("application/json")));
    114         assertThat(response.getHeaderFields().get("Content-TYPE"), is(Collections.singletonList("application/json")));
    115         try (InputStream in = response.getContent();
    116              JsonReader json = JsonProvider.provider().createReader(in)) {
    117             final JsonObject root = json.readObject();
    118             assertThat(root.getJsonObject("args").getString("foo"), is("bar"));
    119             assertThat(root.getString("url"), is("https://httpbin.org/get?foo=bar"));
    120             assertThat(root.getJsonObject("headers").get("Cache-Control"), nullValue());
    121             assertThat(root.getJsonObject("headers").get("Pragma"), nullValue());
    122         }
    123     }
    124 
    125     /**
    126      * Test JOSM User-Agent
     134        assertThat(response.getContentType(), is("application/json; encoding=utf-8"));
     135        assertThat(response.getHeaderField("Content-Type"), is("application/json; encoding=utf-8"));
     136        assertThat(response.getHeaderField("Content-TYPE"), is("application/json; encoding=utf-8"));
     137        assertThat(response.getHeaderFields().get("Content-Type"), is(Collections.singletonList("application/json; encoding=utf-8")));
     138        assertThat(response.getHeaderFields().get("Content-TYPE"), is(Collections.singletonList("application/json; encoding=utf-8")));
     139        localServer.verify(getRequestedFor(pattern)
     140                .withQueryParam("foo", equalTo("bar"))
     141                .withoutHeader("Cache-Control")
     142                .withoutHeader("Pragma"));
     143    }
     144
     145    /**
     146     * Test JOSM User-Agent and the incoming request's HTTP headers.
    127147     * @throws IOException if an I/O error occurs
    128148     */
    129149    @Test
    130150    public void testHeaders() throws IOException {
    131         try (InputStream in = HttpClient.create(new URL("https://httpbin.org/headers")).connect(progress).getContent();
    132              JsonReader json = JsonProvider.provider().createReader(in)) {
    133             final JsonObject headers = json.readObject().getJsonObject("headers");
    134             assertThat(headers.getString("Accept"), is("*/*"));
    135             assertThat(headers.getString("Accept-Encoding"), is("gzip, deflate"));
    136             assertThat(headers.getString("User-Agent"), is(Version.getInstance().getFullAgentString()));
    137         }
    138     }
    139 
    140     /**
    141      * Test JOSM User-Agent
    142      * @throws IOException if an I/O error occurs
    143      */
    144     @Test
    145     public void testUserAgent() throws IOException {
    146         try (InputStream in = HttpClient.create(new URL("https://httpbin.org/user-agent")).connect(progress).getContent();
    147              JsonReader json = JsonProvider.provider().createReader(in)) {
    148             assertThat(json.readObject().getString("user-agent"), is(Version.getInstance().getFullAgentString()));
    149         }
     151        final UrlPattern pattern = urlEqualTo("/headers");
     152        localServer.stubFor(get(pattern).willReturn(aResponse()));
     153        connect("/headers");
     154        localServer.verify(getRequestedFor(pattern)
     155                .withHeader("Accept", equalTo("*/*"))
     156                .withHeader("Accept-Encoding", equalTo("gzip, deflate"))
     157                .withHeader("User-Agent", equalTo(Version.getInstance().getFullAgentString())));
    150158    }
    151159
     
    156164    @Test
    157165    public void testFetchUtf8Content() throws IOException {
    158         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/encoding/utf8")).connect(progress);
     166        localServer.stubFor(get(urlEqualTo("/encoding/utf8"))
     167                .willReturn(aResponse().withBody("∀x∈ℝ: UTF-8 encoded sample plain-text file")));
     168        final Response response = connect("/encoding/utf8");
    159169        assertThat(response.getResponseCode(), is(200));
    160170        final String content = response.fetchContent();
     
    164174
    165175    /**
    166      * Test HTTP POST
     176     * Test HTTP POST with non-empty body
    167177     * @throws IOException if an I/O error occurs
    168178     */
    169179    @Test
    170180    public void testPost() throws IOException {
     181        final UrlPattern pattern = urlEqualTo("/post");
     182        localServer.stubFor(post(pattern).willReturn(aResponse()));
    171183        final String text = "Hello World!\nGeetings from JOSM, the Java OpenStreetMap Editor";
    172         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/post"), "POST")
     184        final Response response = HttpClient.create(url("/post"), "POST")
    173185                .setHeader("Content-Type", "text/plain")
    174186                .setRequestBody(text.getBytes(StandardCharsets.UTF_8))
     
    176188                .connect(progress);
    177189        assertThat(response.getResponseCode(), is(200));
    178         try (InputStream in = response.getContent();
    179              JsonReader json = JsonProvider.provider().createReader(in)) {
    180             assertThat(json.readObject().getString("data"), is(text));
    181         }
    182     }
    183 
     190        assertThat(response.getRequestMethod(), is("POST"));
     191        localServer.verify(postRequestedFor(pattern).withRequestBody(equalTo(text)));
     192    }
     193
     194    /**
     195     * Test HTTP POST with empty body
     196     * @throws IOException if an I/O error occurs
     197     */
    184198    @Test
    185199    public void testPostZero() throws IOException {
    186         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/post"), "POST")
     200        final UrlPattern pattern = urlEqualTo("/post");
     201        localServer.stubFor(post(pattern).willReturn(aResponse()));
     202        final byte[] bytes = "".getBytes(StandardCharsets.UTF_8);
     203        final Response response = HttpClient.create(url("/post"), "POST")
    187204                .setHeader("Content-Type", "text/plain")
    188                 .setRequestBody("".getBytes(StandardCharsets.UTF_8))
     205                .setRequestBody(bytes)
    189206                .setFinishOnCloseOutput(false) // to fix #12583, not sure if it's the best way to do it
    190207                .connect(progress);
    191208        assertThat(response.getResponseCode(), is(200));
    192         try (InputStream in = response.getContent();
    193              JsonReader json = JsonProvider.provider().createReader(in)) {
    194             assertThat(json.readObject().getString("data"), is(""));
    195         }
    196     }
    197 
    198     /*@Test
     209        assertThat(response.getRequestMethod(), is("POST"));
     210        localServer.verify(postRequestedFor(pattern).withRequestBody(binaryEqualTo(bytes)));
     211    }
     212
     213    @Test
    199214    public void testRelativeRedirects() throws IOException {
    200         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/relative-redirect/3")).connect(progress);
    201         assertThat(response.getResponseCode(), is(200));
    202         assertThat(response.getContentLength() > 100, is(true));
    203     }*/
    204 
    205     /*@Test
     215        mockRedirects(false, 3);
     216        final Response response = connect("/relative-redirect/3");
     217        assertThat(response.getResponseCode(), is(200));
     218        assertThat(response.getHeaderField("foo"), is("bar"));
     219    }
     220
     221    @Test
    206222    public void testAbsoluteRedirects() throws IOException {
    207         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/absolute-redirect/3")).connect(progress);
    208         assertThat(response.getResponseCode(), is(200));
    209         assertThat(response.getContentLength() > 100, is(true));
    210     }*/
     223        mockRedirects(true, 3);
     224        final Response response = connect("/absolute-redirect/3");
     225        assertThat(response.getResponseCode(), is(200));
     226        assertThat(response.getHeaderField("foo"), is("bar"));
     227    }
    211228
    212229    /**
     
    214231     * @throws IOException if an I/O error occurs
    215232     */
    216     /*@Test(expected = IOException.class)
     233    @Test(expected = IOException.class)
    217234    public void testTooMuchRedirects() throws IOException {
    218         HttpClient.create(new URL("https://httpbin.org/redirect/3")).setMaxRedirects(2).connect(progress);
    219     }*/
     235        mockRedirects(false, 3);
     236        HttpClient.create(url("/relative-redirect/3")).setMaxRedirects(2).connect(progress);
     237    }
    220238
    221239    /**
     
    226244    public void testHttp418() throws IOException {
    227245        // https://tools.ietf.org/html/rfc2324
    228         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/418")).connect(progress);
    229         assertThat(response.getResponseCode(), is(418));
    230         final String content = response.fetchContent();
    231         assertThat(content, containsString("-=[ teapot ]=-"));
    232         assertThat(captured.getMessage(), containsString("-=[ teapot ]=-"));
    233         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     246        final Response response = doTestHttp(418, "I'm a teapot!", "I'm a teapot!",
     247                Collections.singletonMap("X-More-Info", "http://tools.ietf.org/html/rfc2324"));
     248        assertThat(response.getHeaderField("X-More-Info"), is("http://tools.ietf.org/html/rfc2324"));
    234249    }
    235250
     
    241256    public void testHttp401() throws IOException {
    242257        // https://tools.ietf.org/html/rfc2324
    243         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/401")).connect(progress);
    244         assertThat(response.getResponseCode(), is(401));
    245         assertThat(response.getResponseMessage(), equalToIgnoringCase("UNAUTHORIZED"));
    246         final String content = response.fetchContent();
    247         assertThat(content, is(""));
    248         assertThat(captured.getMessage(), containsString("Server did not return any body"));
    249         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     258        doTestHttp(401, "UNAUTHORIZED", null);
    250259    }
    251260
     
    257266    public void testHttp402() throws IOException {
    258267        // https://tools.ietf.org/html/rfc2324
    259         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/402")).connect(progress);
    260         assertThat(response.getResponseCode(), is(402));
    261         assertThat(response.getResponseMessage(), equalToIgnoringCase("PAYMENT REQUIRED"));
    262         final String content = response.fetchContent();
    263         assertThat(content, containsString("Fuck you, pay me!"));
    264         assertThat(captured.getMessage(), containsString("Fuck you, pay me!"));
    265         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     268        doTestHttp(402, "PAYMENT REQUIRED", "Fuck you, pay me!");
    266269    }
    267270
     
    273276    public void testHttp403() throws IOException {
    274277        // https://tools.ietf.org/html/rfc2324
    275         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/403")).connect(progress);
    276         assertThat(response.getResponseCode(), is(403));
    277         assertThat(response.getResponseMessage(), equalToIgnoringCase("FORBIDDEN"));
    278         final String content = response.fetchContent();
    279         assertThat(content, is(""));
    280         assertThat(captured.getMessage(), containsString("Server did not return any body"));
    281         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     278        doTestHttp(403, "FORBIDDEN", null);
    282279    }
    283280
     
    289286    public void testHttp404() throws IOException {
    290287        // https://tools.ietf.org/html/rfc2324
    291         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/404")).connect(progress);
    292         assertThat(response.getResponseCode(), is(404));
    293         assertThat(response.getResponseMessage(), equalToIgnoringCase("NOT FOUND"));
    294         final String content = response.fetchContent();
    295         assertThat(content, is(""));
    296         assertThat(captured.getMessage(), containsString("Server did not return any body"));
    297         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     288        doTestHttp(404, "NOT FOUND", null);
    298289    }
    299290
     
    305296    public void testHttp500() throws IOException {
    306297        // https://tools.ietf.org/html/rfc2324
    307         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/500")).connect(progress);
    308         assertThat(response.getResponseCode(), is(500));
    309         assertThat(response.getResponseMessage(), equalToIgnoringCase("INTERNAL SERVER ERROR"));
    310         final String content = response.fetchContent();
    311         assertThat(content, containsString(""));
    312         assertThat(captured.getMessage(), containsString("Server did not return any body"));
    313         assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     298        doTestHttp(500, "INTERNAL SERVER ERROR", null);
    314299    }
    315300
     
    320305    @Test
    321306    public void testRequestInTime() throws IOException {
    322         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/delay/1")).setReadTimeout(2000).connect(progress);
     307        mockDelay(1);
     308        final Response response = HttpClient.create(url("/delay/1")).setReadTimeout(2000).connect(progress);
    323309        assertThat(response.getResponseCode(), is(200));
    324310    }
     
    330316    @Test(expected = IOException.class)
    331317    public void testTakesTooLong() throws IOException {
    332         HttpClient.create(new URL("https://httpbin.org/delay/1")).setReadTimeout(500).connect(progress);
    333     }
    334 
    335     /**
    336      * Test reading Deflate-encoded data.
    337      * @throws IOException if any I/O error occurs
    338      */
    339     @Test
    340     public void testDeflate() throws IOException {
    341         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/deflate")).connect(progress);
    342         assertThat(response.getResponseCode(), is(200));
    343         try (InputStream in = response.getContent();
    344              JsonReader json = JsonProvider.provider().createReader(in)) {
    345             assertThat(json.readObject().getBoolean("deflated"), is(true));
    346         }
     318        mockDelay(1);
     319        HttpClient.create(url("/delay/1")).setReadTimeout(500).connect(progress);
    347320    }
    348321
     
    353326    @Test
    354327    public void testGzip() throws IOException {
    355         final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/gzip")).connect(progress);
    356         assertThat(response.getResponseCode(), is(200));
    357         try (InputStream in = response.getContent();
    358              JsonReader json = JsonProvider.provider().createReader(in)) {
    359             assertThat(json.readObject().getBoolean("gzipped"), is(true));
    360         }
    361     }
    362 
    363     /**
    364      * Test of {@link HttpClient.Response#uncompress} method with Gzip compression.
     328        localServer.stubFor(get(urlEqualTo("/gzip")).willReturn(aResponse().withBody("foo")));
     329        final Response response = connect("/gzip");
     330        assertThat(response.getResponseCode(), is(200));
     331        assertThat(response.getContentEncoding(), is("gzip"));
     332        assertThat(response.fetchContent(), is("foo"));
     333    }
     334
     335    /**
     336     * Test of {@link Response#uncompress} method with Gzip compression.
    365337     * @throws IOException if any I/O error occurs
    366338     */
     
    374346
    375347    /**
    376      * Test of {@link HttpClient.Response#uncompress} method with Bzip compression.
     348     * Test of {@link Response#uncompress} method with Bzip compression.
    377349     * @throws IOException if any I/O error occurs
    378350     */
     
    386358
    387359    /**
    388      * Test of {@link HttpClient.Response#uncompress} method with Bzip compression.
     360     * Test of {@link Response#uncompress} method with Bzip compression.
    389361     * @throws IOException if any I/O error occurs
    390362     */
     
    420392        assertEquals("La commune demandée n'existe pas ou n'est pas accessible.", m.group(1));
    421393    }
     394
     395    private void mockDelay(int seconds) {
     396        localServer.stubFor(get(urlEqualTo("/delay/" + seconds))
     397                .willReturn(aResponse().withFixedDelay(1000 * seconds)));
     398    }
     399
     400    private void mockRedirects(boolean absolute, int n) {
     401        final String prefix = absolute ? "absolute" : "relative";
     402        for (int i = n; i > 0; i--) {
     403            final String location = "/" + prefix + "-redirect/" + (i-1);
     404            localServer.stubFor(get(urlEqualTo("/" + prefix + "-redirect/" + i))
     405                    .willReturn(aResponse().withStatus(302).withHeader(
     406                            "Location", absolute ? localServer.url(location) : location)));
     407        }
     408        localServer.stubFor(get(urlEqualTo("/" + prefix + "-redirect/0"))
     409                .willReturn(aResponse().withHeader("foo", "bar")));
     410    }
     411
     412    private Response doTestHttp(int responseCode, String message, String body) throws IOException {
     413        return doTestHttp(responseCode, message, body, Collections.emptyMap());
     414    }
     415
     416    private Response doTestHttp(int responseCode, String message, String body, Map<String, String> headersMap) throws IOException {
     417        localServer.stubFor(get(urlEqualTo("/status/" + responseCode))
     418                .willReturn(aResponse().withStatus(responseCode).withStatusMessage(message).withBody(body).withHeaders(
     419                        new HttpHeaders(headersMap.entrySet().stream().map(
     420                                e -> new HttpHeader(e.getKey(), e.getValue())).collect(Collectors.toList())))));
     421        Response response = connect("/status/" + responseCode);
     422        assertThat(response.getResponseCode(), is(responseCode));
     423        assertThat(response.getResponseMessage(), equalToIgnoringCase(message));
     424        final String content = response.fetchContent();
     425        assertThat(content, is(body == null ? "" : body));
     426        assertThat(captured.getMessage(), containsString(body == null ? "Server did not return any body" : body));
     427        assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG));
     428        return response;
     429    }
     430
     431    private Response connect(String path) throws IOException {
     432        return HttpClient.create(url(path)).connect(progress);
     433    }
     434
     435    private URL url(String path) throws MalformedURLException {
     436        return new URL(localServer.url(path));
     437    }
    422438}
Note: See TracChangeset for help on using the changeset viewer.