001package org.apache.maven.wagon.tck.http;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.wagon.ResourceDoesNotExistException;
023import org.apache.maven.wagon.TransferFailedException;
024import org.apache.maven.wagon.WagonException;
025import org.apache.maven.wagon.authorization.AuthorizationException;
026import org.apache.maven.wagon.proxy.ProxyInfo;
027import org.codehaus.plexus.util.IOUtil;
028import org.codehaus.plexus.util.StringUtils;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import javax.servlet.http.HttpServletResponse;
033import java.io.File;
034import java.io.IOException;
035import java.io.InputStream;
036
037import static org.codehaus.plexus.util.FileUtils.fileRead;
038import static org.junit.Assert.assertEquals;
039import static org.junit.Assert.assertNotNull;
040import static org.junit.Assert.assertTrue;
041
042/**
043 *
044 */
045public final class Assertions
046{
047
048    public static final int NO_RESPONSE_STATUS_CODE = -1;
049
050    protected static final Logger LOGGER = LoggerFactory.getLogger( Assertions.class );
051
052    public static void assertFileContentsFromResource( final String resourceBase, final String resourceName,
053                                                       final File output, final String whyWouldItFail )
054        throws IOException
055    {
056        String content = readResource( resourceBase, resourceName );
057        String test = fileRead( output );
058
059        assertEquals( whyWouldItFail, content, test );
060    }
061
062    private static String readResource( final String base, final String name )
063        throws IOException
064    {
065        String url = base;
066        if ( !url.endsWith( "/" ) && !name.startsWith( "/" ) )
067        {
068            url += "/";
069        }
070        url += name;
071
072        ClassLoader cloader = Thread.currentThread().getContextClassLoader();
073        InputStream stream = cloader.getResourceAsStream( url );
074
075        if ( stream == null )
076        {
077            return null;
078        }
079
080        final String resource = IOUtil.toString( stream );
081        stream.close();
082        return resource;
083    }
084
085    /**
086     * Assert a WagonException message contains required format and context based on the status code we expected to
087     * trigger it in the first place.
088     * <p>
089     * This implementation represents the most desired assertions, but HttpWagonTestCase sub-classes could override
090     * this method if a specific wagon representation makes it impossible to meet these assertions.
091     *
092     * @param e               an instance of {@link WagonException}
093     * @param forStatusCode   the response status code that triggered the exception
094     * @param forUrl          the url that triggered the exception
095     * @param forReasonPhrase the optional status line reason phrase the server returned
096     */
097    public static void assertWagonExceptionMessage( Exception e, int forStatusCode, String forUrl,
098                                                    String forReasonPhrase, ProxyInfo proxyInfo )
099    {
100        // TODO: handle AuthenticationException for Wagon.connect() calls
101        assertNotNull( e );
102        try
103        {
104            assertTrue( "only verify instances of WagonException", e instanceof WagonException );
105
106            String reasonPhrase;
107            String assertMessageForBadMessage = "exception message not described properly";
108
109            if ( proxyInfo != null )
110            {
111                assertTrue( "message should end with proxy information if proxy was used",
112                        e.getMessage().endsWith( proxyInfo.toString() ) );
113            }
114
115            switch ( forStatusCode )
116            {
117                case HttpServletResponse.SC_NOT_FOUND:
118                    // TODO: add test for 410: Gone?
119                    assertTrue( "404 not found response should throw ResourceDoesNotExistException",
120                            e instanceof ResourceDoesNotExistException );
121                    reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Not Found" : ( " " + forReasonPhrase );
122                    assertEquals( assertMessageForBadMessage, "resource missing at " + forUrl + ", status: 404"
123                            + reasonPhrase, e.getMessage() );
124                    break;
125
126                case HttpServletResponse.SC_UNAUTHORIZED:
127                    // FIXME assumes Wagon.get()/put() returning 401 instead of Wagon.connect()
128                    assertTrue( "401 Unauthorized should throw AuthorizationException since "
129                                    + " AuthenticationException is not explicitly declared as thrown from wagon "
130                                    + "methods",
131                            e instanceof AuthorizationException );
132                    reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Unauthorized" : ( " " + forReasonPhrase );
133                    assertEquals( assertMessageForBadMessage, "authentication failed for " + forUrl + ", status: 401"
134                            + reasonPhrase, e.getMessage() );
135                    break;
136
137                case HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED:
138                    assertTrue( "407 Proxy authentication required should throw AuthorizationException",
139                            e instanceof AuthorizationException );
140                    reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Proxy Authentication Required"
141                            : ( " " + forReasonPhrase );
142                    assertEquals( assertMessageForBadMessage, "proxy authentication failed for "
143                            + forUrl + ", status: 407" + reasonPhrase, e.getMessage() );
144                    break;
145
146                case HttpServletResponse.SC_FORBIDDEN:
147                    assertTrue( "403 Forbidden should throw AuthorizationException",
148                            e instanceof AuthorizationException );
149                    reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Forbidden" : ( " " + forReasonPhrase );
150                    assertEquals( assertMessageForBadMessage, "authorization failed for " + forUrl + ", status: 403"
151                            + reasonPhrase, e.getMessage() );
152                    break;
153
154                default:
155                    assertTrue( "transfer failures should at least be wrapped in a TransferFailedException", e
156                            instanceof TransferFailedException );
157
158                    // the status code and reason phrase cannot always be learned due to implementation limitations
159                    // so the message may not include them, but the implementation should use a consistent format
160                    assertTrue( "message should always include url tried: " + e.getMessage(),
161                            e.getMessage().startsWith( "transfer failed for " + forUrl ) );
162
163                    String statusCodeStr = forStatusCode == NO_RESPONSE_STATUS_CODE ? ""
164                            : ", status: " +  forStatusCode;
165                    if ( forStatusCode != NO_RESPONSE_STATUS_CODE )
166                    {
167
168                        assertTrue( "if there was a response status line, the status code should be >= 400",
169                                forStatusCode >= HttpServletResponse.SC_BAD_REQUEST );
170
171                        if ( e.getMessage().length() > ( "transfer failed for " + forUrl ).length() )
172                        {
173                            assertTrue( "message should include url and status code: " + e.getMessage(),
174                                    e.getMessage().startsWith( "transfer failed for " + forUrl + statusCodeStr ) );
175                        }
176
177                        reasonPhrase = forReasonPhrase == null ? "" : " " + forReasonPhrase;
178
179                        if ( reasonPhrase.length() > 0 && e.getMessage().length() > ( "transfer failed for " + forUrl
180                                + statusCodeStr ).length() )
181                        {
182                            assertTrue( "message should include url and status code and reason phrase: "
183                                    + e.getMessage(), e.getMessage().startsWith( "transfer failed for "
184                                            + forUrl + statusCodeStr
185                                            + reasonPhrase ) );
186                        }
187
188                    }
189
190                    break;
191            }
192        }
193        catch ( AssertionError assertionError )
194        {
195            LOGGER.error( "Exception which failed assertions: ", e );
196            throw assertionError;
197        }
198
199    }
200
201}