/*
* Copyright (C) 2014 Civilian Framework.
*
* Licensed under the Civilian License (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.civilian-framework.org/license.txt
*
* 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.civilian.controller;
import java.lang.reflect.Method;
import java.util.Iterator;
import org.junit.BeforeClass;
import org.junit.Test;
import org.civilian.CivTest;
import org.civilian.Request;
import org.civilian.Response;
import org.civilian.annotation.BeanParam;
import org.civilian.annotation.Consumes;
import org.civilian.annotation.CookieParam;
import org.civilian.annotation.DefaultValue;
import org.civilian.annotation.Get;
import org.civilian.annotation.Head;
import org.civilian.annotation.MatrixParam;
import org.civilian.annotation.Options;
import org.civilian.annotation.PathParam;
import org.civilian.annotation.Post;
import org.civilian.annotation.Produces;
import org.civilian.annotation.Parameter;
import org.civilian.annotation.RequestContent;
import org.civilian.annotation.RequestMethod;
import org.civilian.content.ContentType;
import org.civilian.content.ContentTypeList;
import org.civilian.controller.ControllerMethod;
import org.civilian.resource.PathParamMap;
import org.civilian.resource.PathParams;
import org.civilian.response.protocol.NgReply;
import org.civilian.type.TypeLib;
import org.civilian.util.Value;
public class AnnotationTest extends CivTest
{
@BeforeClass public static void beforeClass()
{
typeLib_ = new TypeLib();
pathParams_ = new PathParamMap(null);
pathParams_.addAndSeal(PathParams.forIntSegment("customerId"));
}
//--------------------------------
// Base and Demo define methods which we use as input
// to ControllerMethod.create.
// actually it is not required to be part of a Controller class
// to generate a ControllerMethod
public static class Base
{
@Get public void overwritten()
{
}
}
// defines invalid actions methods
public static class Ignored
{
public void noMethodAnnotation()
{
// not a ControllerMethod since it has no RequestMethod annotation
}
@Get public static void staticMethod()
{
// not a ControllerMethod since it is static
}
@Get public static int notVoid()
{
// not a ControllerMethod since the return type is not void
return 1;
}
}
public static class Demo extends Base
{
@RequestMethod("PATCH")
public void recognizedExplicit()
{
// Action is recognized when using the RequestMethod annotation
}
@Options
public void recognizedAbbreviated()
{
// Action is recognized when using a abbreviated method annotations
}
@Get
@RequestMethod({"GET", "POST", "GET", "PATCH"})
public void methodReduction()
{
// artificial cumulation is reduced to a proper set
}
@Post public void extensionNone()
{
// this action only accepts requests with extension null
}
@Head public void producesAny()
{
// this action promises to produce any content type
}
@Get @Produces(ContentType.Strings.TEXT_HTML)
public void producesHtml()
{
// this action promises to produce text/html
}
@Get @Produces({ContentType.Strings.ANY, "text/*", ContentType.Strings.TEXT_HTML})
public void producesMulti()
{
// this action promises to produce text/html and text/* and */*:
// they are sorted by specificity, more specific first
}
@Get @Produces("text/html; q=0.5")
public void producesQualified()
{
// this action promises to produce text/html with a quality of 0.5:
}
@Post public void consumesAny()
{
}
@Post @Consumes("application/json, text/xml")
public void consumesSome()
{
}
@Get public void inject(
@Parameter("min") @DefaultValue("1") int minValue,
@Parameter("max") int maxValue,
@Parameter("val") Value<Integer> wrapped,
@MatrixParam("mode") short mode,
@PathParam("customerId") Integer customerId,
@CookieParam("store") String test,
@BeanParam Bean bean,
@RequestContent String content,
Request request,
Response response,
NgReply reply,
@Custom Object custom)
{
}
@Override public void overwritten()
{
}
}
// used for inject test of @BeanParam
private static class Bean
{
}
/**
* Static methods and methods missing a RequestMethod annotation
* are not recognized as resource actions.
*/
@Test public void testIgnored()
{
MethodArgFactory factory = new MethodArgFactory(pathParams_, typeLib_);
for (Method method : Ignored.class.getDeclaredMethods())
{
ControllerMethod action = ControllerMethod.create(factory, method);
if (action != null)
fail(method.getName() + " is not a valid action method");
}
}
/**
* ControllerMethods can either use the explicit @RequestMethod annotation or
* the abbreviations like @GET, etc...
*/
@Test public void testRecognized()
{
ControllerMethod method;
method = createMethod("recognizedExplicit");
assertRequestMethods(method.getRequestMethods(), "PATCH");
method = createMethod("recognizedAbbreviated");
assertRequestMethods(method.getRequestMethods(), "OPTIONS");
method = createMethod("methodReduction");
assertRequestMethods(method.getRequestMethods(), "GET", "POST", "PATCH");
}
private void assertRequestMethods(Iterator<String> it, String... methods)
{
for (int i=0; it.hasNext(); i++)
assertEquals(methods[i], it.next());
assertFalse(it.hasNext());
}
/**
* Test the @Produces annotation.
*/
@Test public void testProduces()
{
ControllerMethod action;
action = createMethod("producesAny");
assertEquals(0, action.getProducedContentTypes().size());
action = createMethod("producesHtml");
assertContentTypes(action.getProducedContentTypes(), ContentType.TEXT_HTML);
// ContentTypes are sorted by concreteness
action = createMethod("producesMulti");
assertContentTypes(action.getProducedContentTypes(), ContentType.TEXT_HTML, new ContentType("text/*"), ContentType.ANY);
// ContentTypes can have a quality
action = createMethod("producesQualified");
assertContentTypes(action.getProducedContentTypes(), ContentType.TEXT_HTML.withQuality(0.5));
}
/**
* Test the @Consumes annotation.
*/
@Test public void testConsumes()
{
ControllerMethod action;
action = createMethod("consumesAny");
assertEquals(0, action.getConsumesContentTypes().size());
assertTrue(action.canConsume(ContentType.ANY));
action = createMethod("consumesSome");
assertContentTypes(action.getConsumesContentTypes(), ContentType.APPLICATION_JSON, ContentType.TEXT_XML);
assertFalse(action.canConsume(ContentType.APPLICATION_EXCEL));
assertTrue(action.canConsume(ContentType.APPLICATION_JSON));
assertTrue(action.canConsume(ContentType.TEXT_XML));
}
/**
* Test the @QueryParm, @MatrixParam, @PathParam and @DefaultValue annotations.
*/
@Test public void testInjectParams()
{
ControllerMethod action;
action = createMethod("inject");
assertEquals(12, action.getArgCount());
int next = 0;
assertEquals("Parameter \"min\"", action.getArgument(next++).toString());
assertEquals("Parameter \"max\"", action.getArgument(next++).toString());
assertEquals("Parameter \"val\"", action.getArgument(next++).toString());
assertEquals("MatrixParam \"mode\"", action.getArgument(next++).toString());
assertEquals("PathParam \"customerId\"", action.getArgument(next++).toString());
assertEquals("CookieParam \"store\"", action.getArgument(next++).toString());
assertEquals("BeanParam", action.getArgument(next++).toString());
assertEquals("RequestContent", action.getArgument(next++).toString());
assertEquals("Request", action.getArgument(next++).toString());
assertEquals("Response", action.getArgument(next++).toString());
assertEquals("ResponseContent", action.getArgument(next++).toString());
assertEquals("Custom", action.getArgument(next++).toString());
}
/**
* Test that overwritten resource methods are recognized.
*/
@Test public void testOverride()
{
@SuppressWarnings("unused")
ControllerMethod action;
action = createMethod("overwritten");
}
private ControllerMethod createMethod(String methodName)
{
MethodArgFactory argFactory = new MethodArgFactory(pathParams_, typeLib_);
Method method = findMethod(Demo.class, methodName);
ControllerMethod action = ControllerMethod.create(argFactory, method);
if (action == null)
fail(methodName + " is not a resource action");
return action;
}
private void assertContentTypes(ContentTypeList list, ContentType... types)
{
int n = Math.min(list.size(), types.length);
for (int i=0; i<n; i++)
assertEquals(types[i], list.get(i));
assertEquals("size", types.length, list.size());
}
private static TypeLib typeLib_ = new TypeLib();
private static PathParamMap pathParams_;
}