001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2016 ForgeRock AS.
015 */
016
017package org.forgerock.http.handler;
018
019import static com.xebialabs.restito.builder.stub.StubHttp.whenHttp;
020import static com.xebialabs.restito.semantics.Action.composite;
021import static com.xebialabs.restito.semantics.Action.ok;
022import static com.xebialabs.restito.semantics.Action.status;
023import static com.xebialabs.restito.semantics.Action.stringContent;
024import static com.xebialabs.restito.semantics.Condition.not;
025import static com.xebialabs.restito.semantics.Condition.post;
026import static com.xebialabs.restito.semantics.Condition.withPostBody;
027import static com.xebialabs.restito.semantics.Condition.withPostBodyContaining;
028import static java.lang.String.format;
029import static org.assertj.core.api.Assertions.assertThat;
030
031import org.forgerock.http.HttpApplicationException;
032import org.forgerock.http.protocol.Request;
033import org.forgerock.http.protocol.Response;
034import org.forgerock.http.protocol.Status;
035import org.forgerock.services.context.RootContext;
036import org.glassfish.grizzly.http.util.HttpStatus;
037import org.testng.annotations.AfterTest;
038import org.testng.annotations.BeforeMethod;
039import org.testng.annotations.BeforeTest;
040import org.testng.annotations.Test;
041
042import com.xebialabs.restito.server.StubServer;
043
044/**
045 * This is the base class to ensure common behaviour between CHF client implementations.
046 */
047public abstract class HttpClientHandlerTest {
048
049    /** The HTTP server to query. */
050    protected StubServer server;
051
052    /**
053     * Setup the HTTP server.
054     */
055    @BeforeTest
056    public void setUp() {
057        // Create mock HTTP server.
058        server = new StubServer().run();
059    }
060
061    /**
062     * Stop the HTTP server.
063     */
064    @AfterTest
065    public void tearDown() {
066        server.stop();
067    }
068
069    /**
070     * Reset the HTTP server.
071     */
072    @BeforeMethod
073    public void cleanup() {
074        // Clear mocked invocations between tests
075        // So we can reuse the server instance (less traces) still having isolation
076        if (server != null) {
077            server.clear();
078        }
079    }
080
081    /**
082     * Ensure that a response is produced.
083     * @throws Exception In case of failure.
084     */
085    @Test
086    public void shouldProduceResponse() throws Exception {
087        whenHttp(server).match(post("/ping"))
088                .then(composite(ok(), stringContent("Pong")));
089
090        try (HttpClientHandler handler = buildHttpClientHandler()) {
091            Request request = new Request();
092            request.setMethod("POST");
093            request.setUri(format("http://localhost:%d/ping", server.getPort()));
094            Response response = handler.handle(new RootContext(), request).get();
095            assertThat(response.getStatus()).isEqualTo(Status.OK);
096            assertThat(response.getEntity().getString()).isEqualTo("Pong");
097        }
098    }
099
100    /**
101     * Ensure that a request with a posted entity can be sent.
102     * @throws Exception In case of failure.
103     */
104    @Test
105    public void shouldSendPostHttpMessageWithEntityContent() throws Exception {
106        whenHttp(server).match(post("/test"),
107                withPostBodyContaining("Hello"))
108                .then(status(HttpStatus.OK_200));
109
110        try (HttpClientHandler handler = buildHttpClientHandler()) {
111            Request request = new Request();
112            request.setMethod("POST");
113            request.setUri(format("http://localhost:%d/test", server.getPort()));
114            request.getEntity().setString("Hello");
115            assertThat(handler.handle(new RootContext(), request).get().getStatus()).isEqualTo(Status.OK);
116        }
117    }
118
119    /**
120     * Ensure that a request with a posted empty entity can be sent.
121     * @throws Exception In case of failure.
122     */
123    @Test
124    public void shouldSendPostHttpMessageWithEmptyEntity() throws Exception {
125        whenHttp(server).match(post("/test"),
126                not(withPostBody()))
127                .then(status(HttpStatus.OK_200));
128
129        try (HttpClientHandler handler = buildHttpClientHandler()) {
130            Request request = new Request();
131            request.setMethod("POST");
132            request.setUri(format("http://localhost:%d/test", server.getPort()));
133            assertThat(handler.handle(new RootContext(), request).get().getStatus()).isEqualTo(Status.OK);
134        }
135    }
136
137    /**
138     * Ensure that a response with status BAD_GATEWAY is received when an error occurred.
139     * @throws Exception In case of failure.
140     */
141    @Test
142    public void shouldFailToObtainResponse() throws Exception {
143        // The request is invalid because we did not specify the method.
144        final Request invalidRequest = new Request();
145        invalidRequest.setUri(format("http://localhost:%d/shouldFail", server.getPort()));
146
147        try (HttpClientHandler handler = buildHttpClientHandler()) {
148            final Response response = handler.handle(new RootContext(), invalidRequest).get();
149
150            assertThat(response.getStatus()).isEqualTo(Status.BAD_GATEWAY);
151            assertThat(response.getEntity().getString()).isEmpty();
152            assertThat(response.getCause()).isNotNull();
153        }
154    }
155
156    /**
157     *  Instantiates the expected HttpClientHandler to test.
158     *  @return the expected HttpClientHandler to test.
159     *  @throws HttpApplicationException In case of failure when trying to create it.
160     */
161    protected abstract HttpClientHandler buildHttpClientHandler() throws HttpApplicationException;
162
163}