content: /*
   * Copyright (c) 2010. All rights reserved.
   */
  package ro.isdc.wro.http;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertSame;
  import static org.junit.Assert.assertTrue;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
  import static ro.isdc.wro.http.handler.ResourceProxyRequestHandler.PARAM_RESOURCE_ID;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.PrintWriter;
  import java.util.Collection;
  import java.util.Properties;
  import java.util.concurrent.Callable;
  
  import javax.management.MBeanServer;
  import javax.management.ObjectName;
  import javax.servlet.FilterChain;
  import javax.servlet.FilterConfig;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletException;
  import javax.servlet.ServletOutputStream;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpServletResponseWrapper;
  
  import org.junit.After;
  import org.junit.AfterClass;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.mockito.Mock;
  import org.mockito.Mockito;
  import org.mockito.MockitoAnnotations;
  
  import ro.isdc.wro.WroRuntimeException;
  import ro.isdc.wro.cache.CacheKey;
  import ro.isdc.wro.cache.CacheStrategy;
  import ro.isdc.wro.cache.CacheValue;
  import ro.isdc.wro.config.Context;
  import ro.isdc.wro.config.factory.FilterConfigWroConfigurationFactory;
  import ro.isdc.wro.config.factory.PropertiesAndFilterConfigWroConfigurationFactory;
  import ro.isdc.wro.config.factory.PropertyWroConfigurationFactory;
  import ro.isdc.wro.config.jmx.ConfigConstants;
  import ro.isdc.wro.config.jmx.WroConfiguration;
  import ro.isdc.wro.http.handler.ReloadCacheRequestHandler;
  import ro.isdc.wro.http.handler.ReloadModelRequestHandler;
  import ro.isdc.wro.http.handler.RequestHandler;
  import ro.isdc.wro.http.handler.ResourceProxyRequestHandler;
  import ro.isdc.wro.http.handler.factory.RequestHandlerFactory;
  import ro.isdc.wro.http.support.DelegatingServletOutputStream;
  import ro.isdc.wro.http.support.UnauthorizedRequestException;
  import ro.isdc.wro.manager.factory.BaseWroManagerFactory;
  import ro.isdc.wro.manager.factory.ConfigurableWroManagerFactory;
  import ro.isdc.wro.manager.factory.DefaultWroManagerFactory;
  import ro.isdc.wro.manager.factory.WroManagerFactory;
  import ro.isdc.wro.model.WroModel;
  import ro.isdc.wro.model.factory.WroModelFactory;
  import ro.isdc.wro.model.factory.XmlModelFactory;
  import ro.isdc.wro.model.group.Group;
  import ro.isdc.wro.model.group.InvalidGroupNameException;
  import ro.isdc.wro.model.group.processor.Injector;
  import ro.isdc.wro.model.group.processor.InjectorBuilder;
  import ro.isdc.wro.model.resource.locator.UriLocator;
  import ro.isdc.wro.model.resource.locator.factory.UriLocatorFactory;
  import ro.isdc.wro.model.resource.locator.support.DispatcherStreamLocator;
  import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
  import ro.isdc.wro.model.resource.processor.impl.css.CssMinProcessor;
  import ro.isdc.wro.model.resource.support.ResourceAuthorizationManager;
  import ro.isdc.wro.util.AbstractDecorator;
  import ro.isdc.wro.util.ObjectFactory;
  import ro.isdc.wro.util.WroTestUtils;
  import ro.isdc.wro.util.WroUtil;
  import ro.isdc.wro.util.io.NullOutputStream;
  
  
  /**
   * Test for {@link WroFilter} class.
   *
   * @author Alex Objelean
   * @created Created on Jul 13, 2009
   */
  public class TestWroFilter {
    private WroFilter victim;
    @Mock
    private FilterConfig mockFilterConfig;
    @Mock
    private HttpServletRequest mockRequest;
    @Mock
    private HttpServletResponse mockResponse;
    @Mock
    private FilterChain mockFilterChain;
    @Mock
    private ServletContext mockServletContext;
    @Mock
    private WroManagerFactory mockManagerFactory;
    @Mock
    private ResourceAuthorizationManager mockAuthorizationManager;
    @Mock
    private UriLocatorFactory mockUriLocatorFactory;
    @Mock
    private MBeanServer mockMBeanServer;
    @Mock
    private UriLocator mockUriLocator;
  
    @BeforeClass
    public static void onBeforeClass() {
      assertEquals(0, Context.countActive());
    }
  
    @AfterClass
    public static void onAfterClass() {
      assertEquals(0, Context.countActive());
    }
  
    @Before
    public void setUp()
        throws Exception {
      Context.set(Context.standaloneContext());
      MockitoAnnotations.initMocks(this);
  
      when(mockUriLocatorFactory.getInstance(Mockito.anyString())).thenReturn(mockUriLocator);
      when(mockUriLocator.locate(Mockito.anyString())).thenReturn(WroUtil.EMPTY_STREAM);
      when(mockUriLocatorFactory.locate(Mockito.anyString())).thenReturn(WroUtil.EMPTY_STREAM);
  
      when(mockRequest.getAttribute(Mockito.anyString())).thenReturn(null);
      when(mockManagerFactory.create()).thenReturn(new BaseWroManagerFactory().create());
      when(mockFilterConfig.getServletContext()).thenReturn(mockServletContext);
      when(mockResponse.getOutputStream()).thenReturn(new DelegatingServletOutputStream(new NullOutputStream()));
  
      victim = new WroFilter() {
        @Override
        protected void onException(final Exception e, final HttpServletResponse response, final FilterChain chain) {
          throw WroRuntimeException.wrap(e);
        }
  
        @Override
        protected MBeanServer getMBeanServer() {
          return mockMBeanServer;
        }
      };
      victim.setWroManagerFactory(mockManagerFactory);
    }
  
    private WroManagerFactory createValidManagerFactory() {
      return new BaseWroManagerFactory().setModelFactory(createValidModelFactory());
    }
  
    private WroModelFactory createValidModelFactory() {
      return new XmlModelFactory() {
        @Override
        protected InputStream getModelResourceAsStream() {
          return TestWroFilter.class.getResourceAsStream("wro.xml");
        }
      };
    }
  
    private void initChainOnErrorFilter()
        throws ServletException {
      victim = new WroFilter();
      victim.init(mockFilterConfig);
    }
  
    /**
     * Set filter init params with proper values and check they are the same in {@link WroConfiguration} object.
     */
    @Test(expected = WroRuntimeException.class)
    public void testFilterInitParamsAreWrong()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.cacheUpdatePeriod.name())).thenReturn("InvalidNumber");
      when(mockFilterConfig.getInitParameter(ConfigConstants.modelUpdatePeriod.name())).thenReturn("100");
      victim.init(mockFilterConfig);
    }
  
    @Test(expected = WroRuntimeException.class)
    public void cannotAcceptInvalidAppFactoryClassNameIsSet()
        throws Exception {
      victim = new WroFilter();
      when(mockFilterConfig.getInitParameter(ConfigConstants.managerFactoryClassName.name())).thenReturn("Invalid value");
      victim.init(mockFilterConfig);
    }
  
    @Test
    public void shouldUseInitiallySetManagerEvenIfAnInvalidAppFactoryClassNameIsSet()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.managerFactoryClassName.name())).thenReturn("Invalid value");
      victim.init(mockFilterConfig);
  
      Assert.assertSame(mockManagerFactory, AbstractDecorator.getOriginalDecoratedObject(victim.getWroManagerFactory()));
    }
  
    /**
     * Test that in DEPLOYMENT mode if {@link InvalidGroupNameException} is thrown, the response redirect to 404.
     */
    @Test
    public void testInvalidGroupNameExceptionThrownInDEPLOYMENTMode()
        throws Exception {
      testChainContinueWhenSpecificExceptionThrown(new InvalidGroupNameException(""));
    }
  
    /**
     * Test that in DEPLOYMENT mode if {@link InvalidGroupNameException} is thrown, the response redirect to 404.
     */
    @Test
    public void testUnauthorizedRequestExceptionThrownInDEPLOYMENTMode()
        throws Exception {
      testChainContinueWhenSpecificExceptionThrown(new UnauthorizedRequestException(""));
    }
  
    /**
     * Test that in DEPLOYMENT mode if specified exception is thrown, the response redirect to 404.
     */
    public void testChainContinueWhenSpecificExceptionThrown(final Throwable e)
        throws Exception {
      initChainOnErrorFilter();
      when(mockManagerFactory.create()).thenThrow(e);
  
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      verifyChainIsCalled(mockFilterChain);
    }
  
    @Test
    public void testValidAppFactoryClassNameIsSet()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.managerFactoryClassName.name())).thenReturn(
          BaseWroManagerFactory.class.getName());
      victim.init(mockFilterConfig);
    }
  
    /**
     * Test that when setting WwroManagerFactory via setter, even if wroConfiguration has a different
     * {@link WroManagerFactory} configured, the first one instance is used.
     */
    @Test
    public void shouldUseCorrectWroManagerFactoryWhenOneIsSet()
        throws Exception {
      final Class<?> managerClass = TestWroManagerFactory.class;
      victim.setWroManagerFactory(null);
      when(mockFilterConfig.getInitParameter(ConfigConstants.managerFactoryClassName.name())).thenReturn(
          managerClass.getName());
  
      victim.init(mockFilterConfig);
      final Class<?> actualClass = ((DefaultWroManagerFactory) AbstractDecorator.getOriginalDecoratedObject(victim.getWroManagerFactory())).getFactory().getClass();
      assertSame(managerClass, actualClass);
    }
  
    public static class TestWroManagerFactory
        extends BaseWroManagerFactory {
    }
  
    @Test
    public void testJmxDisabled()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.jmxEnabled.name())).thenReturn("false");
      victim.init(mockFilterConfig);
    }
  
    /**
     * Set filter init params with proper values and check they are the same in {@link WroConfiguration} object.
     */
    @Test
    public void testFilterInitParamsAreSetProperly()
        throws Exception {
      setConfigurationMode(FilterConfigWroConfigurationFactory.PARAM_VALUE_DEPLOYMENT);
      when(mockFilterConfig.getInitParameter(ConfigConstants.gzipResources.name())).thenReturn("false");
      when(mockFilterConfig.getInitParameter(ConfigConstants.cacheUpdatePeriod.name())).thenReturn("10");
      when(mockFilterConfig.getInitParameter(ConfigConstants.modelUpdatePeriod.name())).thenReturn("100");
      victim.init(mockFilterConfig);
      final WroConfiguration config = victim.getConfiguration();
      assertEquals(false, config.isDebug());
      assertEquals(false, config.isGzipEnabled());
      assertEquals(10, config.getCacheUpdatePeriod());
      assertEquals(100, config.getModelUpdatePeriod());
    }
  
    @Test
    public void testValidHeaderParamIsSet()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.header.name())).thenReturn("ETag: 998989");
      victim.init(mockFilterConfig);
    }
  
    @Test
    public void testValidHeaderParamsAreSet()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.header.name())).thenReturn(
          "ETag: 998989 | Expires: Thu, 15 Apr 2010 20:00:00 GMT");
      victim.init(mockFilterConfig);
    }
  
    @Test(expected = WroRuntimeException.class)
    public void testInvalidHeaderParamIsSet()
        throws Exception {
      // this test fails only when debug is turned off.
      when(mockFilterConfig.getInitParameter(ConfigConstants.header.name())).thenReturn("ETag 998989 expires 1");
      victim.init(mockFilterConfig);
    }
  
    /**
     * Set filter init params with proper values and check they are the same in {@link WroConfiguration} object.
     */
    @Test
    public void testConfigurationInitParam()
        throws Exception {
      when(mockFilterConfig.getInitParameter(FilterConfigWroConfigurationFactory.PARAM_CONFIGURATION)).thenReturn(
          "anyOtherString");
      victim.init(mockFilterConfig);
      assertEquals(true, victim.getConfiguration().isDebug());
    }
  
    @Test
    public void testDisableCacheInitParamInDeploymentMode()
        throws Exception {
      when(mockFilterConfig.getInitParameter(FilterConfigWroConfigurationFactory.PARAM_CONFIGURATION)).thenReturn(
          FilterConfigWroConfigurationFactory.PARAM_VALUE_DEPLOYMENT);
      when(mockFilterConfig.getInitParameter(ConfigConstants.disableCache.name())).thenReturn("true");
      victim.init(mockFilterConfig);
      assertEquals(false, victim.getConfiguration().isDebug());
    }
  
    @Test
    public void testDisableCacheInitParamInDevelopmentMode()
        throws Exception {
      when(mockFilterConfig.getInitParameter(ConfigConstants.disableCache.name())).thenReturn("true");
      victim.init(mockFilterConfig);
      assertEquals(true, victim.getConfiguration().isDebug());
    }
  
    /**
     * Check what happens when the request cannot be processed and assure that the we proceed with chain.
     *
     * @throws Exception
     */
    public void cannotProcessConfigResourceStream()
        throws Exception {
      when(mockRequest.getRequestURI()).thenReturn("");
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      verifyChainIsCalled(mockFilterChain);
    }
  
    /**
     * Check if the chain call was performed.
     */
    private void verifyChainIsCalled(final FilterChain chain)
        throws IOException, ServletException {
      verify(chain, Mockito.atLeastOnce()).doFilter(Mockito.any(HttpServletRequest.class),
          Mockito.any(HttpServletResponse.class));
    }
  
    /**
     * Check if the chain call was performed.
     */
    private void verifyChainIsNotCalled(final FilterChain chain)
        throws IOException, ServletException {
      verify(chain, Mockito.never()).doFilter(Mockito.any(HttpServletRequest.class),
          Mockito.any(HttpServletResponse.class));
    }
  
    @Test
    public void cannotProcessInvalidUri()
        throws Exception {
      initChainOnErrorFilter();
      requestGroupByUri("", mockFilterChain);
      verifyChainIsCalled(mockFilterChain);
    }
  
    @Test
    public void requestValidGroup()
        throws Exception {
      initChainOnErrorFilter();
      requestGroupByUri("/folder/g1.css");
    }
  
    @Test
    public void requestInvalidGroup()
        throws Exception {
      initChainOnErrorFilter();
      requestGroupByUri("/folder/INVALID_GROUP.css", mockFilterChain);
      verifyChainIsCalled(mockFilterChain);
    }
  
    @Test
    public void cannotAccessUnauthorizedRequest()
        throws Exception {
      initVictimWithMockAuthManager();
      final String resourcePath = "/g1.css";
      final String requestUri = ResourceProxyRequestHandler.createProxyPath("", resourcePath);
  
      when(mockAuthorizationManager.isAuthorized(resourcePath)).thenReturn(true);
      requestGroupByUri(requestUri, new RequestBuilder(requestUri) {
        @Override
        protected HttpServletRequest newRequest() {
          final HttpServletRequest request = super.newRequest();
          when(request.getParameter(PARAM_RESOURCE_ID)).thenReturn(resourcePath);
          when(request.getParameter(ResourceProxyRequestHandler.PATH_API)).thenReturn(ResourceProxyRequestHandler.PATH_RESOURCES);
          return request;
        }
      }, mockFilterChain);
      verifyChainIsNotCalled(mockFilterChain);
    }
  
    /**
     * Creates the victim filter which usues mock {@link ResourceAuthorizationManager}.
     */
    private void initVictimWithMockAuthManager() {
      victim = new WroFilter() {
        @Override
        protected void onException(final Exception e, final HttpServletResponse response, final FilterChain chain) {
          throw WroRuntimeException.wrap(e);
        }
  
        @Override
        Injector getInjector() {
          return new InjectorBuilder(
              new BaseWroManagerFactory().setUriLocatorFactory(mockUriLocatorFactory).setResourceAuthorizationManager(
                  mockAuthorizationManager)).build();
        }
      };
    }
  
    @Test
    public void requestUrlRewrittenResource()
        throws Exception {
      final String resourcePath = "classpath:ro/isdc/wro/http/2.css";
      initVictimWithMockAuthManager();
  
      when(mockAuthorizationManager.isAuthorized(resourcePath)).thenReturn(true);
  
      final String requestUri = ResourceProxyRequestHandler.createProxyPath("", resourcePath);
      requestGroupByUri(requestUri, new RequestBuilder(requestUri) {
        @Override
        protected HttpServletRequest newRequest() {
          final HttpServletRequest request = super.newRequest();
          when(request.getParameter(PARAM_RESOURCE_ID)).thenReturn(resourcePath);
          when(request.getParameter(ResourceProxyRequestHandler.PATH_API)).thenReturn(ResourceProxyRequestHandler.PATH_RESOURCES);
          return request;
        }
      });
    }
  
    private void requestGroupByUri(final String requestUri)
        throws Exception {
      requestGroupByUri(requestUri, new RequestBuilder(requestUri), mockFilterChain);
    }
  
    private void requestGroupByUri(final String requestUri, final FilterChain chain)
        throws Exception {
      requestGroupByUri(requestUri, new RequestBuilder(requestUri), chain);
    }
  
    @Test
    public void testDoFilterInDEPLOYMENTMode()
        throws Exception {
      when(mockRequest.getRequestURI()).thenReturn("/g2.js");
      victim.setWroManagerFactory(createValidManagerFactory());
      setConfigurationMode(FilterConfigWroConfigurationFactory.PARAM_VALUE_DEPLOYMENT);
      victim.init(mockFilterConfig);
  
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
    }
  
    /**
     * Perform initialization and simulates a call to WroFilter with given requestUri.
     *
     * @param requestUri
     */
    private void requestGroupByUri(final String requestUri, final RequestBuilder requestBuilder, final FilterChain chain)
        throws Exception {
      victim.init(mockFilterConfig);
      doFilter(requestUri, requestBuilder, chain);
    }
  
    private void doFilter(final String requestUri, final RequestBuilder requestBuilder, final FilterChain chain)
        throws Exception {
      final HttpServletRequest request = requestBuilder.newRequest();
      final ServletOutputStream sos = mock(ServletOutputStream.class);
      final HttpServletResponse mockResponse = mock(HttpServletResponse.class);
      when(mockResponse.getOutputStream()).thenReturn(sos);
      victim.doFilter(request, mockResponse, chain);
    }
  
    private void requestGroupByUri(final String requestUri, final RequestBuilder requestBuilder)
        throws Exception {
      requestGroupByUri(requestUri, requestBuilder, mockFilterChain);
    }
  
    /**
     * Tests that in DEPLOYMENT mode the API is not exposed.
     */
    @Test
    public void testApiCallInDEPLOYMENTMode()
        throws Exception {
      initChainOnErrorFilter();
      when(mockRequest.getRequestURI()).thenReturn(ReloadCacheRequestHandler.PATH_API + "/someMethod");
      setConfigurationMode(FilterConfigWroConfigurationFactory.PARAM_VALUE_DEPLOYMENT);
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      // No api method exposed -> proceed with chain
      verifyChainIsCalled(mockFilterChain);
    }
  
    /**
     * Tests that in DEPLOYMENT mode the API is not exposed.
     */
    @Test
    public void testApiCallInDEVELOPMENTModeAndInvalidApiCall()
        throws Exception {
      initChainOnErrorFilter();
      when(mockRequest.getRequestURI()).thenReturn(ReloadCacheRequestHandler.PATH_API + "/someMethod");
      // by default configuration is development
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      // No api method exposed -> proceed with chain
      verifyChainIsCalled(mockFilterChain);
    }
  
    /**
     * Tests that in DEVELOPMENT mode the API is exposed.
     */
    @Test
    public void testApiCallInDEVELOPMENTModeAndReloadCacheCall()
        throws Exception {
      when(mockRequest.getRequestURI()).thenReturn(ReloadCacheRequestHandler.ENDPOINT_URI);
      when(mockResponse.getWriter()).thenReturn(new PrintWriter(System.out));
  
      final CacheStrategy<CacheKey, CacheValue> mockCacheStrategy = mock(CacheStrategy.class);
  
      final WroManagerFactory managerFactory = new BaseWroManagerFactory().setCacheStrategy(mockCacheStrategy);
  
      victim.setWroManagerFactory(managerFactory);
      // by default configuration is development
      victim.init(mockFilterConfig);
  
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      // api method exposed -> chain is not called
      verifyChainIsNotCalled(mockFilterChain);
  
      verify(mockCacheStrategy).clear();
    }
  
    /**
     * Tests that in DEPLOYMENT mode the API is NOT exposed.
     */
    @Test
    public void apiCallInDeploymentMode()
        throws Exception {
      final Properties props = new Properties();
      // init WroConfig properties
      props.setProperty(ConfigConstants.debug.name(), Boolean.FALSE.toString());
      final WroFilter theFilter = new WroFilter() {
        @Override
        protected ObjectFactory<WroConfiguration> newWroConfigurationFactory(final FilterConfig filterConfig) {
          final PropertyWroConfigurationFactory factory = new PropertyWroConfigurationFactory(props);
          return factory;
        }
      };
      // initFilterWithValidConfig(theFilter);
      final HttpServletRequest request = mock(HttpServletRequest.class, Mockito.RETURNS_DEEP_STUBS);
      when(request.getRequestURI()).thenReturn(ReloadCacheRequestHandler.ENDPOINT_URI);
  
      final HttpServletResponse response = mock(HttpServletResponse.class);
      when(response.getWriter()).thenReturn(new PrintWriter(System.out));
      // by default configuration is development
      theFilter.init(mockFilterConfig);
  
      theFilter.doFilter(request, response, mockFilterChain);
      // No api method exposed -> proceed with chain
      verifyChainIsCalled(mockFilterChain);
    }
  
    /**
     * Proves that the model reload has effect.
     */
    @Test
    public void modelShouldBeReloadedWhenReloadIsTriggered()
        throws Exception {
      final WroManagerFactory wroManagerFactory = new BaseWroManagerFactory().setModelFactory(new WroModelFactory() {
        private boolean wasCreated = false;
  
        public WroModel create() {
          if (!wasCreated) {
            wasCreated = true;
            // return model with no groups defined
            return new WroModel();
          }
          // second time when created add one group
          return new WroModel().addGroup(new Group("g1"));
        }
  
        public void destroy() {
        }
      });
  
      final WroFilter filter = new WroFilter() {
        @Override
        protected WroManagerFactory newWroManagerFactory() {
          return wroManagerFactory;
        }
  
        @Override
        protected ObjectFactory<WroConfiguration> newWroConfigurationFactory(final FilterConfig filterConfig) {
          return new ObjectFactory<WroConfiguration>() {
            public WroConfiguration create() {
              return Context.get().getConfig();
            }
          };
        }
      };
      filter.init(mockFilterConfig);
      final WroModelFactory modelFactory = wroManagerFactory.create().getModelFactory();
  
      assertTrue(modelFactory.create().getGroups().isEmpty());
  
      // reload model
      Context.get().getConfig().reloadModel();
      // the second time should have one group
      assertEquals(1, modelFactory.create().getGroups().size());
    }
  
    @Test
    public void testReloadCacheCall()
        throws Exception {
      when(mockRequest.getRequestURI()).thenReturn(ReloadCacheRequestHandler.ENDPOINT_URI);
  
      final ThreadLocal<Integer> status = new ThreadLocal<Integer>();
      final HttpServletResponse response = new HttpServletResponseWrapper(mockResponse) {
        @Override
        public void setStatus(final int sc) {
          status.set(sc);
        }
      };
      Context.unset();
      Context.set(Context.webContext(mockRequest, response, mockFilterConfig));
      victim.init(mockFilterConfig);
      victim.doFilter(Context.get().getRequest(), Context.get().getResponse(), mockFilterChain);
  
      assertEquals(Integer.valueOf(HttpServletResponse.SC_OK), status.get());
    }
  
    @Test
    public void testReloadModelCall()
        throws Exception {
      when(mockRequest.getRequestURI()).thenReturn(ReloadModelRequestHandler.ENDPOINT_URI);
  
      final ThreadLocal<Integer> status = new ThreadLocal<Integer>();
      final HttpServletResponse response = new HttpServletResponseWrapper(mockResponse) {
        @Override
        public void setStatus(final int sc) {
          status.set(sc);
        }
      };
      Context.unset();
      Context.set(Context.webContext(mockRequest, response, mockFilterConfig));
      victim.init(mockFilterConfig);
      victim.doFilter(Context.get().getRequest(), Context.get().getResponse(), mockFilterChain);
      assertEquals(Integer.valueOf(HttpServletResponse.SC_OK), status.get());
    }
  
    /**
     * Mocks the WroFilter.PARAM_CONFIGURATION init param with passed value.
     */
    private void setConfigurationMode(final String value) {
      when(mockFilterConfig.getInitParameter(FilterConfigWroConfigurationFactory.PARAM_CONFIGURATION)).thenReturn(value);
    }
  
    class RequestBuilder {
      private final String requestUri;
  
      public RequestBuilder(final String requestUri) {
        this.requestUri = requestUri;
      }
  
      protected HttpServletRequest newRequest() {
        final HttpServletRequest mockRequest = mock(HttpServletRequest.class);
        when(mockRequest.getRequestURI()).thenReturn(requestUri);
        return mockRequest;
      }
    }
  
    /**
     * Should throw {@link NullPointerException} when provided requestHandler's collection is null.
     */
    @Test(expected = NullPointerException.class)
    public void shouldNotAcceptNullRequestHandlers()
        throws Throwable {
      victim.setRequestHandlerFactory(new RequestHandlerFactory() {
        public Collection<RequestHandler> create() {
          return null;
        }
      });
      try {
        victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      } catch (final WroRuntimeException e) {
        throw e.getCause();
      }
    }
  
    @Test(expected = NullPointerException.class)
    public void cannotAcceptNullRequestHandlerFactory() {
      victim.setRequestHandlerFactory(null);
    }
  
    @Test(expected = UnauthorizedRequestException.class)
    public void testProxyUnauthorizedRequest()
        throws Exception {
      processProxyWithResourceId("test");
    }
  
    private void processProxyWithResourceId(final String resourceId)
        throws Exception {
      when(mockRequest.getParameter(PARAM_RESOURCE_ID)).thenReturn(resourceId);
      when(mockRequest.getParameter(ResourceProxyRequestHandler.PATH_API)).thenReturn(ResourceProxyRequestHandler.PATH_RESOURCES);
      when(mockRequest.getRequestURI()).thenReturn(ResourceProxyRequestHandler.createProxyPath("", resourceId));
  
      final WroConfiguration config = new WroConfiguration();
      // we don't need caching here, otherwise we'll have clashing during unit tests.
      Context.unset();
      Context.set(Context.webContext(mockRequest, mockResponse, mockFilterConfig), newConfigWithUpdatePeriodValue(0));
      victim.init(mockFilterConfig);
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
    }
  
    /**
     * Initialize {@link WroConfiguration} object with cacheUpdatePeriod & modelUpdatePeriod equal with provided argument.
     */
    private WroConfiguration newConfigWithUpdatePeriodValue(final long periodValue) {
      final WroConfiguration config = new WroConfiguration();
      config.setCacheUpdatePeriod(periodValue);
      config.setModelUpdatePeriod(periodValue);
      return config;
    }
  
    private void prepareValidRequest(final WroConfiguration config)
        throws ServletException {
      when(mockRequest.getRequestURI()).thenReturn("/resource/g1.css");
      Context.unset();
      Context.set(Context.webContext(mockRequest, mockResponse, mockFilterConfig));
      victim.setConfiguration(config);
      victim.setWroManagerFactory(createValidManagerFactory());
      victim.init(mockFilterConfig);
    }
  
    @Test
    public void shouldNotDestroyWroModelWhenCacheIsNotDisabled()
        throws Exception {
      final WroConfiguration config = new WroConfiguration();
  
      prepareValidRequest(config);
  
      final WroModelFactory mockModelFactory = Mockito.spy(createValidModelFactory());
      victim.setWroManagerFactory(new BaseWroManagerFactory().setModelFactory(mockModelFactory));
  
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
  
      verify(mockModelFactory, Mockito.never()).destroy();
    }
  
    @Test(expected = NullPointerException.class)
    public void cannotSetNullConfiguration() {
      victim.setConfiguration(null);
    }
  
    @Test(expected = IllegalStateException.class)
    public void shouldFailWhenConfigurationFactoryFails()
        throws Exception {
      victim = new WroFilter() {
        @Override
        protected ObjectFactory<WroConfiguration> newWroConfigurationFactory(final FilterConfig filterConfig) {
          throw new IllegalStateException("BOOM!");
        }
      };
      victim.init(mockFilterConfig);
    }
  
    @Test
    public void shouldChainTheIncludedRequestByDispatcher()
        throws Exception {
      when(mockRequest.getAttribute(DispatcherStreamLocator.ATTRIBUTE_INCLUDED_BY_DISPATCHER)).thenReturn(Boolean.TRUE);
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      verify(mockManagerFactory, Mockito.never()).create();
      verifyChainIsCalled(mockFilterChain);
    }
  
    @Test
    public void shouldChainWhenFilterIsNotEnabled()
        throws Exception {
      victim.setEnable(false);
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      verifyChainIsCalled(mockFilterChain);
    }
  
    @Test
    public void shouldNotChainWhenFilterIsEnabled()
        throws Exception {
      prepareValidRequest(new WroConfiguration());
      victim.setEnable(true);
  
      victim.doFilter(mockRequest, mockResponse, mockFilterChain);
      verifyChainIsNotCalled(mockFilterChain);
    }
  
    @Test
    public void shouldUnregisterMBeanOnDestroy()
        throws Exception {
      when(mockMBeanServer.isRegistered(Mockito.any(ObjectName.class))).thenReturn(true);
      victim.init(mockFilterConfig);
      victim.destroy();
      verify(mockMBeanServer).unregisterMBean(Mockito.any(ObjectName.class));
    }
  
    @Test
    public void shouldUseProcessorsConfiguredInWroProperties()
        throws Exception {
      final ObjectFactory<WroConfiguration> configurationFactory = new PropertiesAndFilterConfigWroConfigurationFactory(
          mockFilterConfig) {
        @Override
        public Properties createProperties() {
          final Properties props = new Properties();
          props.setProperty(ConfigConstants.managerFactoryClassName.name(), ConfigurableWroManagerFactory.class.getName());
          props.setProperty(ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS, CssMinProcessor.ALIAS);
          return props;
        }
      };
      victim.setWroConfigurationFactory(configurationFactory);
      victim.setWroManagerFactory(null);
      victim.init(mockFilterConfig);
      Context.unset();
      Context.set(Context.webContext(mockRequest, mockResponse, mockFilterConfig), configurationFactory.create());
      final WroManagerFactory factory = victim.getWroManagerFactory();
      assertEquals(1, factory.create().getProcessorsFactory().getPreProcessors().size());
    }
  
    @Test
    public void shouldNotFailWhenInvokedConcurrently() throws Exception {
      final String requestUri = "/folder/g1.css";
      //Unset it to allow tested filter to set its own context.
      Context.unset();
      victim.setWroManagerFactory(createValidManagerFactory());
      victim.init(mockFilterConfig);
      WroTestUtils.runConcurrently(new Callable<Void>() {
        public Void call()
            throws Exception {
          doFilter(requestUri, new RequestBuilder(requestUri), mockFilterChain);
          return null;
        }
      });
    }
  
    @After
    public void tearDown() {
      if (victim != null) {
        victim.destroy();
      }
      Context.unset();
    }
  }
  