Back to Repositories

Testing Admin Service Authentication Filter Workflow in Apollo Config

This test suite validates the authentication filter functionality for Apollo’s admin service, focusing on access control mechanisms and token validation. It ensures proper handling of authentication scenarios across different configuration states and token combinations.

Test Coverage Overview

The test suite provides comprehensive coverage of the AdminServiceAuthenticationFilter class, examining both enabled and disabled access control states. Key functionality tested includes:

  • Access control toggle behavior
  • Single and multiple token validation
  • Token presence/absence scenarios
  • Configuration change handling
  • Error response verification

Implementation Analysis

The testing approach utilizes Mockito for dependency mocking and behavior verification. The implementation follows a systematic pattern of setting up test scenarios, executing the filter, and verifying the expected outcomes. JUnit’s test framework features are leveraged for clear test organization and setup management through @Before annotations.

Technical Details

Testing tools and setup include:

  • JUnit 4 test framework
  • Mockito mocking framework
  • MockitoJUnitRunner for test execution
  • Mock objects for HttpServletRequest, HttpServletResponse, and FilterChain
  • BizConfig configuration mocking

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Thorough setup isolation using @Before
  • Comprehensive mock verification
  • Clear test method naming
  • Edge case coverage
  • Systematic state verification
  • Clean test organization

apolloconfig/apollo

apollo-adminservice/src/test/java/com/ctrip/framework/apollo/adminservice/filter/AdminServiceAuthenticationFilterTest.java

            
/*
 * Copyright 2024 Apollo Authors
 *
 * 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.ctrip.framework.apollo.adminservice.filter;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.ctrip.framework.apollo.biz.config.BizConfig;
import javax.servlet.FilterChain;
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.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpHeaders;

@RunWith(MockitoJUnitRunner.class)
public class AdminServiceAuthenticationFilterTest {

  @Mock
  private BizConfig bizConfig;
  private HttpServletRequest servletRequest;
  private HttpServletResponse servletResponse;
  private FilterChain filterChain;

  private AdminServiceAuthenticationFilter authenticationFilter;

  @Before
  public void setUp() throws Exception {
    authenticationFilter = new AdminServiceAuthenticationFilter(bizConfig);
    initVariables();
  }

  private void initVariables() {
    servletRequest = mock(HttpServletRequest.class);
    servletResponse = mock(HttpServletResponse.class);
    filterChain = mock(FilterChain.class);
  }

  @Test
  public void testWithAccessControlDisabled() throws Exception {
    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(false);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(bizConfig, never()).getAdminServiceAccessTokens();
    verify(servletRequest, never()).getHeader(HttpHeaders.AUTHORIZATION);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
  }

  @Test
  public void testWithAccessControlEnabledWithTokenSpecifiedWithValidTokenPassed()
      throws Exception {
    String someValidToken = "someToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someValidToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
  }

  @Test
  public void testWithAccessControlEnabledWithTokenSpecifiedWithInvalidTokenPassed()
      throws Exception {
    String someValidToken = "someValidToken";
    String someInvalidToken = "someInvalidToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someInvalidToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(servletResponse, times(1))
        .sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    verify(filterChain, never()).doFilter(servletRequest, servletResponse);
  }

  @Test
  public void testWithAccessControlEnabledWithTokenSpecifiedWithNoTokenPassed() throws Exception {
    String someValidToken = "someValidToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(null);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(servletResponse, times(1))
        .sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    verify(filterChain, never()).doFilter(servletRequest, servletResponse);
  }


  @Test
  public void testWithAccessControlEnabledWithMultipleTokenSpecifiedWithValidTokenPassed()
      throws Exception {
    String someToken = "someToken";
    String anotherToken = "anotherToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens())
        .thenReturn(String.format("%s,%s", someToken, anotherToken));
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
  }

  @Test
  public void testWithAccessControlEnabledWithNoTokenSpecifiedWithTokenPassed() throws Exception {
    String someToken = "someToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(null);
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
  }

  @Test
  public void testWithAccessControlEnabledWithNoTokenSpecifiedWithNoTokenPassed() throws Exception {
    String someToken = "someToken";

    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(null);
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(null);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
    verify(bizConfig, times(1)).getAdminServiceAccessTokens();
    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
  }

  @Test
  public void testWithConfigChanged() throws Exception {
    String someToken = "someToken";
    String anotherToken = "anotherToken";
    String yetAnotherToken = "yetAnotherToken";

    // case 1: init state
    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
    when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someToken);

    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());

    // case 2: change access tokens specified
    initVariables();
    when(bizConfig.getAdminServiceAccessTokens())
        .thenReturn(String.format("%s,%s", anotherToken, yetAnotherToken));
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(servletResponse, times(1))
        .sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    verify(filterChain, never()).doFilter(servletRequest, servletResponse);

    initVariables();
    when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(anotherToken);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());

    // case 3: change access control flag
    initVariables();
    when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(false);

    authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

    verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
    verify(servletResponse, never()).sendError(anyInt(), anyString());
    verify(servletRequest, never()).getHeader(HttpHeaders.AUTHORIZATION);
  }
}