package org.slc.sli.ingestion.util;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.rmi.AccessException;
import java.rmi.RemoteException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * LogUtil unit-tests
 *
 * @author tshewchuk
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring/applicationContext-test.xml" })
public class LogUtilTest {

    @Test
    public void testLogUtil() {
        // Log a nested exception.
        final Logger mockLogger = Mockito.mock(Logger.class);
        Mockito.when(mockLogger.isErrorEnabled()).thenReturn(true);
        try {
            throw new RemoteException("*** SENSITIVE DATA ONE!!! ***");
        } catch (RemoteException re1) {
            try {
                throw new RuntimeException("*** SENSITIVE DATA TWO!!! ***", re1);
            } catch (RuntimeException re2) {
                try {
                    throw new AccessException("*** SENSITIVE DATA THREE!!! ***", re2);
                } catch (AccessException ae) {
                    String message = "This is a test of the LogUtil utility";
                    LogUtil.error(mockLogger, message, ae);

                    // Now test what would be logged.
                    Mockito.verify(mockLogger).error(Mockito.eq(message), Mockito.argThat(new IsCorrectException()));
                }
            }
        }

    }

    private class IsCorrectException extends ArgumentMatcher<Exception> {

        @Override
        public boolean matches(Object argument) {
            Exception arg = (Exception) argument;

            // Verify the logged contents.
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(baos);
            arg.printStackTrace(ps);
            String logContents = baos.toString(); // e.g. ISO-8859-1

            // Verify log file does NOT contain exception local message.
            if ((logContents.contains("*** SENSITIVE DATA THREE!!! ***"))
                    || (logContents.contains("*** SENSITIVE DATA TWO!!! ***"))
                    || (logContents.contains("*** SENSITIVE DATA ONE!!! ***"))) {
                return false;
            }

            // Verify log file DOES contain error message with nested exception types and stack
            // traces.
            if ((!logContents.contains("java.lang.Exception: class java.rmi.AccessException"))
                    || (!logContents
                            .contains("at org.slc.sli.ingestion.util.LogUtilTest.testLogUtil(LogUtilTest.java:38)"))
                    || (!logContents.contains("Caused by: java.lang.Exception: class java.lang.RuntimeException"))
                    || (!logContents
                            .contains("at org.slc.sli.ingestion.util.LogUtilTest.testLogUtil(LogUtilTest.java:35)"))
                    || (!logContents.contains("... 28 "))
                    || (!logContents.contains("Caused by: java.lang.Exception: class java.rmi.RemoteException"))
                    || (!logContents
                            .contains("at org.slc.sli.ingestion.util.LogUtilTest.testLogUtil(LogUtilTest.java:32)"))) {
                return false;
            }

            return true;
        }
    };

}

@@ -161,16 +161,48 @@
 ception;
+%0Aimport java.util.regex.Pattern;
 %0A%0Aimport
@@ -503,16 +503,17 @@
 tests%0A *
+ 
 %0A * @aut
@@ -528,16 +528,17 @@
 wchuk%0A *
+ 
 %0A */%0A@Ru
@@ -678,16 +678,20 @@
 lTest %7B%0A
+    
 %0A    @Te
@@ -1455,16 +1455,36 @@
 e, ae);%0A
+                    
 %0A       
@@ -1694,15 +1694,27 @@
   %7D%0A
+        
 %0A    %7D%0A
+    
 %0A   
@@ -1784,16 +1784,24 @@
 tion%3E %7B%0A
+        
 %0A       
@@ -1911,16 +1911,28 @@
 gument;%0A
+            
 %0A       
@@ -2195,16 +2195,28 @@
 -8859-1%0A
+            
 %0A       
@@ -2296,17 +2296,16 @@
      if 
-(
 (logCont
@@ -2344,33 +2344,32 @@
 A THREE!!! ***%22)
-)
 %0A               
@@ -2368,33 +2368,32 @@
              %7C%7C 
-(
 logContents.cont
@@ -2429,17 +2429,16 @@
 !! ***%22)
-)
 %0A       
@@ -2453,17 +2453,16 @@
      %7C%7C 
-(
 logConte
@@ -2507,17 +2507,16 @@
 ! ***%22))
-)
  %7B%0A     
@@ -2546,32 +2546,143 @@
 ;%0A            %7D%0A
+            %0A            Pattern p = Pattern.compile(%22.*%5C%5C.%5C%5C.%5C%5C. %5B%5C%5Cd%5D* more.*%22, Pattern.DOTALL);%0A            
 %0A            // 
@@ -2800,17 +2800,16 @@
      if 
-(
 (!logCon
@@ -2869,33 +2869,32 @@
 ccessException%22)
-)
 %0A               
@@ -2893,33 +2893,32 @@
              %7C%7C 
-(
 !logContents%0A   
@@ -3029,13 +3029,12 @@
 va:3
-8
+9
 )%22)
-)
 %0A   
@@ -3045,33 +3045,32 @@
              %7C%7C 
-(
 !logContents.con
@@ -3134,33 +3134,32 @@
 ntimeException%22)
-)
 %0A               
@@ -3158,33 +3158,32 @@
              %7C%7C 
-(
 !logContents%0A   
@@ -3294,13 +3294,12 @@
 va:3
-5
+6
 )%22)
-)
 %0A   
@@ -3322,41 +3322,40 @@
  %7C%7C 
-(!logContents.contains(%22... 28 %22)
+!p.matcher(logContents).matches(
 )%0A  
@@ -3367,33 +3367,32 @@
              %7C%7C 
-(
 !logContents.con
@@ -3462,17 +3462,16 @@
 eption%22)
-)
 %0A       
@@ -3486,17 +3486,16 @@
      %7C%7C 
-(
 !logCont
@@ -3614,14 +3614,13 @@
 va:3
-2
+3
 )%22))
-)
  %7B%0A 
@@ -3662,16 +3662,28 @@
       %7D%0A
+            
 %0A       
@@ -3717,11 +3717,15 @@
 %0A    %7D;%0A
+    
 %0A%7D%0A

Update the LogUtilTest to be forgiving of different stack depths when using different logging implementations.