/*
 * JBoss, Home of Professional Open Source
 * Copyright 2009, Red Hat Middleware LLC, and others contributors as indicated
 * by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.jboss.soa.esb.actions.bpel;

import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.format.MessageFactory;
import org.w3c.dom.Node;

import junit.framework.TestCase;

public class BPELInvokeTest extends TestCase {

	private static final String MESSAGE = "message";
	private static final String TEST_SERVICE = "TestService";
	private static final String TEST_OP = "TestOp";
	private static final String REQ_TEST_PART = "ReqTestPart";
	private static final String RESP_TEST_PART = "RespTestPart";
	private static final String TEST_MESSAGE_ELEM = "hello";

	/**
	 * This test supplies the part content in text form, so the action needs to add the
	 * configured part name before invoking the process, and similarly remove the part
	 * name from the response and return as text.
	 */
	public void testNamedPartText() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.REQUEST_PART_NAME, REQ_TEST_PART);
		config.setAttribute(BPELInvoke.RESPONSE_PART_NAME, RESP_TEST_PART);
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
			
		// Message to be returned from invoke
		String respTextMessageText="Hello World Response";
		String respTextMessage="<"+TEST_MESSAGE_ELEM+">"+respTextMessageText+"</"+TEST_MESSAGE_ELEM+">";			
		String respMessage="<"+MESSAGE+"><"+RESP_TEST_PART+">"+respTextMessage+
					"</"+RESP_TEST_PART+"></"+MESSAGE+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine(respMessage);
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp == null) {
			fail("Response expected");
		}
		
		// Validate request received by engine
		if (bpelEngine.getRequest() == null) {
			fail("Request not set");
		}
		
		org.w3c.dom.Element req=bpelEngine.getRequest();
		
		if (req.getNodeName().equals(MESSAGE) == false) {
			fail("Request top level node is not message: "+req.getNodeName());
		}
		
		org.w3c.dom.Element reqpart=getElement(req, REQ_TEST_PART);
		
		if (reqpart == null) {
			fail("Request does not have test part");
		}
		
		org.w3c.dom.Element reqmsgelem=getElement(reqpart, TEST_MESSAGE_ELEM);
		
		if (reqmsgelem == null) {
			fail("Request does not have test message element");
		}
		
		if (reqmsgelem.getTextContent().equals(reqTextMessageText) == false) {
			fail("Request text not valid: "+reqmsgelem.getTextContent());
		}
		
		// Validate response received from action
		if (resp.getBody().get().equals(respTextMessage) == false) {
			fail("Response not as expected '"+respTextMessage+
						"', but got: "+resp.getBody().get());
		}
	}
	
	/**
	 * This will test the case where the text message passed to the invoke contains the full multipart
	 * message. The response is also treated as complete, and returned as text.
	 */
	public void testUnnamedPartText() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+MESSAGE+"><"+REQ_TEST_PART+"><"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+
								"</"+TEST_MESSAGE_ELEM+"></"+REQ_TEST_PART+"></"+MESSAGE+">";
			
		// Message to be returned from invoke
		String respTextMessageText="Hello World Response";
		String respMessage="<"+MESSAGE+"><"+RESP_TEST_PART+"><"+TEST_MESSAGE_ELEM+">"+respTextMessageText+
							"</"+TEST_MESSAGE_ELEM+"></"+RESP_TEST_PART+"></"+MESSAGE+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine(respMessage);
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp == null) {
			fail("Response expected");
		}
		
		// Validate request received by engine
		if (bpelEngine.getRequest() == null) {
			fail("Request not set");
		}
		
		org.w3c.dom.Element req=bpelEngine.getRequest();
		
		if (req.getNodeName().equals(MESSAGE) == false) {
			fail("Request top level node is not message: "+req.getNodeName());
		}
		
		org.w3c.dom.Element reqpart=getElement(req, REQ_TEST_PART);
		
		if (reqpart == null) {
			fail("Request does not have test part");
		}
		
		org.w3c.dom.Element reqmsgelem=getElement(reqpart, TEST_MESSAGE_ELEM);
		
		if (reqmsgelem == null) {
			fail("Request does not have test message element");
		}
		
		if (reqmsgelem.getTextContent().equals(reqTextMessageText) == false) {
			fail("Request text not valid: "+reqmsgelem.getTextContent());
		}
		
		// Validate response received from action
		if (resp.getBody().get().equals(respMessage) == false) {
			fail("Response not as expected '"+respMessage+
						"', but got: "+resp.getBody().get());
		}
	}
	
	/**
	 * This test supplies the part content in DOM element form, so the action needs to add the
	 * configured part name before invoking the process, and similarly remove the part
	 * name from the response and return as DOM element.
	 */
	public void testNamedPartDOMElement() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.REQUEST_PART_NAME, REQ_TEST_PART);
		config.setAttribute(BPELInvoke.RESPONSE_PART_NAME, RESP_TEST_PART);
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
			
		// Message to be returned from invoke
		String respTextMessageText="Hello World Response";
		String respTextMessage="<"+TEST_MESSAGE_ELEM+">"+respTextMessageText+"</"+TEST_MESSAGE_ELEM+">";			
		String respMessage="<"+MESSAGE+"><"+RESP_TEST_PART+">"+respTextMessage+
					"</"+RESP_TEST_PART+"></"+MESSAGE+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine(respMessage);
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(BPELInvoke.getNode(reqTextMessage));
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp == null) {
			fail("Response expected");
		}
		
		// Validate request received by engine
		if (bpelEngine.getRequest() == null) {
			fail("Request not set");
		}
		
		org.w3c.dom.Element req=bpelEngine.getRequest();
		
		if (req.getNodeName().equals(MESSAGE) == false) {
			fail("Request top level node is not message: "+req.getNodeName());
		}
		
		org.w3c.dom.Element reqpart=getElement(req, REQ_TEST_PART);
		
		if (reqpart == null) {
			fail("Request does not have test part");
		}
		
		org.w3c.dom.Element reqmsgelem=getElement(reqpart, TEST_MESSAGE_ELEM);
		
		if (reqmsgelem == null) {
			fail("Request does not have test message element");
		}
		
		if (reqmsgelem.getTextContent().equals(reqTextMessageText) == false) {
			fail("Request text not valid: "+reqmsgelem.getTextContent());
		}
		
		// Validate response received from action
		if ((resp.getBody().get() instanceof org.w3c.dom.Element) == false) {
			fail("Response was not a DOM element");
		}
		
		try {
			String respText=BPELInvoke.getText((org.w3c.dom.Element)resp.getBody().get());
			
			if (respText.equals(respTextMessage) == false) {
				fail("Response not as expected '"+respTextMessage+
							"', but got: "+respText);
			}
		} catch(Exception e) {
			fail("Failed to validate response text: "+e);
		}
	}
	
	/**
	 * This will test the case where the text message passed to the invoke contains the full multipart
	 * message, but specified using DOM element. The response equally represents the complete multipart
	 * message to be returned in DOM form.
	 */
	public void testUnnamedPartDOMElement() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+MESSAGE+"><"+REQ_TEST_PART+"><"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+
								"</"+TEST_MESSAGE_ELEM+"></"+REQ_TEST_PART+"></"+MESSAGE+">";
			
		// Message to be returned from invoke
		String respTextMessageText="Hello World Response";
		String respMessage="<"+MESSAGE+"><"+RESP_TEST_PART+"><"+TEST_MESSAGE_ELEM+">"+respTextMessageText+
							"</"+TEST_MESSAGE_ELEM+"></"+RESP_TEST_PART+"></"+MESSAGE+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine(respMessage);
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(BPELInvoke.getNode(reqTextMessage));
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp == null) {
			fail("Response expected");
		}
		
		// Validate request received by engine
		if (bpelEngine.getRequest() == null) {
			fail("Request not set");
		}
		
		org.w3c.dom.Element req=bpelEngine.getRequest();
		
		if (req.getNodeName().equals(MESSAGE) == false) {
			fail("Request top level node is not message: "+req.getNodeName());
		}
		
		org.w3c.dom.Element reqpart=getElement(req, REQ_TEST_PART);
		
		if (reqpart == null) {
			fail("Request does not have test part");
		}
		
		org.w3c.dom.Element reqmsgelem=getElement(reqpart, TEST_MESSAGE_ELEM);
		
		if (reqmsgelem == null) {
			fail("Request does not have test message element");
		}
		
		if (reqmsgelem.getTextContent().equals(reqTextMessageText) == false) {
			fail("Request text not valid: "+reqmsgelem.getTextContent());
		}
		
		// Validate response received from action
		if ((resp.getBody().get() instanceof org.w3c.dom.Element) == false) {
			fail("Response was not a DOM element");
		}
		
		try {
			String respText=BPELInvoke.getText((org.w3c.dom.Element)resp.getBody().get());
			
			if (respText.equals(respMessage) == false) {
				fail("Response not as expected '"+respMessage+
							"', but got: "+respText);
			}
		} catch(Exception e) {
			fail("Failed to validate response text: "+e);
		}
	}

	/**
	 * This test checks whether a null response is handled.
	 */
	public void testNamedPartRequestOnly() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.REQUEST_PART_NAME, REQ_TEST_PART);
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine();
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp != null) {
			fail("Response NOT expected");
		}
	}
	
	/**
	 * This test checks that missing operation causes failure.
	 */
	public void testMissingOperation() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
		
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine();
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			try {
				invoke.process(mesg);
				fail("Exception should have been thrown as operation not specified");
			} catch(Exception inner) {
				// Ok
			}
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
	}
	
	/**
	 * This test checks that missing service causes failure.
	 */
	public void testMissingService() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
		
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine();
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			try {
				invoke.process(mesg);
				fail("Exception should have been thrown as service not specified");
			} catch(Exception inner) {
				// Ok
			}
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
	}
	
	/**
	 * This test attempts to invoke a BPEL process using invalid XML text
	 */
	public void testInvalidRequestXMLText() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"<"+TEST_MESSAGE_ELEM+">";
		
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine();
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			try {
				invoke.process(mesg);
				fail("Exception should have been thrown as XML is invalid");
			} catch(Exception inner) {
				// Ok
			}
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
	}
	
	/**
	 * This test attempts to invoke a BPEL process using a text node
	 * with no part name. Text node could only be specified if it was
	 * contained in a named part of a multi-part message, so should
	 * result in exception.
	 */
	public void testInvalidRequestTextNoPartName() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine();
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessageText);
			
			try {
				invoke.process(mesg);
				fail("Exception should have been thrown as message is just text, with no part name defined");
			} catch(Exception inner) {
				// Ok
			}
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
	}
	
	/**
	 * This test supplies the part content in text form, but gets the response part name incorrect,
	 * so should get a null response.
	 */
	public void testNamedPartTextWithIncorrectResponsePartName() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.REQUEST_PART_NAME, REQ_TEST_PART);
		config.setAttribute(BPELInvoke.RESPONSE_PART_NAME, RESP_TEST_PART+"XXX");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message
		String reqTextMessageText="Hello World Request";
		String reqTextMessage="<"+TEST_MESSAGE_ELEM+">"+reqTextMessageText+"</"+TEST_MESSAGE_ELEM+">";
			
		// Message to be returned from invoke
		String respTextMessageText="Hello World Response";
		String respTextMessage="<"+TEST_MESSAGE_ELEM+">"+respTextMessageText+"</"+TEST_MESSAGE_ELEM+">";			
		String respMessage="<"+MESSAGE+"><"+RESP_TEST_PART+">"+respTextMessage+
					"</"+RESP_TEST_PART+"></"+MESSAGE+">";
		
		Message resp=null;
		TestBPELEngine bpelEngine=null;

		try {		
			bpelEngine=new TestBPELEngine(respMessage);
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add(reqTextMessage);
			
			resp = invoke.process(mesg);
		} catch(Exception e) {
			fail("Failed processing message: "+e);
		}
		
		if (resp == null) {
			fail("Response message expected");
		}
				
		// Validate response received from action
		if (resp.getBody().get() != null) {
			fail("Response value NOT expected: "+resp.getBody().get());
		}
	}
	
	public void testFaultAsException() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		
		// Request message		
		TestBPELEngine bpelEngine=null;
		javax.xml.namespace.QName faultName=new javax.xml.namespace.QName("ns","lp");
		
		try {					
			bpelEngine=new TestBPELEngine(faultName, (org.w3c.dom.Element)BPELInvoke.getNode("<data/>"));
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add("<request/>");
			
			invoke.process(mesg);
			
			fail("Should have caused an exception");
			
		} catch(org.jboss.soa.esb.actions.ActionProcessingDetailFaultException faultex) {
			
			if (faultName.equals(faultex.getFaultMessage().getBody().get(BPELInvoke.BODY_FAULT_CODE)) == false) {
				fail("QNames don't match: "+faultName+" against "+
						faultex.getFaultMessage().getBody().get(BPELInvoke.BODY_FAULT_CODE));
			}
			
		} catch(Exception e) {
			fail("Failed processing fault: "+e);
		}
	}
	
	public void testFaultAsMessage() {
		
		ConfigTree config=new ConfigTree("test");
		config.setAttribute(BPELInvoke.OPERATION, TEST_OP);
		config.setAttribute(BPELInvoke.SERVICE, TEST_SERVICE);
		config.setAttribute(BPELInvoke.ABORT_ON_FAULT, "false");
		
		// Request message		
		TestBPELEngine bpelEngine=null;
		javax.xml.namespace.QName faultName=new javax.xml.namespace.QName("ns","lp");
		
		try {		
			bpelEngine=new TestBPELEngine(faultName, (org.w3c.dom.Element)BPELInvoke.getNode("<data/>"));
			
			BPELInvoke invoke=new BPELInvoke(config);
			
			invoke.setBPELEngine(bpelEngine);
			
			Message mesg=MessageFactory.getInstance().getMessage();
			
			mesg.getBody().add("<request/>");
			
			Message resp=invoke.process(mesg);
			
			// Check that response is a fault message with the correct fault code
			if (resp.getFault() == null) {
				fail("Response message should be a fault");
			}
			
			if (faultName.equals(resp.getBody().get(BPELInvoke.BODY_FAULT_CODE)) == false) {
				fail("QNames don't match: "+faultName+" against "+
						resp.getBody().get(BPELInvoke.BODY_FAULT_CODE));
			}
									
		} catch(Exception e) {
			fail("Failed processing fault: "+e);
		}
	}
	
	protected org.w3c.dom.Element getElement(org.w3c.dom.Element parent, String name) {
		org.w3c.dom.Element ret=null;
		
		org.w3c.dom.NodeList nl=parent.getChildNodes();
		for (int i=0; ret == null && i < nl.getLength(); i++) {
			if (nl.item(i).getNodeName().equals(name) && 
						nl.item(i).getNodeType() == Node.ELEMENT_NODE) {
				ret = (org.w3c.dom.Element)nl.item(i);
			}
		}
		
		return(ret);
	}
}
