content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * 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.
   */
  package org.n52.wps.test;
  
  import static org.hamcrest.MatcherAssert.assertThat;
  import static org.hamcrest.Matchers.containsString;
  import static org.hamcrest.Matchers.is;
  import static org.hamcrest.Matchers.not;
  import static org.hamcrest.Matchers.nullValue;
  
  import java.io.IOException;
  import java.io.StringWriter;
  import java.net.HttpURLConnection;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Random;
  import java.util.UUID;
  
  import javax.servlet.http.HttpServletResponse;
  import javax.xml.parsers.ParserConfigurationException;
  
  import org.apache.commons.io.IOUtils;
  import org.apache.xmlbeans.XmlException;
  import org.apache.xmlbeans.XmlObject;
  import org.junit.AfterClass;
  import org.junit.Assume;
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.rosuda.REngine.Rserve.RConnection;
  import org.rosuda.REngine.Rserve.RserveException;
  import org.xml.sax.SAXException;
  
  /**
   * 
   * To run this integration tests there has to be RServe running on the localhost and the R repository must be
   * enabled in the WPS config.
   * 
   * To start RServe:
   * 
   * <ul>
   * <li>start <code>R</code></li>
   * <li>in the R console, load the library Rserve: <code>library(Rserve)</code></li>
   * <li>in the R console, start Rserve: <code>Rserve()</code></li>
   * <li>you should see and output "... Ok, ready to answer queries."</li>
   * </ul>
   * 
   * To enable the R process repository:
   * 
   * <ul>
   * <li>open your WPSConfiguration file, normally located in /config/wps_config.xml</li>
   * <li>Find the "LocalRAlgorithmRepository"</li>
   * <li>set the attribute "active" to <code>true</code></li>
   * <li>(restart your WPS server)</li>
   * </ul>
   */
  public class Wps4rIT {
  
      private static String wpsUrl;
  
      @BeforeClass
      public static void beforeClass() {
          wpsUrl = AllTestsIT.getURL();
  
          // Seems not to work but it would be nice if it does...
          // URL resource = WPS4RTester.class
          // .getResource("/R/wps_config.xml");
          // WPSConfig.forceInitialization(new File(resource.getFile()).getAbsolutePath());
  
          String host = System.getProperty("test.rserve.host", "127.0.0.1");
          int port = Integer.parseInt(System.getProperty("test.rserve.port", "6311"));
          String user = System.getProperty("test.rserve.user", null);
          String password = System.getProperty("test.rserve.pwd", null);
          try {
              RConnection c = getNewConnection(host, port, user, password);
              c.close();
          }
          catch (RserveException e1) {
              Assume.assumeNoException(e1);
          }
      }
  
      @AfterClass
      public static void afterClass() {
          // WPSConfig.forceInitialization("src/main/webapp/config/wps_config.xml");
      }
  
      private static RConnection getNewConnection(String host, int port, String user, String password) throws RserveException {
          RConnection con = new RConnection(host, port);
          if (con != null && con.needLogin())
              con.login(user, password);
  
          return con;
      }
  
      /*
       * DN: test disabled, sessionInfo.jsp was deleted, service endpoint must be implemented.
       */
      // @Test
      public void sessionInfoRetrievedFromWPSWebsite() throws MalformedURLException {
          String temp = wpsUrl.substring(0, wpsUrl.lastIndexOf("/"));
          URL urlSessionInfo = new URL(temp + "/rsessioninfoendpoint");
          try {
              String response = GetClient.sendRequest(urlSessionInfo.toExternalForm());
              assertThat(response, containsString("R ")); // "R version" fails if using unstable R!
              assertThat(response, containsString("Platform:"));
              assertThat(response, containsString("attached base packages:"));
          }
          catch (IOException e) {
              String message = "Cannot retrieve the R session info from WPS.";
              e.printStackTrace();
              throw new AssertionError(message);
          }
      }
  
      @Test
      public void resourcesAreLoadedAndRead() throws IOException,
              ParserConfigurationException,
              SAXException,
              XmlException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestResources.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat("response is not an exception", response, not(containsString("ExceptionReport")));
          assertThat("text resource content could be read",
                     response,
                     containsString("dataType=\"xs:string\">This is a dummy txt-file"));
          assertThat("image resource is loaded", response, containsString("480"));
          assertThat("directories are created", response, containsString("xs:integer\">1</wps:LiteralData>"));
          assertThat("subdirectory resources can be read", response, containsString("xs:double\">42.0</wps:LiteralData>"));
          assertThat("subsubdirectory resources can be read",
                     response,
                     containsString("xs:integer\">17</wps:LiteralData>"));
          assertThat("recursive folders all exist", response, containsString("xs:integer\">3</wps:LiteralData>"));
      }
  
      @Test
      public void responseContainsSessionInfo() throws IOException,
              ParserConfigurationException,
              SAXException,
              XmlException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestResources.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat(response, not(containsString("ExceptionReport")));
          assertThat(response, containsString("attached base packages:"));
          assertThat(response, containsString("locale:"));
          assertThat(response, containsString("<wps:ComplexData encoding=\"UTF-8\" mimeType=\"text/plain\">"));
          assertThat(response, containsString("methods"));
          assertThat(response, containsString("base"));
      }
  
      @Test
      public void responseContainsWarningsSection() throws IOException,
              ParserConfigurationException,
              SAXException,
              XmlException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWarnings.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat(response, containsString("<ows:Identifier>warnings</ows:Identifier>"));
          assertThat(response, containsString("mimeType=\"text/plain\">warning"));
          assertThat(response, containsString("Test warning 1"));
          assertThat(response, containsString("Test warning 4: This is the LAST warning"));
      }
  
      @Test
      public void responseContainsWarningsContent() throws IOException,
              ParserConfigurationException,
              SAXException,
              XmlException {
  
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWarnings.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat(response, containsString("This is the LAST warning."));
      }
  
      @Test
      public void decribeProcess() throws IOException, ParserConfigurationException, SAXException {
          String identifier = "org.n52.wps.server.r.test.resources";
          String response = GetClient.sendRequest(wpsUrl, "Service=WPS&Request=DescribeProcess&Version=1.0.0&Identifier="
                  + identifier);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat(response, not(containsString("ExceptionReport")));
          assertThat(response, containsString(identifier));
          assertThat(response, containsString("<ows:Identifier>" + identifier + "</ows:Identifier>"));
      }
  
      @Test
      public void capabilitiesContainProcess() throws IOException, ParserConfigurationException, SAXException {
          String response = GetClient.sendRequest(wpsUrl, "Service=WPS&Request=GetCapabilities");
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          assertThat(response, not(containsString("ExceptionReport")));
          assertThat(response, containsString("org.n52.wps.server.r.test.resources"));
          assertThat(response, containsString("org.n52.wps.server.r.test.calculator"));
          assertThat(response, containsString("org.n52.wps.server.r.test.image"));
      }
  
      @Test
      public void responseTypeIsImage() throws IOException, XmlException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestImage.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
          String payload = xmlPayload.toString();
          payload = payload.replace("@@@size@@@", "420");
  
          String response = PostClient.sendRequest(wpsUrl, payload);
          assertThat(response.split("\n", 1)[0], containsString("PNG"));
          assertThat(response, response, not(containsString("ExceptionReport")));
      }
  
      @Test
      public void calculatorWorksCorrectly() throws IOException, ParserConfigurationException, SAXException, XmlException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCalculator.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
          String payload = xmlPayload.toString();
  
          Random rand = new Random();
          int a = rand.nextInt(100);
          payload = payload.replace("@@@a@@@", Integer.toString(a));
          int b = rand.nextInt(100);
          payload = payload.replace("@@@b@@@", Integer.toString(b));
          int op = rand.nextInt(3);
          String[] ops = new String[] {"+", "-", "*"};
          String opString = ops[op];
          payload = payload.replace("@@@op@@@", opString);
          int result = Integer.MIN_VALUE;
          if (opString.equals("+"))
              result = a + b;
          else if (opString.equals("-"))
              result = a - b;
          else if (opString.equals("*"))
              result = a * b;
  
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
          String expected = "dataType=\"xs:double\">" + Integer.toString(result) + ".0";
          assertThat(response, containsString(expected));
      }
  
      @Test
      public void wpsOffAnnotationWorks() throws XmlException, IOException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWpsOff.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          String expected = "<wps:LiteralData dataType=\"xs:integer\">42</wps:LiteralData>";
          assertThat("Returned value is sum of provided ones, not sum of values defined in deactivated code.",
                     response,
                     containsString(expected));
      }
  
      @Test
      public void defaultValuesAreLoaded() throws XmlException, IOException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestDefaults.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
          String payload = xmlPayload.toString();
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          String expected = "<wps:LiteralData dataType=\"xs:integer\">42</wps:LiteralData>";
          assertThat("Returned value is sum of defaults, not sum of values defined in deactivated code.",
                     response,
                     containsString(expected));
      }
  
      @Test
      public void exceptionsOnIllegalInputs() throws XmlException, IOException {
          String[] illegalCommands = new String[] {
          // "\\x0022;", // FIXME Rserve can be crashed with this
          // "\u0071\u0075\u0069\u0074\u0028\u0029",
          // "<-", "a<-q", // result in WPS parsing error
          "="};
  
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          for (String cmd : illegalCommands) {
              String payload = xmlPayload.toString();
              payload = payload.replace("@@@data@@@", cmd);
  
              String expected = "illegal input";
              
              PostClient.checkForExceptionReport(wpsUrl, payload, HttpServletResponse.SC_BAD_REQUEST, expected, cmd);
          }
      }
  
      @Test
      public void syntaxErrorOnIllegalInputs() throws XmlException, IOException {
          String[] illegalCommands = new String[] {"\"\";quit(\"no\");", "setwd('/root/')", "setwd(\"c:/\")"};
  
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          for (String cmd : illegalCommands) {
              String payload = xmlPayload.toString();
              payload = payload.replace("@@@data@@@", cmd);
              
              String expected = "eval failed";
              PostClient.checkForExceptionReport(wpsUrl, payload, HttpServletResponse.SC_BAD_REQUEST, expected);
          }
      }
  
      @Test
      public void replacementsOnIllegalInputs() throws XmlException, IOException {
          String[] illegalCommands = new String[] {"unlink(getwd())", "q();", "quit()", "%lt;-",
                                                   // "system('format hardisk')",
                                                   "quit(\\\"no\\\");inputVariable;"};
  
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          for (String cmd : illegalCommands) {
              String payload = xmlPayload.toString();
              payload = payload.replace("@@@data@@@", cmd);
  
              String response = PostClient.sendRequest(wpsUrl, payload);
  
              assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
              // assertThat("Response contains an echo of '" + cmd + "'", response, containsString(cmd));
          }
      }
  
      @Test
      public void renamingDoesNotAffectScript() throws XmlException, IOException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String data = UUID.randomUUID().toString();
          String payload = xmlPayload.toString();
          payload = payload.replace("@@@data@@@", data);
  
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
          assertThat("Response contains an echo of '" + data + "'", response, containsString(data));
      }
  
      @Test
      public void csvResponseInlineWorks() throws XmlException, IOException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCSV.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          payload = payload.replace("@@@ref@@@", Boolean.toString(false));
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
          assertThat("Response contains mime type", response, containsString("mimeType=\"text/csv\""));
  
          assertThat("Response contains test data names",
                     response,
                     containsString("\"cadmium\",\"copper\",\"lead\",\"zinc\""));
      }
  
      @Test
      public void csvResponseReferenceWorks() throws XmlException, IOException {
          URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCSV.xml");
          XmlObject xmlPayload = XmlObject.Factory.parse(resource);
  
          String payload = xmlPayload.toString();
          payload = payload.replace("@@@ref@@@", Boolean.toString(true));
          String response = PostClient.sendRequest(wpsUrl, payload);
  
          assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
          assertThat("Response contains mime type", response, containsString("mimeType=\"text/csv\""));
  
          String urlString = response.substring(response.indexOf("http", response.indexOf("text/csv")));
          urlString = urlString.substring(0, urlString.indexOf("\""));
          URL url = new URL(urlString);
          HttpURLConnection connection = (HttpURLConnection) url.openConnection();
          connection.connect();
          String contentType = connection.getContentType();
          assertThat("Response content type is correct", contentType, is("text/csv"));
  
          StringWriter writer = new StringWriter();
          IOUtils.copy(connection.getInputStream(), writer);
          String csv = writer.toString();
  
          assertThat("CSV file contains test data names", csv, containsString("\"cadmium\",\"copper\",\"lead\",\"zinc\""));
      }
  
  }
  