package org.jboss.seam.mock; import org.jboss.seam.servlet.SeamResourceServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Cookie; import javax.servlet.ServletException; import javax.servlet.ServletResponse; import javax.servlet.ServletRequest; import javax.servlet.FilterChain; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import java.util.Collections; import java.util.Set; import java.util.List; import java.util.Map; import java.util.Enumeration; import java.security.Principal; import java.io.IOException; /** * Executes (through local calls, not TCP sockets) an HTTP request in a unit test, passing it through * the Seam resource handlers and filters. * * <p> * This class is supposed to be used <b>within</b> a <tt>SeamTest</tt>, in fact, you need * to pass an instance of <tt>SeamTest</tt> into its constructor. This prepares the environment * for the resource request processing. You can either share an instance of the environment between * all your test methods (prepare it in <tt>@BeforeClass</tt>) or you can create a new instance * for each <tt>ResourceRequest</tt>: * </p> * * <pre> * import org.jboss.seam.mock.ResourceRequestEnvironment; * import org.jboss.seam.mock.EnhancedMockHttpServletRequest; * import org.jboss.seam.mock.EnhancedMockHttpServletResponse; * import static org.jboss.seam.mock.ResourceRequestEnvironment.ResourceRequest; * import static org.jboss.seam.mock.ResourceRequestEnvironment.Method; * * public class MyTest extends SeamTest { * * ResourceRequestEnvironment sharedEnvironment; * * @BeforeClass * public void prepareSharedEnvironment() throws Exception { * sharedEnvironment = new ResourceRequestEnvironment(this) { * @Override * public Map<String, Object> getDefaultHeaders() { * return new HashMap<String, Object>() {{ * put("Accept", "text/plain"); * }}; * } * }; * } * * @Test * public void test() throws Exception * { * //Not shared: new ResourceRequest(new ResourceRequestEnvironment(this), Method.GET, "/my/relative/uri) * * new ResourceRequest(sharedEnvironment, Method.GET, "/my/relative/uri) * { * * @Override * protected void prepareRequest(EnhancedMockHttpServletRequest request) * { * request.addQueryParameter("foo", "123"); * request.addHeader("Accept-Language", "en_US, de"); * } * * @Override * protected void onResponse(EnhancedMockHttpServletResponse response) * { * assert response.getStatus() == 200; * assert response.getContentAsString().equals("foobar"); * } * * }.run(); * } * * } * </pre> * <p> * Note that in a <tt>SeamTest</tt> the (mock) HTTP session is always shared between all requests in a particular test * method. Each test method however executes with a new (mock) HTTP session. Design your tests accordingly, this is not * configurable. * </p> * <p> * <b>IMPORTANT: A <tt>ResourceRequest</tt> has to be executed in a <tt>@Test</tt> method or in a * <tt>@BeforeMethod</tt> callback. You can not execute it in any other callback, such * as <tt>@BeforeClass</tt>. * </p> * * @author Christian Bauer */ public class ResourceRequestEnvironment { public enum Method { GET, PUT, POST, DELETE, HEAD, OPTIONS } final protected AbstractSeamTest seamTest; final protected SeamResourceServlet resourceServlet; public ResourceRequestEnvironment(AbstractSeamTest seamTest) { this.seamTest = seamTest; resourceServlet = new SeamResourceServlet(); try { resourceServlet.init( new ServletConfig() { public String getServletName() { return "Seam Resource Servlet"; } public ServletContext getServletContext() { return ResourceRequestEnvironment.this.seamTest.servletContext; } public String getInitParameter(String s) { return null; } public Enumeration getInitParameterNames() { return null; } } ); } catch (Exception ex) { throw new RuntimeException(ex); } } public static class ResourceRequest { final private ResourceRequestEnvironment environment; private Method httpMethod; private String requestPath; private EnhancedMockHttpServletRequest request; private EnhancedMockHttpServletResponse response; public ResourceRequest(ResourceRequestEnvironment environment, Method httpMethod, String requestPath) { this.environment = environment; this.httpMethod = httpMethod; this.requestPath = environment.getServletPath() + (requestPath.startsWith("/") ? requestPath : "/" + requestPath); } public void run() throws Exception { init(); prepareRequest(request); environment.seamTest.seamFilter.doFilter(request, response, new FilterChain() { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { environment.resourceServlet.service(request, response); } }); environment.seamTest.seamFilter.destroy(); onResponse(getResponse()); } protected void init() { request = createRequest(); response = createResponse(); request.setMethod(httpMethod.toString()); request.setRequestURI(requestPath); request.setServletPath(environment.getServletPath()); request.setCookies(getCookies().toArray(new Cookie[getCookies().size()])); for (Map.Entry<String, Object> entry : environment.getDefaultHeaders().entrySet()) { request.addHeader(entry.getKey(), entry.getValue()); } request.setUserPrincipal( new Principal() { public String getName() { return getPrincipalName(); } } ); for (String role : getPrincipalRoles()) { request.addUserRole(role); } // Use the (mock) HttpSession that Seam uses, see AbstractSeamTest request.setSession(environment.seamTest.session); } protected EnhancedMockHttpServletRequest createRequest() { return new EnhancedMockHttpServletRequest(); } protected EnhancedMockHttpServletResponse createResponse() { return new EnhancedMockHttpServletResponse(); } protected Map<String, String> getRequestQueryParameters() { return Collections.EMPTY_MAP; } protected List<Cookie> getCookies() { return Collections.EMPTY_LIST; } protected String getPrincipalName() { return null; } protected Set<String> getPrincipalRoles() { return Collections.EMPTY_SET; } protected void prepareRequest(EnhancedMockHttpServletRequest request) { } protected void onResponse(EnhancedMockHttpServletResponse response) { } public HttpServletRequest getRequest() { return request; } public EnhancedMockHttpServletResponse getResponse() { return response; } } public String getServletPath() { return "/seam/resource"; } public Map<String, Object> getDefaultHeaders() { return Collections.EMPTY_MAP; } }