package com.smartertravel.metrics.aop;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.smartertravel.metrics.aop.TimingAspect.DefaultKeyGenerator;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@RunWith(MockitoJUnitRunner.class)
public class TimingAspectTest {

    interface UserDao {
        @SuppressWarnings("unused")
        boolean userExists(String id);
    }

    static class UserDaoMysql implements UserDao {

        @Timed("mysqlDao.userExists")
        @Override
        public boolean userExists(String id) {
            return false;
        }
    }

    static class UserDaoHystrix implements UserDao {

        @Timed
        @Override
        public boolean userExists(String id) {
            return false;
        }
    }

    @Mock
    private ProceedingJoinPoint joinPoint;

    @Mock
    private Signature signature;

    @Mock
    private UserDao userDao;

    @Mock
    private MetricRegistry metricRegistry;

    @Mock
    private Timer timer;

    @Test
    public void testDefaultKeyGeneratorGetKeyAnnotationValue() throws NoSuchMethodException {
        final UserDaoMysql dao = new UserDaoMysql();
        final Method method = dao.getClass().getMethod("userExists", String.class);
        final Timed[] annotations = method.getAnnotationsByType(Timed.class);

        final DefaultKeyGenerator keyGenerator = new DefaultKeyGenerator();
        assertEquals("mysqlDao.userExists", keyGenerator.getKey(joinPoint, dao, annotations[0]));
    }

    @Test
    public void testDefaultKeyGeneratorGetKeyDerivedValue() throws NoSuchMethodException {
        final UserDaoHystrix dao = new UserDaoHystrix();
        final Method method = dao.getClass().getMethod("userExists", String.class);
        final Timed[] annotations = method.getAnnotationsByType(Timed.class);

        when(joinPoint.getSignature()).thenReturn(signature);
        when(signature.getName()).thenReturn("userExists");

        final DefaultKeyGenerator keyGenerator = new DefaultKeyGenerator();
        assertEquals("UserDaoHystrix.userExists", keyGenerator.getKey(joinPoint, dao, annotations[0]));
    }

    @Test
    public void testPerformanceLog() throws Throwable {
        final UserDaoHystrix dao = new UserDaoHystrix();
        final Method method = dao.getClass().getMethod("userExists", String.class);
        final Timed[] annotations = method.getAnnotationsByType(Timed.class);

        when(joinPoint.proceed()).thenReturn(true);
        when(joinPoint.getSignature()).thenReturn(signature);
        when(signature.getName()).thenReturn("userExists");
        when(metricRegistry.timer(eq("timer.UserDaoHystrix.userExists"))).thenReturn(timer);

        final TimingAspect aspect = new TimingAspect(metricRegistry);
        final Object result = aspect.performanceLog(joinPoint, dao, annotations[0]);

        assertNotNull("Expected non-null result type", result);
        assertTrue("Expected boolean return type from aspect, was " + result.getClass().getName(), result instanceof Boolean);
        verify(timer).update(anyLong(), eq(TimeUnit.NANOSECONDS));
    }
}

@@ -392,16 +392,44 @@
 unner;%0A%0A
+import java.io.IOException;%0A
 import j
@@ -3575,11 +3575,1002 @@
 );%0A    %7D
+%0A%0A    @Test%0A    public void testPerformanceLogWithException() throws Throwable %7B%0A        final UserDaoHystrix dao = new UserDaoHystrix();%0A        final Method method = dao.getClass().getMethod(%22userExists%22, String.class);%0A        final Timed%5B%5D annotations = method.getAnnotationsByType(Timed.class);%0A%0A        when(joinPoint.proceed()).thenThrow(IOException.class);%0A        when(joinPoint.getSignature()).thenReturn(signature);%0A        when(signature.getName()).thenReturn(%22userExists%22);%0A        when(metricRegistry.timer(eq(%22timer.UserDaoHystrix.userExists%22))).thenReturn(timer);%0A%0A        final TimingAspect aspect = new TimingAspect(metricRegistry);%0A        IOException err = null;%0A%0A        try %7B%0A            aspect.performanceLog(joinPoint, dao, annotations%5B0%5D);%0A        %7D catch (IOException e) %7B%0A            err = e;%0A        %7D%0A%0A        assertNotNull(%22Expected exception to be raised while calling join point%22, err);%0A        verify(timer).update(anyLong(), eq(TimeUnit.NANOSECONDS));%0A    %7D
 %0A%7D%0A

Add test to ensure timings are still recorded when exceptions are raised by the wrapped method