org.jasig.portal.portlet.rendering.PortletRendererImplTest.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.portal.portlet.rendering.PortletRendererImplTest.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you 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 org.jasig.portal.portlet.rendering;

import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
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 java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.portlet.CacheControl;
import javax.portlet.PortletException;
import javax.servlet.http.HttpServletRequest;

import junit.framework.Assert;

import org.apache.commons.lang.time.DateUtils;
import org.apache.pluto.container.PortletContainer;
import org.apache.pluto.container.PortletContainerException;
import org.apache.pluto.container.PortletWindow;
import org.jasig.portal.api.portlet.PortletDelegationLocator;
import org.jasig.portal.events.IPortalEventFactory;
import org.jasig.portal.portlet.container.CacheControlImpl;
import org.jasig.portal.portlet.container.cache.CachedPortletData;
import org.jasig.portal.portlet.container.cache.IPortletCacheControlService;
import org.jasig.portal.portlet.om.IPortletDefinition;
import org.jasig.portal.portlet.om.IPortletEntity;
import org.jasig.portal.portlet.om.IPortletWindow;
import org.jasig.portal.portlet.om.IPortletWindowId;
import org.jasig.portal.portlet.registry.IPortletWindowRegistry;
import org.jasig.portal.url.IPortalRequestInfo;
import org.jasig.portal.url.IUrlSyntaxProvider;
import org.jasig.portal.utils.web.PortletHttpServletRequestWrapper;
import org.jasig.portal.utils.web.PortletHttpServletResponseWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

import com.google.common.collect.ImmutableMap;

/**
 * Tests for {@link PortletRendererImpl}.
 * 
 * @author Nicholas Blair
 * @version $Id$
 */
@RunWith(MockitoJUnitRunner.class)
public class PortletRendererImplTest {
    @InjectMocks
    private PortletRendererImpl portletRenderer = new PortletRendererImpl();
    @Mock
    private IUrlSyntaxProvider urlSyntaxProvider;
    @Mock
    private IPortalEventFactory portalEventFactory;
    @Mock
    private IPortletCacheControlService portletCacheControlService;
    @Mock
    private IPortletWindowRegistry portletWindowRegistry;
    @Mock
    private PortletContainer portletContainer;
    @Mock
    private PortletDelegationLocator portletDelegationLocator;
    @Mock
    private IPortletWindowId portletWindowId;
    @Mock
    private IPortletWindow portletWindow;
    @Mock
    private IPortletEntity portletEntity;
    @Mock
    private IPortletDefinition portletDefinition;
    @Mock
    private PortletWindow plutoPortletWindow;
    @Mock
    private IPortalRequestInfo portalRequestInfo;

    private final String portletFname = "MyPortlet";

    /**
     * Does common setup of mock options needed for portlet execution
     */
    protected void setupPortletExecutionMocks(MockHttpServletRequest request) {
        when(portletWindowRegistry.getPortletWindow(isA(HttpServletRequest.class), eq(portletWindowId)))
                .thenReturn(portletWindow);
        when(portletWindow.getRenderParameters()).thenReturn(Collections.<String, String[]>emptyMap());
        when(portletWindow.getPlutoPortletWindow()).thenReturn(plutoPortletWindow);
        when(portletWindowRegistry.getPortletWindow(request, portletWindowId)).thenReturn(portletWindow);
        when(portletWindow.getPortletEntity()).thenReturn(portletEntity);
        when(portletEntity.getPortletDefinition()).thenReturn(portletDefinition);
        when(portletDefinition.getFName()).thenReturn(portletFname);
        when(urlSyntaxProvider.getPortalRequestInfo(isA(HttpServletRequest.class))).thenReturn(portalRequestInfo);
    }

    /**
     * {@link CacheControl} says don't cache, make sure no caching.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doRenderMarkupNoCacheControl() throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(0);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(false);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);

        // call 2 times
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);

        verify(portletContainer, times(2)).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        verify(portletCacheControlService, never()).cachePortletRenderOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(String.class), isA(CacheControl.class));

    }

    /**
     * No cached data exists, but mock a {@link CacheControl} that will trigger the portletContainer#doRender, 
     * capture the output, and give to the portlet cachecontrol service.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doRenderMarkupCapture() throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(300);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(true);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);

        verify(portletContainer, times(1)).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));

        verify(portletCacheControlService, times(1)).cachePortletRenderOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(String.class), isA(CacheControl.class));
    }

    /**
     * No cached data exists, but mock a {@link CacheControl} with a negative value for expirationtime.
     * Will trigger the portletContainer#doRender, 
     * capture the output, and give to the portlet cachecontrol service.
     * 
     * negative value for cacheControl expiration time means "cache forever."
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doRenderMarkupCaptureNegativeExpirationTime()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(-1);

        setupPortletExecutionMocks(request);
        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(true);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);

        verify(portletContainer, times(1)).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));

        verify(portletCacheControlService, times(1)).cachePortletRenderOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(String.class), isA(CacheControl.class));
    }

    /**
     * Mimic workflow when cached portlet data using "expiration" method is available.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doRenderMarkupCachedContentExpirationMethodTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setStringData("<p>Some content</p>");
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);
        Assert.assertEquals("<p>Some content</p>", writer.toString());

        // verify we enter the first branch and never execute portletContainer#doRender
        verify(portletContainer, never()).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(cacheControl);
    }

    /**
     * Mimic workflow when data cached portlet data using "validation" method is available.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doRenderMarkupCachedContentValidationNotExpiredMethodTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setETag("123456");
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setStringData("<p>Some content</p>");
        cachedPortletData.setEtag("123456");
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);
        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);
        Assert.assertEquals("<p>Some content</p>", writer.toString());

        // verify we enter the first branch and never execute portletContainer#doRender
        verify(portletContainer, never()).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(cacheControl);
    }

    /**
     * Mimic workflow when data cached portlet data using "validation" method is available.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doRenderMarkupCachedContentValidationMethodExpiredTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        // by setting useCachedContent to true, we are saying even though content is expired, replay it anyways (since etag is still valid)
        cacheControl.setUseCachedContent(true);
        cacheControl.setETag("123456");
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setStringData("<p>Some content</p>");
        cachedPortletData.setEtag("123456");
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        // set the time stored to be a time that will trigger the content to be expired
        Date expiredTime = DateUtils.addSeconds(now, -301);
        cachedPortletData.setTimeStored(expiredTime);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);
        when(portalRequestInfo.getTargetedPortletWindowId()).thenReturn(portletWindowId);

        StringWriter writer = new StringWriter();
        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);
        Assert.assertEquals("<p>Some content</p>", writer.toString());
        // verify the expiration time has been updated
        Assert.assertNotSame(expiredTime, cachedPortletData.getTimeStored());
        // context is expired, triggers doRender
        verify(portletContainer, times(1)).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Verify invoking portletRenderer#doAction removes cached content.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doActionPurgesCachedContent() throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();

        cacheControl.setETag("123456");
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setStringData("<p>Some content</p>");
        cachedPortletData.setEtag("123456");
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletRenderCacheControl(portletWindowId, request))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(true);

        StringWriter writer = new StringWriter();

        // doAction will trigger purge
        portletRenderer.doAction(portletWindowId, request, response);
        verify(portletCacheControlService, times(1)).purgeCachedPortletData(portletWindowId, request, cacheControl);
        verify(portletContainer, times(1)).doAction(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));

        portletRenderer.doRenderMarkup(portletWindowId, request, response, writer);

        verify(portletContainer, times(1)).doRender(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, times(1)).cachePortletRenderOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(String.class), isA(CacheControl.class));

    }

    /**
     * {@link CacheControl} says don't cache, make sure no caching for doServeResource.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @SuppressWarnings("unchecked")
    @Test
    public void testDoServeResourceNoCache() throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(0);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(false);

        portletRenderer.doServeResource(portletWindowId, request, response);

        // call 2 times
        portletRenderer.doServeResource(portletWindowId, request, response);

        verify(portletContainer, times(2)).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        verify(portletCacheControlService, never()).cachePortletResourceOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(CachedPortletData.class), isA(CacheControl.class));
    }

    /**
     * No cached data exists, but mock a {@link CacheControl} that will trigger the portletContainer#doServeResource, 
     * capture the output, and give to the portlet cachecontrol service.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @SuppressWarnings("unchecked")
    @Test
    public void doServeResourceCapture() throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        response.setContentType("application/octet-stream");
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(300);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(true);

        portletRenderer.doServeResource(portletWindowId, request, response);

        verify(portletContainer, times(1)).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));

        verify(portletCacheControlService, times(1)).cachePortletResourceOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(CachedPortletData.class), isA(CacheControl.class));
    }

    /**
     * No cached data exists, but mock a {@link CacheControl} with a negative value for expirationtime.
     * Will trigger the portletContainer#doServeResource, 
     * capture the output, and give to the portlet cachecontrol service.
     * 
     * negative value for cacheControl expiration time means "cache forever."
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @SuppressWarnings("unchecked")
    @Test
    public void doServeResourceMarkupCaptureNegativeExpirationTime()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        response.setContentType("application/octet-stream");
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(false);
        cacheControl.setExpirationTime(-1);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletRenderOutput(portletWindowId, request)).thenReturn(null);
        when(portletCacheControlService.shouldOutputBeCached(cacheControl)).thenReturn(true);

        portletRenderer.doServeResource(portletWindowId, request, response);

        verify(portletContainer, times(1)).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));

        verify(portletCacheControlService, times(1)).cachePortletResourceOutput(isA(IPortletWindowId.class),
                isA(HttpServletRequest.class), isA(CachedPortletData.class), isA(CacheControl.class));
    }

    /**
     * Mimic workflow when cached portlet data using "expiration" method is available.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doServeResourceCachedContentExpirationMethodTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);
        // verify content matches what was in cache (no array support in Assert.assertEquals, check byte for byte)
        byte[] fromResponse = response.getContentAsByteArray();
        Assert.assertEquals(content.length, fromResponse.length);
        for (int i = 0; i < content.length; i++) {
            Assert.assertEquals(content[i], fromResponse[i]);
        }
        // verify we enter the first branch and never execute portletContainer#doServeResource
        verify(portletContainer, never()).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Mimic workflow when cached portlet data using "validation" method is available.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doServeResourceCachedContentValidationMethodTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        cacheControl.setETag("123456");
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setEtag("123456");
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);
        // verify content matches what was in cache (no array support in Assert.assertEquals, check byte for byte)
        byte[] fromResponse = response.getContentAsByteArray();
        Assert.assertEquals(content.length, fromResponse.length);
        for (int i = 0; i < content.length; i++) {
            Assert.assertEquals(content[i], fromResponse[i]);
        }
        Assert.assertEquals(200, response.getStatus());
        // verify we enter the first branch and never execute portletContainer#doServeResource
        verify(portletContainer, never()).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Same as {@link #doServeResourceCachedContentValidationMethodTest()}, but simulate browser
     * sending If-None-Match header that matches the etag. Verify no content returned and a 304 status code.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doServeResourceCachedContentValidationMethodNotModifiedTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.addHeader("If-None-Match", "123456");
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        cacheControl.setETag("123456");
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setEtag("123456");
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);
        //byte [] fromResponse = response.getContentAsByteArray();

        Assert.assertEquals(0, response.getContentLength());
        Assert.assertEquals(304, response.getStatus());
        // verify we enter the first branch and never execute portletContainer#doServeResource
        verify(portletContainer, never()).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Same as {@link #doServeResourceCachedContentValidationMethodTest()}, but simulate browser
     * sending If-None-Match header with mismatched etag. Response is 200 with content and new etag
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doServeResourceCachedContentValidationMethodIfNoneMatchInvalidTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.addHeader("If-None-Match", "123456");
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        cacheControl.setETag("123457");
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setEtag("123457");
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);
        // verify content matches what was in cache (no array support in Assert.assertEquals, check byte for byte)
        byte[] fromResponse = response.getContentAsByteArray();
        Assert.assertEquals(content.length, fromResponse.length);
        for (int i = 0; i < content.length; i++) {
            Assert.assertEquals(content[i], fromResponse[i]);
        }
        Assert.assertEquals(200, response.getStatus());
        Assert.assertEquals("123457", response.getHeader("ETag"));
        // verify we enter the first branch and never execute portletContainer#doServeResource
        verify(portletContainer, never()).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Same as {@link #doServeResourceCachedContentValidationMethodNotModifiedTest()}, however the CachedPortletData
     * is older than it's expiration time. Verify the renderer still detects the etag and returns 304 not modified.
     * 
     * @throws PortletException
     * @throws IOException
     * @throws PortletContainerException
     */
    @Test
    public void doServeResourceCachedContentValidationMethodNotModifiedInternalCacheExpiredTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.addHeader("If-None-Match", "123456");
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        cacheControl.setETag("123456");
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setEtag("123456");
        // set Time stored to a value prior to the expiration time
        cachedPortletData.setTimeStored(DateUtils.addSeconds(now, -310));

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);

        Assert.assertEquals(0, response.getContentLength());
        Assert.assertEquals(304, response.getStatus());
        // since the cached content is expired, a doServeResource is going to be invoked
        verify(portletContainer, times(1)).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // portlet said we should useCachedContent, so don't expect an attempt to "cache output"
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }

    /**
     * Verify headers stored in cache are replayed on the response for cached doServeResource content.
     * 
     * @throws PortletContainerException 
     * @throws IOException 
     * @throws PortletException 
     */
    @Test
    public void doServeResourceCachedContentReplayHeadersTest()
            throws PortletException, IOException, PortletContainerException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        Date now = new Date();
        CacheControlImpl cacheControl = new CacheControlImpl();
        cacheControl.setUseCachedContent(true);
        cacheControl.setExpirationTime(300);
        CachedPortletData cachedPortletData = new CachedPortletData();
        cachedPortletData.setContentType("application/json");
        byte[] content = "{ \"hello\": \"world\" }".getBytes();
        Map<String, List<Object>> headers = ImmutableMap.<String, List<Object>>of("header1",
                Arrays.<Object>asList("value1"), "header2", Arrays.<Object>asList("value2", "value3"));

        cachedPortletData.setHeaders(headers);
        cachedPortletData.setByteData(content);
        cachedPortletData.setExpirationTimeSeconds(cacheControl.getExpirationTime());
        cachedPortletData.setTimeStored(now);

        setupPortletExecutionMocks(request);

        when(portletCacheControlService.getPortletResourceCacheControl(portletWindowId, request, response))
                .thenReturn(cacheControl);
        when(portletCacheControlService.getCachedPortletResourceOutput(portletWindowId, request))
                .thenReturn(cachedPortletData);

        portletRenderer.doServeResource(portletWindowId, request, response);
        // verify content matches what was in cache (no array support in Assert.assertEquals, check byte for byte)
        byte[] fromResponse = response.getContentAsByteArray();
        Assert.assertEquals(content.length, fromResponse.length);
        for (int i = 0; i < content.length; i++) {
            Assert.assertEquals(content[i], fromResponse[i]);
        }
        Assert.assertEquals("value1", response.getHeader("header1"));
        Assert.assertEquals(Arrays.asList(new String[] { "value2", "value3" }), response.getHeaders("header2"));
        // verify we enter the first branch and never execute portletContainer#doServeResource
        verify(portletContainer, never()).doServeResource(isA(PortletWindow.class),
                isA(PortletHttpServletRequestWrapper.class), isA(PortletHttpServletResponseWrapper.class));
        // verify we never enter the other branch of the "should render cached output" if statement
        verify(portletCacheControlService, never()).shouldOutputBeCached(isA(CacheControl.class));
    }
}