/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.test.mock; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.xmlmatchers.XmlMatchers.isEquivalentTo; import static org.xmlmatchers.transform.XmlConverters.the; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; public class MockRequestMatcher { private static final Charset UTF8 = Charset.forName( "UTF-8" ); private String from; private MockResponseProvider response; private Set<String> methods = null; private String pathInfo = null; private String requestURL = null; Map<String,Matcher> headers = null; Set<Cookie> cookies = null; private Map<String,Object> attributes = null; private Map<String,String> queryParams = null; private String contentType = null; private String characterEncoding = null; private Integer contentLength = null; private byte[] entity = null; private Map<String,String[]> formParams = null; public MockRequestMatcher( MockResponseProvider response ) { this.response = response; } public MockResponseProvider respond() { return response; } public MockRequestMatcher from( String from ) { this.from = from; return this; } public MockRequestMatcher method( String... methods ) { if( this.methods == null ) { this.methods = new HashSet<String>(); } if( methods != null ) { for( String method: methods ) { this.methods.add( method ); } } return this; } public MockRequestMatcher pathInfo( String pathInfo ) { this.pathInfo = pathInfo; return this; } public MockRequestMatcher requestUrl( String requestUrl ) { this.requestURL = requestUrl; return this; } public MockRequestMatcher header( String name, String value ) { if( headers == null ) { headers = new HashMap<String, Matcher>(); } headers.put( name, Matchers.is(value) ); return this; } public MockRequestMatcher header( String name, Matcher matcher ) { if( headers == null ) { headers = new HashMap<String, Matcher>(); } headers.put( name, matcher ); return this; } public MockRequestMatcher cookie( Cookie cookie ) { if( cookies == null ) { cookies = new HashSet<Cookie>(); } cookies.add( cookie ); return this; } public MockRequestMatcher attribute( String name, Object value ) { if( this.attributes == null ) { this.attributes = new HashMap<String,Object>(); } attributes.put( name, value ); return this; } public MockRequestMatcher queryParam( String name, String value ) { if( this.queryParams == null ) { this.queryParams = new HashMap<String, String>(); } queryParams.put( name, value ); return this; } public MockRequestMatcher formParam( String name, String... values ) { if( entity != null ) { throw new IllegalStateException( "Entity already specified." ); } if( formParams == null ) { formParams = new HashMap<String, String[]>(); } String[] currentValues = formParams.get( name ); if( currentValues == null ) { currentValues = values; } else if ( values != null ) { currentValues = ArrayUtils.addAll( currentValues, values ); } formParams.put( name, currentValues ); return this; } public MockRequestMatcher content( String string, Charset charset ) { content( string.getBytes( charset ) ); return this; } public MockRequestMatcher content( byte[] entity ) { if( formParams != null ) { throw new IllegalStateException( "Form params already specified." ); } this.entity = entity; return this; } public MockRequestMatcher content( URL url ) throws IOException { content( url.openStream() ); return this; } public MockRequestMatcher content( InputStream stream ) throws IOException { content( IOUtils.toByteArray( stream ) ); return this; } public MockRequestMatcher contentType( String contentType ) { this.contentType = contentType; return this; } public MockRequestMatcher contentLength( int length ) { this.contentLength = length; return this; } public MockRequestMatcher characterEncoding( String charset ) { this.characterEncoding = charset; return this; } public void match( HttpServletRequest request ) throws IOException { if( methods != null ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " is not using one of the expected HTTP methods", methods, hasItem( request.getMethod() ) ); } if( pathInfo != null ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected pathInfo", request.getPathInfo(), is( pathInfo ) ); } if( requestURL != null ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected requestURL", request.getRequestURL().toString(), is( requestURL ) ); } if( headers != null ) { for( Entry<String, Matcher> entry : headers.entrySet() ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected value for header " + entry.getKey(), request.getHeader( entry.getKey() ), entry.getValue() ); } } if( cookies != null ) { List<Cookie> requestCookies = Arrays.asList( request.getCookies() ); for( Cookie cookie: cookies ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected cookie " + cookie, requestCookies, hasItem( cookie ) ); } } if( contentType != null ) { String[] requestContentType = request.getContentType().split(";",2); assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected content type", requestContentType[ 0 ], is( contentType ) ); } if( characterEncoding != null ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected character encoding", request.getCharacterEncoding(), equalToIgnoringCase( characterEncoding ) ); } if( contentLength != null ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " does not have the expected content length", request.getContentLength(), is( contentLength ) ); } if( attributes != null ) { for( String name: attributes.keySet() ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " is missing attribute '" + name + "'", request.getAttribute( name ), notNullValue() ); assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " has wrong value for attribute '" + name + "'", request.getAttribute( name ), is( request.getAttribute( name ) ) ); } } // Note: Cannot use any of the expect.getParameter*() methods because they will read the // body and we don't want that to happen. if( queryParams != null ) { String queryString = request.getQueryString(); List<NameValuePair> requestParams = parseQueryString( queryString == null ? "" : queryString ); for( Entry<String, String> entry : queryParams.entrySet() ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " query string " + queryString + " is missing parameter '" + entry.getKey() + "'", requestParams, hasItem( new BasicNameValuePair(entry.getKey(), entry.getValue())) ); } } if( formParams != null ) { String paramString = IOUtils.toString( request.getInputStream(), request.getCharacterEncoding() ); List<NameValuePair> requestParams = parseQueryString( paramString == null ? "" : paramString ); for( Entry<String, String[]> entry : formParams.entrySet() ) { String[] expectedValues = entry.getValue(); for( String expectedValue : expectedValues ) { assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " form params " + paramString + " is missing a value " + expectedValue + " for parameter '" + entry.getKey() + "'", requestParams, hasItem( new BasicNameValuePair(entry.getKey(), expectedValue ) )); } } } if( entity != null ) { if( contentType != null && contentType.endsWith( "/xml" ) ) { String expectEncoding = characterEncoding; String expect = new String( entity, ( expectEncoding == null ? UTF8.name() : expectEncoding ) ); String actualEncoding = request.getCharacterEncoding(); String actual = IOUtils.toString( request.getInputStream(), actualEncoding == null ? UTF8.name() : actualEncoding ); assertThat( the( actual ), isEquivalentTo( the( expect ) ) ); } else if ( contentType != null && contentType.endsWith( "/json" ) ) { String expectEncoding = characterEncoding; String expect = new String( entity, ( expectEncoding == null ? UTF8.name() : expectEncoding ) ); String actualEncoding = request.getCharacterEncoding(); String actual = IOUtils.toString( request.getInputStream(), actualEncoding == null ? UTF8.name() : actualEncoding ); // System.out.println( "EXPECT=" + expect ); // System.out.println( "ACTUAL=" + actual ); assertThat( actual, sameJSONAs( expect ) ); } else if( characterEncoding == null || request.getCharacterEncoding() == null ) { byte[] bytes = IOUtils.toByteArray( request.getInputStream() ); assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " content does not match the expected content", bytes, is( entity ) ); } else { String expect = new String( entity, characterEncoding ); String actual = IOUtils.toString( request.getInputStream(), request.getCharacterEncoding() ); assertThat( "Request " + request.getMethod() + " " + request.getRequestURL() + " content does not match the expected content", actual, is( expect ) ); } } } public String toString() { return "from=" + from + ", pathInfo=" + pathInfo; } private static List<NameValuePair> parseQueryString( String queryString ) { return URLEncodedUtils.parse(queryString, Charset.defaultCharset()); } }