package com.ruoyi.framework.aspectj;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.Test;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.core.cache.InMemoryCacheStore;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;

class RateLimiterAspectTest
{
    @Test
    void shouldRejectThirdRequestWithinWindow() throws Throwable
    {
        RateLimiterAspect aspect = new RateLimiterAspect(new RedisCache(new InMemoryCacheStore()));
        JoinPoint joinPoint = mockJoinPoint(TestRateLimitTarget.class.getDeclaredMethod("limited"));
        RateLimiter rateLimiter = TestRateLimitTarget.class.getDeclaredMethod("limited").getAnnotation(RateLimiter.class);

        assertDoesNotThrow(() -> aspect.doBefore(joinPoint, rateLimiter));
        assertDoesNotThrow(() -> aspect.doBefore(joinPoint, rateLimiter));
        assertThrows(ServiceException.class, () -> aspect.doBefore(joinPoint, rateLimiter));
    }

    private JoinPoint mockJoinPoint(Method method)
    {
        MethodSignature signature = mock(MethodSignature.class);
        when(signature.getMethod()).thenReturn(method);

        JoinPoint joinPoint = mock(JoinPoint.class);
        when(joinPoint.getSignature()).thenReturn((Signature) signature);
        return joinPoint;
    }

    static class TestRateLimitTarget
    {
        @RateLimiter(count = 2, time = 60)
        public void limited()
        {
        }
    }
}
