/* * Copyright (C) 2011 Laurent Caillette * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.novelang.daemon; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.junit.Rule; import org.junit.Test; import static com.google.common.base.Charsets.ISO_8859_1; import static com.google.common.base.Charsets.UTF_8; import static org.fest.assertions.Assertions.assertThat; import static org.junit.Assert.*; import static org.novelang.daemon.HttpDaemonFixture.PDF; import static org.novelang.daemon.HttpDaemonFixture.shaveComments; import static org.novelang.outfit.TextTools.unixifyLineBreaks; import static org.novelang.rendering.multipage.MultipageFixture.TargetPage.*; import org.novelang.ResourcesForTests; import org.novelang.common.filefixture.Resource; import org.novelang.outfit.DefaultCharset; import org.novelang.produce.GenericRequest; import org.novelang.produce.MalformedRequestException; import org.novelang.rendering.multipage.MultipageFixture; /** * Tests for {@link HttpDaemon} based on {@link org.novelang.daemon.HttpDaemonSupport}. * * @author Laurent Caillette */ public class HttpDaemonTest { @Test public void redirectAfterHittingRenderingProblem() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART ; support.resourceInstaller.copy( resource ) ; final Resource stylesheetResource = ResourcesForTests.XslFormatting.XSL_BROKEN_RENDERING ; final File stylesheetFile = support.resourceInstaller.copyScoped( ResourcesForTests.XslFormatting.dir, stylesheetResource ) ; support.setup( stylesheetFile.getParentFile(), DefaultCharset.RENDERING ) ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( support.createUrl( "/" + resource.getBaseName() + HttpDaemonFixture.PDF + "?stylesheet=" + stylesheetResource.getName() ).toExternalForm() ) ; // We can't cast some rubbish to ReadableDateTime. assertThat( responseSnapshot.getContent() ).contains( "org.joda.time.ReadableDateTime" ) ; // That's the matgic for a PDF document. assertThat( responseSnapshot.getContent() ).doesNotContain( "%PDF-" ) ; } @Test public void multipage() throws Exception { final MultipageFixture multipageFixture = new MultipageFixture( support.resourceInstaller, ResourcesForTests.Multipage.MULTIPAGE_XSL, ResourcesForTests.MainResources.Style.DEFAULT_NOVELLA_XSL ) ; support.setup( multipageFixture.getStylesheetFile().getParentFile(), DefaultCharset.RENDERING ) ; verify( multipageFixture, ZERO ) ; verify( multipageFixture, MAIN ) ; verify( multipageFixture, ONE ) ; } @Test public void testAlternateStylesheetInBook() throws Exception { final Resource resource = ResourcesForTests.Served.BOOK_ALTERNATE_XSL ; support.resourceInstaller.copy( resource ) ; support.resourceInstaller.copy( ResourcesForTests.Served.GOOD_PART ) ; final File stylesheetFile = support.resourceInstaller.copyScoped( ResourcesForTests.Served.dir, ResourcesForTests.Served.Style.VOID_XSL ) ; support.setup( stylesheetFile.getParentFile(), DefaultCharset.RENDERING ) ; final byte[] generated = support.readAsBytes( "/" + resource.getBaseName() + HttpDaemonFixture.HTML ) ; assertTrue( new String( generated ).contains( "this is the void stylesheet" ) ) ; } @Test public void indicateErrorLocationForBrokentStylesheet() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART ; support.resourceInstaller.copy( resource ) ; final Resource stylesheetResource = ResourcesForTests.Served.Style.ERRONEOUS_XSL ; final File stylesheetFile = support.resourceInstaller.copyScoped( ResourcesForTests.Served.dir, stylesheetResource ) ; support.setup( stylesheetFile.getParentFile(), DefaultCharset.RENDERING ) ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( support.createUrl( "/" + resource.getBaseName() + HttpDaemonFixture.HTML + "?stylesheet=" + stylesheetResource.getName() ).toExternalForm() ) ; assertThat( responseSnapshot.getContent() ).contains( "line=25; column=38 - " + "xsl:this-is-not-supposed-to-work is not allowed in this position in the stylesheet" ) ; } @Test public void testAlternateStylesheetInQueryParameter() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_BOOK ; support.resourceInstaller.copy( resource ) ; support.resourceInstaller.copy( ResourcesForTests.Served.GOOD_PART ) ; final File stylesheetFile = support.resourceInstaller.copyScoped( ResourcesForTests.Served.dir, ResourcesForTests.Served.Style.VOID_XSL ) ; support.setup( stylesheetFile.getParentFile(), DefaultCharset.RENDERING ) ; final byte[] generated = support.readAsBytes( "/" + resource.getBaseName() + HttpDaemonFixture.HTML + "?stylesheet=" + ResourcesForTests.Served.Style.VOID_XSL.getName() ) ; assertTrue( new String( generated ).contains( "this is the void stylesheet" ) ) ; } @Test public void listDirectoryContentWithSafari() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.resourceInstaller.copyWithPath( resource ) ; support.setup() ; final String urlAsString = support.createUrl( "/" ).toExternalForm() ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( urlAsString, HttpDaemonFixture.SAFARI_USER_AGENT ) ; assertEquals( 1L, ( long ) responseSnapshot.getLocationsRedirectedTo().size() ) ; assertEquals( urlAsString + DirectoryScanHandler.MIME_HINT, responseSnapshot.getLocationsRedirectedTo().get( 0 ).getValue() ) ; HttpDaemonFixture.checkDirectoryListing( responseSnapshot, resource ) ; } @Test public void listDirectoryContentWithTrailingSolidus() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.resourceInstaller.copyWithPath( resource ) ; support.setup() ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( support.createUrl( "/" ).toExternalForm() ) ; HttpDaemonFixture.checkDirectoryListing( responseSnapshot, resource ) ; } @Test public void listDirectoryContentNoTrailingSolidus() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.resourceInstaller.copyWithPath( resource ) ; support.setup() ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( support.createUrl( "" ).toExternalForm() ) ; HttpDaemonFixture.checkDirectoryListing( responseSnapshot, resource ) ; } @Test public void errorPageForUnbrokenHtmlNotBrokenCausesRedirection() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.setup( resource ) ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( support.createUrl( "/" + resource.getBaseName() + HttpDaemonFixture.HTML + GenericRequest.ERRORPAGE_SUFFIX ).toExternalForm() ) ; assertFalse( responseSnapshot.getContent().contains( "Requested:" ) ) ; } @Test public void htmlBrokenCausesRedirection() throws Exception { final Resource resource = ResourcesForTests.Served.BROKEN_NOVELLA; support.setup( resource ) ; final String brokenDocumentRequest = "/" + resource.getBaseName() + HttpDaemonFixture.HTML ; final String brokenDocumentUrl = support.createUrl( brokenDocumentRequest ).toExternalForm() ; final HttpDaemonFixture.ResponseSnapshot responseSnapshot = support.followRedirection( brokenDocumentUrl ) ; assertTrue( responseSnapshot.getContent().contains( "Requested:" ) ) ; assertTrue( "Expected link to requested page", responseSnapshot.getContent().contains( brokenDocumentRequest ) ) ; assertEquals( 1L, ( long ) responseSnapshot.getLocationsRedirectedTo().size() ) ; assertEquals( brokenDocumentUrl + GenericRequest.ERRORPAGE_SUFFIX, responseSnapshot.getLocationsRedirectedTo().get( 0 ).getValue() ) ; } /** * Tests if a Novella renders as its own source! * In order to force character escape we use non-default encoding. */ @Test public void novellaOk() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; final String novellaSource = support.alternateSetup( resource, UTF_8, ISO_8859_1 ) ; final String generated = support.readAsString( resource, // Weird, but forces correct escaping. Plays with partial compatiblity with ISO_8859_1. HttpDaemonFixture.DEFAULT_PLATFORM_CHARSET // MAC_ROMAN ) ; final String normalizedNovellaSource = unixifyLineBreaks( novellaSource ) ; final String normalizedShaved = unixifyLineBreaks( shaveComments( generated ) ) ; assertThat( normalizedShaved ).isEqualTo( normalizedNovellaSource ) ; } @Test public void pdfOk() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.alternateSetup( resource, UTF_8, ISO_8859_1 ) ; final byte[] generated = support.readAsBytes( "/" + resource.getBaseName() + PDF ) ; final String pdfText = HttpDaemonFixture.extractPdfText( generated ) ; assertThat( pdfText ).contains( GOOD_PART_EXTRACT ) ; } @Test public void correctMimeTypeForPdf() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.setup( resource ) ; final HttpGet httpGet = support.createHttpGet( "/" + resource.getBaseName() + HttpDaemonFixture.PDF ) ; final HttpResponse httpResponse = new DefaultHttpClient().execute( httpGet ) ; final Header[] headers = httpResponse.getHeaders( "Content-type" ) ; assertThat( headers ).isNotEmpty() ; assertThat( headers[ 0 ].getValue() ).isEqualTo( "application/pdf" ) ; } @Test public void greekCharactersOk() throws Exception { final Resource resource = ResourcesForTests.Parts.NOVELLA_GREEK; renderPdfAndCheckStatusCode( resource ); } @Test public void polishCharactersOk() throws Exception { final Resource resource = ResourcesForTests.Parts.NOVELLA_POLISH ; renderPdfAndCheckStatusCode( resource ) ; } @Test public void romanianCharactersOk() throws Exception { final Resource resource = ResourcesForTests.Parts.NOVELLA_ROMANIAN ; renderPdfAndCheckStatusCode( resource ) ; } @Test // @Ignore( "Moved to BetterHttpDaemonTest" ) public void htmlNoSmoke() throws Exception { final Resource resource = ResourcesForTests.Served.GOOD_PART; support.setup( resource ) ; final String generated = support.readAsString( "/" + resource.getBaseName() + HttpDaemonFixture.HTML ) ; assertThat( generated ).contains( GOOD_PART_EXTRACT ) ; } // ======= // Fixture // ======= @Rule public final HttpDaemonSupport support = new HttpDaemonSupport() ; private static final Charset MAC_ROMAN = Charset.forName( "MacRoman" ) ; private void renderPdfAndCheckStatusCode( final Resource resource ) throws Exception { final Resource novellaGreek = resource ; support.setup( novellaGreek ) ; support.renderAndCheckStatusCode( "/" + resource.getBaseName() + PDF ) ; } private void verify( final MultipageFixture multipageFixture, final MultipageFixture.TargetPage targetPage ) throws IOException, MalformedRequestException { MultipageFixture.verify( targetPage, support.readAsBytes( multipageFixture.requestFor( targetPage ).getOriginalTarget() ) ) ; } private static final String GOOD_PART_EXTRACT = "Used in HttpDaemonTest. Edit with care."; }