/**
 * Tests all of the major functionality of MobyServlet. If you add classes
 * to MOBYServlet's processing, you MUST test them here, otherwise they may not end up in the 
 * MobyServlet deployment WAR.
 */

package org.biomoby.service.test;

import org.biomoby.service.MobyServlet;

import org.biomoby.client.CentralImpl;
import org.biomoby.shared.Central;
import org.biomoby.shared.CentralCached;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.parser.ServiceException;
import org.biomoby.shared.data.*;

import junit.framework.*;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Vector;

public class MobyServletTestCase extends TestCase{
    private final static String TEST_MOBY_XML = "org/biomoby/service/test/mobyAASeqWithSecondaries.xml";
    private final static String TEST_SOAP_XML = "org/biomoby/service/test/soapRequest.xml";  //full SOAP payload
    private final static String SERVLET_TEST_WEBXML = "org/biomoby/service/test/web.xml";
    private final static String SERVLET_TEST_URL = "http://moby.ucalgary.ca:8080/MobyServlet";
    private final static String TEST_MAIN_CLASS_NAME = "org.biomoby.service.test.ConvertAAtoFASTA_AA";

    /**
     * @param name Test case name.
     */
    public MobyServletTestCase(String name) {
        super(name);
    }

    protected URL getExampleURL(){
	URL testDataURL = getClass().getClassLoader().getResource(TEST_MOBY_XML);
	if(testDataURL == null){
	    fail("Could not find test data resource (" + TEST_MOBY_XML + 
		 "), please make sure your class path is specified correctly");
	}
	return testDataURL;
    }

    /**
     * Override this method to run the main method of more specific servlets
     * you want to test (e.g. ACDServlet overrides this and specifies itself
     * as the class to test).
     */
    protected String getExampleClass(){
	return TEST_MAIN_CLASS_NAME;
    }

    /**
     * Override this method to run the main method of more specific servlets
     * you want to test (e.g. ACDServlet overrides this and specifies itself
     * as the class to test).
     */
    protected String getExampleServlet(){
	return SERVLET_TEST_URL;
    }

    public void testMain() throws Exception{
	// Should execute main
	URL testDataURL = getExampleURL();
	String className = getExampleClass();

	// Use reflection to load the class whose main method should be called
	// TODO: Maybe we should check that it inherits from MobyServlet first?
	Class targetClass = null;
	try {
	    targetClass = Class.forName(className);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not find class load " + className + ": " + e);
	}

	java.lang.reflect.Method mainMethod = null;
	try{
	    mainMethod = targetClass.getMethod("main", new Class[] {String[].class});
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not find main method in class " + className + ": " + e);
	}

	MobyServlet.setMainTerminationExit(false);
	String[] args = new String[]{className, testDataURL.toString()};
	try{
	    mainMethod.invoke(null, new Object[]{args});
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Main method for " + className + " had an exception: " +e);
	}
	
	// Should execute subclass main
	//MobyServlet.main(new String[]{TEST_MOBY_XML});
    }

    public MobyServlet getMobyServlet(){
	String webXMLPath = "WEB-INF/web.xml";
	java.net.URL webXmlURL = getClass().getClassLoader().getResource(webXMLPath);
	assertNotNull("No \""+ webXMLPath+"\" in the resource path", webXmlURL);
	TestServletConfig servletConfig = null;
	try{
	    servletConfig = new TestServletConfig(new TestServletContext(), webXmlURL);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not create servlet configuration from " + webXmlURL + ": " + e);
	}

	MobyServlet servlet = null;
        try{
	    // This line can throw many different exception if you didn't get the class right! 
	    Class servletClass = MobyServlet.class.getClassLoader().loadClass(servletConfig.getServletClassName());
            if(servletClass == null){
                throw new ClassNotFoundException("The servlet Class to run (" + 
						 servletConfig.getServletClassName() + 
						 ") was not found, please ensure the web.xml is up-to-date.");
            }
	    servlet = (MobyServlet) servletClass.newInstance();
        } catch(Exception e){
	    e.printStackTrace();
	    fail("The servlet Class was not specified properly in " + webXmlURL +
		 " (the servlet-class tag was not useable): " + e);
        }

	try{
	    servlet.init(servletConfig);
	    servlet.createServiceFromConfig(null);
	}
	catch(Exception e){
	    e.printStackTrace();
	    fail("Caught exception while trying to initialize servlet: " + e);
	}
	return servlet;
    }

    public void testRDF(){
	MobyServlet servlet = getMobyServlet();

	TestHttpServletRequest request = new TestHttpServletRequest();
	request.setParameter(MobyServlet.MODE_HTTP_PARAM, MobyServlet.RDF_MODE);
	request.setRequestURL("http://hostname.domain.tld:8089/UnImportantServiceName");

	TestHttpServletResponse response = new TestHttpServletResponse();
	StringBufferServletOutputStream out = new StringBufferServletOutputStream();
	response.setOutputStream(out);

	try{
	    servlet.doGet(request, response);
	}
	catch(Exception e){
	    e.printStackTrace();
	    fail("Caught exception while trying to generate the service RDF: " + e);
	}

	assertTrue("The returned content-type for the RDF signature request was '"+response.getContentType()+
		   "', expected 'text/xml'" , 
		   "text/xml".equals(response.getContentType()));

	//System.err.println(out.getBuffer().toString());
    }

    public void testHTML(){	
    }

    public void testRequestChain(){
	ConvertAAtoFASTA_AA aa2fasta_aa = new ConvertAAtoFASTA_AA();

	MobyContentInstance requestContents = null;
	try{
	    requestContents = MobyDataUtils.fromXMLDocument(getExampleURL().openStream());
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Exception while trying to load test data: " + e);
	}
	MobyContentInstance resultContents = new MobyContentInstance();

	// Call first service with test data
	try{
	    aa2fasta_aa.processRequests(requestContents, resultContents);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Exception while running ConvertAAtoFASTA_AA (on " + getExampleURL() + "): " + e);
	}

	// Check for errors, there shouldn't be anything above INFO level
	if(resultContents.hasExceptions()){
	    boolean hasErrors = false;
	    for(ServiceException se: resultContents.getExceptions()){
		if(se.getSeverity() < ServiceException.INFO){  //error and warning are < info numerically
		    se.printStackTrace();
		    hasErrors = true;
		}
	    }
	    assertFalse("Running ConvertAAtoFASTA_AA failed on test data " + getExampleURL(), hasErrors);
	}

	MobyDataObjectSet resultObjects = resultContents.retrieveObjects();
	if(!resultObjects.getDataType().inheritsFrom("FASTA_AA")){
	    fail("The resulting objects from ConvertAAtoFASTA_AA do not inherit from FASTA_AA, "+
		 "but are rather " + resultObjects.getDataType().getName());
	}

	FromFastaToDNASequence fasta_na2dna = new FromFastaToDNASequence();

	// Rename the parameters for the next service
	for(MobyDataJob job: resultContents.values()){
	    MobyDataObject seq = (MobyDataObject) job.get("outseq");
	    job.put("inseq", seq);
	    job.remove("outseq");
	}

	// Test the serialization/deserialization of contents (instead of just passing
	// the MobyContentInstance from one service to the next)
	StringBufferServletOutputStream outBuffer = new StringBufferServletOutputStream();
	try{
	    if(!MobyDataUtils.toXMLDocument(outBuffer, resultContents)){
		fail("Could not serialize (to XML) the results of ConvertAAtoFASTA_AA");
	    }
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not serialize (to XML) the results of ConvertAAtoFASTA_AA:" + e);
	}
	resultContents.clear();  // so we don't build up junk from multiple responses

	// Sets up hashtables, etc. we'll use implicitly below for error checking
	try{
	    fasta_na2dna.init(getMobyServlet().getServletConfig()); 
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Exception while configuring FromFastaToDNASequence servlet for testing: " + e);
	}

	System.err.println("Don't worry, an exception warning about INPUT_INCORRECT_SIMPLE should appear next...it's expected");
	// Call second service on output of previous service, the input is actually AA, not DNA as expected
	try{
	    fasta_na2dna.processRequests(MobyDataUtils.fromXMLDocument(new java.io.ByteArrayInputStream(outBuffer.getBuffer().toString().getBytes())), 
					 resultContents);
	} catch(Exception e){
	    e.printStackTrace();
	    System.err.println(outBuffer.getBuffer().toString());
	    fail("Exception while running FromFastaToDNASequence on the results of ConvertAAtoFASTA_AA:" + e);
	}

	// Check for the input type warning (tests the exception reporting)
	if(!resultContents.hasExceptions()){
	    fail("Running FromFastaToDNASequence did *not* produce an exception as expected " +
		 "(processing an amino acid sequence), perhaps something is wrong with exception reporting?");
	}
	boolean haveWarning = false;
	boolean haveError = false;
	for(ServiceException se: resultContents.getExceptions()){
	    if(se.getSeverity() == ServiceException.WARNING){
		haveWarning = true;
	    }
	    else if(se.getSeverity() == ServiceException.ERROR){
		haveError = true;
		se.printStackTrace();
	    }
	    else{
		se.printStackTrace();
	    }
	}
	assertFalse("Running FromFastaToDNASequence produced an error level exception, " +
		    "but only a warning (amino acid input) was expected", haveError);
	assertTrue("Running FromFastaToDNASequence did not produce a warning level exception," +
		   "but one (amino acid input warning) was expected", haveWarning);

        resultObjects = resultContents.retrieveObjects();
	if(!resultObjects.getDataType().inheritsFrom("DNASequence")){
	    resultObjects.setXmlMode(MobyDataObject.SERVICE_XML_MODE);
	    System.err.println(resultObjects.toXML());
	    fail("The resulting objects from FromFastaToDNASequence do not inherit from DNASequence, "+
		 "but are rather " + resultObjects.getDataType().getName());
	}
    }

    public void testServletTester(){
	try{
	    // Use the specific, basic servlet test, with the defined web.xml
	    // because we want to be able to do this test regardless of the
	    // class environment we find ourselves in (i.e. a WEB-INF/web.xml
	    // that is for a more specialized service such as ACDService)
	    ServletTester tester = new ServletTester(SERVLET_TEST_WEBXML);

	    // Clear the ontology cache
	    Central central = CentralImpl.getDefaultCentral(null);
	    if(central instanceof CentralCached){
		((CentralCached) central).removeFromCache(null);
	    }

	    // A useful side effect of an object instance is that it
	    // can use the classloader that loaded it to find resources
	    // (there is no class instance get a classloader from in static methods) 
	    tester.runService(new String[]{SERVLET_TEST_URL, TEST_MOBY_XML});

	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not run servlet tester successfully: " + e);
	}
    }

    public void testSoapParsing() throws Exception{
	URL testDataURL = getClass().getClassLoader().getResource(TEST_SOAP_XML);
	assertNotNull("Could not find test soap rerequest resource (" + TEST_SOAP_XML + 
		      "), please make sure your class path is specified correctly", testDataURL);

	TestHttpServletRequest testRequest = new TestHttpServletRequest();
	testRequest.setInputStream(testDataURL.openStream());

	MobyServlet servlet = getMobyServlet();
	servlet.createServiceFromConfig(null);  // initialized required settings
	MobyContentInstance mobyContents = servlet.getMobyContents(testRequest);
	assertNotNull("Could not retrieve the MOBY data payload from the test HTTP request", mobyContents);
    }

    public void testAsync(){
	// TODO
    }

    public void testSecondaryParams(){
    }

    /**
     * @return a test suite for all the test methods of this test case.
     */
    public static Test suite() {
	System.setProperty("javax.xml.parsers.DocumentBuilderFactory", 
			   "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); 
	TestSuite suite = new TestSuite();
      	suite.addTest(new MobyServletTestCase("testRDF"));
 	suite.addTest(new MobyServletTestCase("testSoapParsing"));
  	suite.addTest(new MobyServletTestCase("testRequestChain")); 
  	suite.addTest(new MobyServletTestCase("testMain"));
  	suite.addTest(new MobyServletTestCase("testSecondaryParams"));
  	suite.addTest(new MobyServletTestCase("testServletTester"));
        return suite;
    }

    /**
     * Runs the test suite when the class is invoked on the command line.
     */
    public static void main(String args[]) throws Exception{
        junit.textui.TestRunner.run(suite());
    }


}
