| // Copyright (C) 2017 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.googlesource.gerrit.plugins.quota; |
| |
| import static com.googlesource.gerrit.plugins.quota.RestApiRateLimiter.SC_TOO_MANY_REQUESTS; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import com.google.common.cache.CacheBuilder; |
| import com.google.common.cache.LoadingCache; |
| import com.google.common.util.concurrent.RateLimiter; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.gerrit.server.IdentifiedUser.GenericFactory; |
| import com.google.gerrit.server.config.GerritServerConfig; |
| import com.google.gerrit.server.group.SystemGroupBackend; |
| import com.google.inject.Provider; |
| import com.googlesource.gerrit.plugins.quota.AccountLimitsConfig.Type; |
| import com.googlesource.gerrit.plugins.quota.Module.Holder; |
| import java.io.IOException; |
| import java.util.concurrent.ExecutionException; |
| import javax.servlet.FilterChain; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import org.eclipse.jgit.lib.Config; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Answers; |
| import org.mockito.Mock; |
| import org.mockito.junit.MockitoJUnitRunner; |
| |
| @RunWith(MockitoJUnitRunner.class) |
| public class RestApiRateLimiterTest { |
| private static final String LIMIT_EXCEEDED_MSG = |
| "test exceeded message: {0,number,##.##}, {1,number,###}"; |
| private static final String REMOTE_HOST = "host"; |
| @Mock private HttpServletRequest req; |
| @Mock private HttpServletResponse res; |
| @Mock private FilterChain chain; |
| @Mock private Provider<CurrentUser> user; |
| |
| @Mock(answer = Answers.RETURNS_DEEP_STUBS) |
| private CurrentUser currentUser; |
| |
| @Mock private Account.Id accountId; |
| |
| @Mock(answer = Answers.RETURNS_DEEP_STUBS) |
| private Holder holder; |
| |
| @Mock private RateLimiter rateLimiter; |
| |
| @Mock @GerritServerConfig Config cfg; |
| @Mock GenericFactory userFactory; |
| @Mock AccountLimitsFinder finder; |
| |
| private RestApiRateLimiter restReqFilter; |
| private SystemGroupBackend systemGroupBackend; |
| private LoadingCache<Account.Id, Holder> limitsPerAccount; |
| private LoadingCache<String, Holder> limitsPerRemoteHost; |
| |
| @Before |
| public void setUp() throws IOException, ServletException { |
| systemGroupBackend = new SystemGroupBackend(cfg); |
| |
| limitsPerAccount = |
| CacheBuilder.newBuilder() |
| .build(new Module.HolderCacheLoaderByAccountId(Type.UPLOADPACK, userFactory, finder)); |
| limitsPerAccount.put(accountId, holder); |
| |
| limitsPerRemoteHost = |
| CacheBuilder.newBuilder() |
| .build( |
| new Module.HolderCacheLoaderByRemoteHost( |
| Type.UPLOADPACK, systemGroupBackend, finder)); |
| limitsPerRemoteHost.put(REMOTE_HOST, holder); |
| |
| restReqFilter = |
| spy( |
| new RestApiRateLimiter( |
| user, limitsPerAccount, limitsPerRemoteHost, LIMIT_EXCEEDED_MSG)); |
| doReturn(true).when(restReqFilter).isRest(req); |
| when(user.get()).thenReturn(currentUser); |
| doNothing().when(chain).doFilter(req, res); |
| } |
| |
| private void setUpRegisteredUser() throws ExecutionException { |
| when(currentUser.isIdentifiedUser()).thenReturn(true); |
| when(currentUser.asIdentifiedUser().getAccountId()).thenReturn(accountId); |
| } |
| |
| private void setUpAnonymous() throws ExecutionException { |
| when(currentUser.isIdentifiedUser()).thenReturn(false); |
| when(req.getRemoteHost()).thenReturn(REMOTE_HOST); |
| } |
| |
| private void setUpNoQuotaViolation1() { |
| when(holder.hasGracePermits()).thenReturn(false); |
| when(holder.get()).thenReturn(rateLimiter); |
| when(holder.get().tryAcquire()).thenReturn(true); |
| } |
| |
| private void setUpNoQuotaViolation2() { |
| when(holder.hasGracePermits()).thenReturn(true); |
| } |
| |
| private void setUpQuotaViolation() { |
| when(holder.hasGracePermits()).thenReturn(false); |
| when(holder.get()).thenReturn(rateLimiter); |
| when(holder.get().tryAcquire()).thenReturn(false); |
| } |
| |
| @Test |
| public void testDoFilterQuotaViolation() |
| throws IOException, ServletException, ExecutionException { |
| setUpRegisteredUser(); |
| setUpQuotaViolation(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| } |
| |
| @Test |
| public void testDoFilterNoQuotaViolation() |
| throws IOException, ServletException, ExecutionException { |
| setUpRegisteredUser(); |
| setUpNoQuotaViolation1(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res, times(0)).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| setUpNoQuotaViolation2(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res, times(0)).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| } |
| |
| @Test |
| public void testDoFilterAnonymQuotaViolation() |
| throws IOException, ServletException, ExecutionException { |
| setUpAnonymous(); |
| setUpQuotaViolation(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| } |
| |
| @Test |
| public void testDoFilterAnonymNoQuotaViolation() |
| throws IOException, ServletException, ExecutionException { |
| setUpAnonymous(); |
| setUpNoQuotaViolation1(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res, times(0)).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| setUpNoQuotaViolation2(); |
| restReqFilter.doFilter(req, res, chain); |
| verify(res, times(0)).sendError(eq(SC_TOO_MANY_REQUESTS), anyString()); |
| } |
| } |