/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.test.util;
import static java.util.Collections.emptyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.jscript.People;
import org.alfresco.repo.jscript.ScriptNode;
import org.json.JSONObject;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Container;
import org.springframework.extensions.webscripts.Description;
import org.springframework.extensions.webscripts.Description.RequiredCache;
import org.springframework.extensions.webscripts.DescriptionExtension;
import org.springframework.extensions.webscripts.FormatRegistry;
import org.springframework.extensions.webscripts.Match;
import org.springframework.extensions.webscripts.ScriptProcessorRegistry;
import org.springframework.extensions.webscripts.SearchPath;
import org.springframework.extensions.webscripts.TemplateProcessorRegistry;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.extensions.webscripts.json.JSONUtils;
import org.springframework.extensions.webscripts.processor.FTLTemplateProcessor;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.TemplateLoader;
/**
* Base Web Script Unit Test.
* <p>
* Provides helper methods that mock the nessesery classes needed to execute
* a Java backed webscript that implements DeclarativeWebScript.
* <p>
* Note that execution of java script controllers is not currently supported.
*
* @author Roy Wetherall
* @since 2.2
*/
public abstract class BaseWebScriptUnitTest extends BaseUnitTest
{
/** web script root folder for RM webscripts */
protected static final String WEBSCRIPT_ROOT_RM = "alfresco/templates/webscripts/org/alfresco/rma/";
/**
* @return declarative webscript
*/
protected abstract AbstractWebScript getWebScript();
/**
* @return classpath location of webscript template
*/
protected abstract String getWebScriptTemplate();
/**
* Helper method to build a map of web script parameter values
* mimicking those provided on the URL
*
* @param values
* @return
*/
protected Map<String, String> buildParameters(String ... values)
{
Map<String, String> result = new HashMap<String, String>(values.length/2);
for (int i = 0; i < values.length; i=i+2)
{
String key = values[i];
String value = values[i+1];
result.put(key, value);
}
return result;
}
/**
*
* @param parameters
* @return
* @throws Exception
*/
protected JSONObject executeJSONWebScript(Map<String, String> parameters) throws Exception
{
return executeJSONWebScript(parameters, null);
}
/**
* Execute web script and convert result into a JSON object.
*
* @param parameters map of all parameter values
* @return {@link JSONObject} result, parsed into a JSON object
*/
protected JSONObject executeJSONWebScript(Map<String, String> parameters, String content) throws Exception
{
String result = executeWebScript(parameters, content);
return new JSONObject(result);
}
/**
*
* @param parameters
* @return
* @throws Exception
*/
protected String executeWebScript(Map<String, String> parameters) throws Exception
{
return executeWebScript(parameters, null);
}
/**
* Execute web script and return result as a string.
*
* @param parameters map of all parameter values
* @return {@link String} result of web script
*/
protected String executeWebScript(Map<String, String> parameters, String content) throws Exception
{
AbstractWebScript webScript = getWebScript();
String template = getWebScriptTemplate();
// initialise webscript
webScript.init(getMockedContainer(template), getMockedDescription());
// execute webscript
WebScriptResponse mockedResponse = getMockedWebScriptResponse();
webScript.execute(getMockedWebScriptRequest(webScript, parameters, content), mockedResponse);
// return results
return mockedResponse.getWriter().toString();
}
/**
* Helper method to get the mocked web script request.
*
* @param webScript declarative web script
* @param parameters web script parameter values
* @return {@link WebScriptRequest} mocked web script request
*/
@SuppressWarnings("rawtypes")
protected WebScriptRequest getMockedWebScriptRequest(AbstractWebScript webScript, final Map<String, String> parameters, String content) throws Exception
{
Match match = new Match(null, parameters, null, webScript);
org.springframework.extensions.webscripts.Runtime mockedRuntime = mock(org.springframework.extensions.webscripts.Runtime.class);
WebScriptRequest mockedRequest = mock(WebScriptRequest.class);
doReturn(match).when(mockedRequest).getServiceMatch();
doReturn(mockedRuntime).when(mockedRequest).getRuntime();
if (content != null && !content.isEmpty())
{
Content mockedContent = mock(Content.class);
doReturn(content).when(mockedContent).getContent();
doReturn(mockedContent).when(mockedRequest).getContent();
}
String [] paramNames = (String[])parameters.keySet().toArray(new String[parameters.size()]);
doReturn(paramNames).when(mockedRequest).getParameterNames();
doAnswer(new Answer()
{
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
{
String paramName = (String)invocation.getArguments()[0];
return parameters.get(paramName);
}
}).when(mockedRequest).getParameter(anyString());
doReturn(new String[0]).when(mockedRequest).getHeaderNames();
doReturn("json").when(mockedRequest).getFormat();
return mockedRequest;
}
/**
* Helper method to get mocked web script response
*
* @return {@link WebScriptResponse} mocked web script response
*/
protected WebScriptResponse getMockedWebScriptResponse() throws Exception
{
WebScriptResponse mockedResponse = mock(WebScriptResponse.class);
StringWriter writer = new StringWriter();
doReturn(writer).when(mockedResponse).getWriter();
return mockedResponse;
}
/**
* Helper method to get mocked container object.
*
* @param template classpath location of webscripts ftl template
* @return {@link Container} mocked container
*/
protected Container getMockedContainer(String template) throws Exception
{
FormatRegistry mockedFormatRegistry = mock(FormatRegistry.class);
doReturn("application/json").when(mockedFormatRegistry).getMimeType(anyString(), anyString());
ScriptProcessorRegistry mockedScriptProcessorRegistry = mock(ScriptProcessorRegistry.class);
doReturn(null).when(mockedScriptProcessorRegistry).findValidScriptPath(anyString());
TemplateProcessorRegistry mockedTemplateProcessorRegistry = mock(TemplateProcessorRegistry.class);
doReturn(template).when(mockedTemplateProcessorRegistry).findValidTemplatePath(anyString());
FTLTemplateProcessor ftlTemplateProcessor = new FTLTemplateProcessor()
{
@Override
protected TemplateLoader getTemplateLoader()
{
return new ClassTemplateLoader(getClass(), "/");
}
};
ftlTemplateProcessor.init();
doReturn(ftlTemplateProcessor).when(mockedTemplateProcessorRegistry).getTemplateProcessor(anyString());
Container mockedContainer = mock(Container.class);
doReturn(mockedFormatRegistry).when(mockedContainer).getFormatRegistry();
doReturn(mockedScriptProcessorRegistry).when(mockedContainer).getScriptProcessorRegistry();
doReturn(mockedTemplateProcessorRegistry).when(mockedContainer).getTemplateProcessorRegistry();
Map<String, Object> containerTemplateParameters = new HashMap<>(5);
containerTemplateParameters.put("jsonUtils", new JSONUtils());
containerTemplateParameters.put("people", getMockedPeopleObject());
doReturn(containerTemplateParameters).when(mockedContainer).getTemplateParameters();
SearchPath mockedSearchPath = mock(SearchPath.class);
doReturn(false).when(mockedSearchPath).hasDocument(anyString());
doReturn(mockedSearchPath).when(mockedContainer).getSearchPath();
// setup description
Description mockDescription = mock(Description.class);
doReturn(mock(RequiredCache.class)).when(mockDescription).getRequiredCache();
return mockedContainer;
}
/**
* Creates a mock {@code people} object for use as a root object within FTL.
* This {@code people} object will return person nodes as specified in {@link #getMockedPeople()}.
*/
protected People getMockedPeopleObject()
{
People p = mock(People.class);
getMockedPeople().forEach((name, person) -> when(p.getPerson(eq(name))).thenReturn(person) );
return p;
}
/**
* Creates a map of person ScriptNodes for use within FTL.
* The default implementation is an empty map, but this can be overridden by subclasses.
* @return a map of usernames to mocked ScriptNode objects representing person nodes.
*/
protected Map<String, ScriptNode> getMockedPeople()
{
return emptyMap();
}
/**
* Helper method to get mocked description class
*
* @return {@link DescriptionExtension} mocked description class
*/
protected Description getMockedDescription()
{
Description mockedDescription = mock(Description.class);
doReturn(mock(RequiredCache.class)).when(mockedDescription).getRequiredCache();
return mockedDescription;
}
}