blob: 64fe0c7887bc6f6d22ccc20ecc08da0673d162ac [file] [log] [blame]
// 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.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Provider;
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.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,###}";
@Mock private HttpServletRequest req;
@Mock private HttpServletResponse res;
@Mock private FilterChain chain;
@Mock private Provider<CurrentUser> user;
@Mock private LoadingCache<Account.Id, Holder> limitsPerAccount;
@Mock private LoadingCache<String, Holder> limitsPerRemoteHost;
@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;
private RestApiRateLimiter restReqFilter;
@Before
public void setUp() throws IOException, ServletException {
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);
when(limitsPerAccount.get(accountId)).thenReturn(holder);
}
private void setUpRegisteredUserExecutionException() throws ExecutionException {
when(currentUser.isIdentifiedUser()).thenReturn(true);
when(currentUser.asIdentifiedUser().getAccountId()).thenReturn(accountId);
when(limitsPerAccount.get(accountId)).thenThrow(new ExecutionException(null));
when(accountId.toString()).thenReturn("123");
}
private void setUpAnonymous() throws ExecutionException {
when(currentUser.isIdentifiedUser()).thenReturn(false);
when(req.getRemoteHost()).thenReturn("host");
when(limitsPerRemoteHost.get("host")).thenReturn(holder);
}
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 testDoFilterCacheMiss() throws IOException, ServletException, ExecutionException {
setUpRegisteredUserExecutionException();
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());
}
}