package eu.dnetlib.contract.cp.eval;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;

import eu.dnetlib.contract.cp.CheckPointEvaluationException;
import eu.dnetlib.contract.cp.ExceptionCheckPoint;
import eu.dnetlib.contract.event.ExceptionContractEvent;
import eu.dnetlib.contract.node.EvaluationResult;
import eu.dnetlib.contract.node.EvaluationResult.Status;


/**
 * Exception CheckPoint evaluation tests
 * @author mhorst
 *
 */
public class ExceptionCheckPointEvaluationTest {

	ExceptionCheckPoint checkPoint = null;
	JoinPoint joinPoint = null;
	
	private Mockery context;
	
//	target method props
	String className = "java.lang.String";
	String methodName = "compareTo";
	String stringValue = "stringValue";
	String anotherString = "anotherStringValue";
//	exception props
	String exceptionClassName = CheckPointEvaluationException.class.getName();
	String exceptionMessage = "some exception message";
	
	@Before
	public void init() {
		context = new Mockery();
		joinPoint = context.mock(JoinPoint.class);
		checkPoint = getValidCheckPoint();
	}
	
	@Test
	public void testEvaluateWithNullJoinPoint() throws Exception {
		joinPoint = null;
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FATAL,result.getStatus());
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldTargetObject() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);

		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		
//		setting invalid target object
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new Integer(0)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
		
		
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldMethodName() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
//		setting invalid method name
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue("invalidMethodName"));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadClassName() throws Exception {
//		setting invalid className
		checkPoint.setClassName("invalidClassName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadMethodName() throws Exception {
//		setting invalid className
		checkPoint.setMethodName("invalidMethodName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateNullException() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint, null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidExceptionBadClass() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		Exception ex = new RuntimeException();
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint, ex));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidExceptionBadMessage() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		Exception ex = new CheckPointEvaluationException("someBadMessage");
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint, ex));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidExceptionBadDerivedClass() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		Exception ex = new CheckPointEvaluationException(exceptionMessage);
		checkPoint.setExceptionDerivedFrom(NullPointerException.class);
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint, ex));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateValidException() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		Exception ex = new CheckPointEvaluationException(exceptionMessage);
		checkPoint.setExceptionDerivedFrom(Exception.class);
		EvaluationResult result = checkPoint.check(new ExceptionContractEvent(joinPoint, ex));
		assertNotNull(result);
		assertEquals(Status.OK,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	ExceptionCheckPoint getValidCheckPoint() {
		ExceptionCheckPoint checkPoint = new ExceptionCheckPoint();
		checkPoint.setClassName(className);
		checkPoint.setMethodName(methodName);
		checkPoint.setExceptionClassName(exceptionClassName);
		checkPoint.setExceptionMessage(exceptionMessage);
		return checkPoint;
	}
}
