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}