package io.dropwizard.jersey.validation;
import com.codahale.metrics.MetricRegistry;
import io.dropwizard.jersey.AbstractJerseyTest;
import io.dropwizard.jersey.DropwizardResourceConfig;
import io.dropwizard.jersey.jackson.JacksonMessageBodyProviderTest.Example;
import io.dropwizard.jersey.jackson.JacksonMessageBodyProviderTest.ListExample;
import io.dropwizard.jersey.jackson.JacksonMessageBodyProviderTest.PartialExample;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assume.assumeThat;
public class ConstraintViolationExceptionMapperTest extends AbstractJerseyTest {
private static final Locale DEFAULT_LOCALE = Locale.getDefault();
@Override
protected Application configure() {
return DropwizardResourceConfig.forTesting(new MetricRegistry())
.packages("io.dropwizard.jersey.validation")
.register(new ValidatingResource2())
.register(new HibernateValidationFeature(Validators.newValidator()));
}
@BeforeClass
public static void init() {
// Set default locale to English because some tests assert localized error messages
Locale.setDefault(Locale.ENGLISH);
}
@AfterClass
public static void shutdown() {
Locale.setDefault(DEFAULT_LOCALE);
}
@Test
public void postInvalidEntityIs422() throws Exception {
assumeThat(Locale.getDefault().getLanguage(), is("en"));
final Response response = target("/valid/foo").request(MediaType.APPLICATION_JSON)
.post(Entity.entity("{}", MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class)).isEqualTo("{\"errors\":[\"name may not be empty\"]}");
}
@Test
public void postNullEntityIs422() throws Exception {
final Response response = target("/valid/foo").request(MediaType.APPLICATION_JSON)
.post(Entity.entity(null, MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(422);
String ret = "{\"errors\":[\"The request body may not be null\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void postInvalidatedEntityIs422() throws Exception {
assumeThat(Locale.getDefault().getLanguage(), is("en"));
final Response response = target("/valid/fooValidated").request(MediaType.APPLICATION_JSON)
.post(Entity.entity("{}", MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class)).isEqualTo("{\"errors\":[\"name may not be empty\"]}");
}
@Test
public void postInvalidInterfaceEntityIs422() throws Exception {
final Response response = target("/valid2/repr").request(MediaType.APPLICATION_JSON)
.post(Entity.entity("{\"name\": \"a\"}", MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"query param interfaceVariable may not be null\"]}");
}
@Test
public void returnInvalidEntityIs500() throws Exception {
assumeThat(Locale.getDefault().getLanguage(), is("en"));
final Response response = target("/valid/foo").request(MediaType.APPLICATION_JSON)
.post(Entity.entity("{ \"name\": \"Coda\" }", MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(500);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"server response name may not be empty\"]}");
}
@Test
public void returnInvalidatedEntityIs500() throws Exception {
assumeThat(Locale.getDefault().getLanguage(), is("en"));
final Response response = target("/valid/fooValidated").request(MediaType.APPLICATION_JSON)
.post(Entity.entity("{ \"name\": \"Coda\" }", MediaType.APPLICATION_JSON));
assertThat(response.getStatus()).isEqualTo(500);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"server response name may not be empty\"]}");
}
@Test
public void getInvalidReturnIs500() throws Exception {
// return value is too long and so will fail validation
final Response response = target("/valid/bar")
.queryParam("name", "dropwizard").request().get();
assertThat(response.getStatus()).isEqualTo(500);
String ret = "{\"errors\":[\"server response length must be between 0 and 3\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidQueryParamsIs400() throws Exception {
// query parameter is too short and so will fail validation
final Response response = target("/valid/bar")
.queryParam("name", "hi").request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"query param name length must be between 3 and 2147483647\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
// Send another request to trigger reflection cache
final Response cache = target("/valid/bar")
.queryParam("name", "hi").request().get();
assertThat(cache.getStatus()).isEqualTo(400);
assertThat(cache.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void cacheIsForParamNamesOnly() throws Exception {
// query parameter must not be null, and must be at least 3
final Response response = target("/valid/fhqwhgads")
.queryParam("num", 2).request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"query param num must be greater than or equal to 3\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
// Send another request to trigger reflection cache. This one is invalid in a different way
// and should get a different message.
final Response cache = target("/valid/fhqwhgads").request().get();
assertThat(cache.getStatus()).isEqualTo(400);
ret = "{\"errors\":[\"query param num may not be null\"]}";
assertThat(cache.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void postInvalidPrimitiveIs422() throws Exception {
// query parameter is too short and so will fail validation
final Response response = target("/valid/simpleEntity")
.request().post(Entity.json("hi"));
assertThat(response.getStatus()).isEqualTo(422);
String ret = "{\"errors\":[\"The request body length must be between 3 and 5\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidCustomTypeIs400() throws Exception {
// query parameter is too short and so will fail validation
final Response response = target("/valid/barter")
.queryParam("name", "hi").request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"query param name length must be between 3 and 2147483647\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidBeanParamsIs400() throws Exception {
// bean parameter is too short and so will fail validation
Response response = target("/valid/zoo")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("\"name must be Coda\"")
.containsOnlyOnce("\"query param name may not be empty\"")
.containsOnlyOnce("\"query param choice may not be null\"");
}
@Test
public void getInvalidSubBeanParamsIs400() throws Exception {
final Response response = target("/valid/sub-zoo")
.queryParam("address", "42 Wallaby Way")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param name may not be empty")
.containsOnlyOnce("name must be Coda");
}
@Test
public void getGroupSubBeanParamsIs400() throws Exception {
final Response response = target("/valid/sub-group-zoo")
.queryParam("address", "42 WALLABY WAY")
.queryParam("name", "Coda")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("[\"address must not be uppercase\"]");
}
@Test
public void postValidGroupsIs400() throws Exception {
final Response response = target("/valid/sub-valid-group-zoo")
.queryParam("address", "42 WALLABY WAY")
.queryParam("name", "Coda")
.request()
.post(Entity.json("{}"));
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("[\"address must not be uppercase\"]");
}
@Test
public void getInvalidatedBeanParamsIs400() throws Exception {
// bean parameter is too short and so will fail validation
final Response response = target("/valid/zoo2")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("\"name must be Coda\"")
.containsOnlyOnce("\"query param name may not be empty\"");
}
@Test
public void getInvalidHeaderParamsIs400() throws Exception {
final Response response = target("/valid/head")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"header cheese may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidCookieParamsIs400() throws Exception {
final Response response = target("/valid/cooks")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"cookie user_id may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidPathParamsIs400() throws Exception {
final Response response = target("/valid/goods/11")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"path param id not a well-formed email address\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidFormParamsIs400() throws Exception {
final Response response = target("/valid/form")
.request().post(Entity.form(new Form()));
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"form field username may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void postInvalidMethodClassIs422() throws Exception {
final Response response = target("/valid/nothing")
.request().post(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(422);
String ret = "{\"errors\":[\"must have a false thing\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidNestedReturnIs500() throws Exception {
final Response response = target("/valid/nested").request().get();
assertThat(response.getStatus()).isEqualTo(500);
String ret = "{\"errors\":[\"server response representation.name may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidNested2ReturnIs500() throws Exception {
final Response response = target("/valid/nested2").request().get();
assertThat(response.getStatus()).isEqualTo(500);
String ret = "{\"errors\":[\"server response example must have a false thing\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidContextIs400() throws Exception {
final Response response = target("/valid/context").request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"context may not be null\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void getInvalidMatrixParamIs400() throws Exception {
final Response response = target("/valid/matrix")
.matrixParam("bob", "").request().get();
assertThat(response.getStatus()).isEqualTo(400);
String ret = "{\"errors\":[\"matrix param bob may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
}
@Test
public void functionWithSameNameReturnDifferentErrors() throws Exception {
// This test is to make sure that functions with the same name and
// number of parameters (but different parameter types), don't return
// the same validation error due to any caching effects
final Response response = target("/valid/head")
.request().get();
String ret = "{\"errors\":[\"header cheese may not be empty\"]}";
assertThat(response.readEntity(String.class)).isEqualTo(ret);
final Response response2 = target("/valid/headCopy")
.request().get();
String ret2 = "{\"errors\":[\"query param cheese may not be null\"]}";
assertThat(response2.readEntity(String.class)).isEqualTo(ret2);
}
@Test
public void paramsCanBeValidatedWhenNull() {
assertThat(target("/valid/nullable-int-param")
.request().get().readEntity(String.class)).isEqualTo("I was null");
}
@Test
public void paramsCanBeUnwrappedAndValidated() {
assertThat(target("/valid/nullable-int-param").queryParam("num", 4)
.request().get().readEntity(String.class))
.containsOnlyOnce("[\"query param num must be less than or equal to 3\"]");
}
@Test
public void returnPartialValidatedRequestEntities() {
final Response response = target("/valid/validatedPartialExample")
.request().post(Entity.json("{\"id\":1}"));
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(PartialExample.class).id)
.isEqualTo(1);
}
@Test
public void invalidEntityExceptionForPartialValidatedRequestEntities() {
final Response response = target("/valid/validatedPartialExampleBoth")
.request().post(Entity.json("{\"id\":1}"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"text may not be null\"]}");
}
@Test
public void returnPartialBothValidatedRequestEntities() {
final Response response = target("/valid/validatedPartialExampleBoth")
.request().post(Entity.json("{\"id\":1,\"text\":\"hello Cemo\"}"));
assertThat(response.getStatus()).isEqualTo(200);
PartialExample ex = response.readEntity(PartialExample.class);
assertThat(ex.id).isEqualTo(1);
assertThat(ex.text).isEqualTo("hello Cemo");
}
@Test
public void invalidEntityExceptionForInvalidRequestEntities() {
final Response response = target("/valid/validExample")
.request().post(Entity.json("{\"id\":-1}"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"id must be greater than or equal to 0\"]}");
}
@Test
public void returnRequestEntities() {
final Response response = target("/valid/validExample")
.request().post(Entity.json("{\"id\":1}"));
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(Example.class).id)
.isEqualTo(1);
}
@Test
public void returnRequestArrayEntities() {
final Response response = target("/valid/validExampleArray")
.request().post(Entity.json("[{\"id\":1}, {\"id\":2}]"));
final Example ex1 = new Example();
final Example ex2 = new Example();
ex1.id = 1;
ex2.id = 2;
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(Example[].class))
.containsExactly(ex1, ex2);
}
@Test
public void invalidRequestCollectionEntities() {
final Response response = target("/valid/validExampleCollection")
.request().post(Entity.json("[{\"id\":-1}, {\"id\":-2}]"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.contains("id must be greater than or equal to 0",
"id must be greater than or equal to 0");
}
@Test
public void invalidRequestSingleCollectionEntities() {
final Response response = target("/valid/validExampleCollection")
.request().post(Entity.json("[{\"id\":1}, {\"id\":-2}]"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("id must be greater than or equal to 0");
}
@Test
public void returnRequestCollectionEntities() {
final Response response = target("/valid/validExampleCollection")
.request().post(Entity.json("[{\"id\":1}, {\"id\":2}]"));
assertThat(response.getStatus()).isEqualTo(200);
final Collection<Example> example =
response.readEntity(new GenericType<Collection<Example>>() {
});
Example ex1 = new Example();
Example ex2 = new Example();
ex1.id = 1;
ex2.id = 2;
assertThat(example).containsOnly(ex1, ex2);
}
@Test
public void invalidRequestSetEntities() {
final Response response = target("/valid/validExampleSet")
.request().post(Entity.json("[{\"id\":1}, {\"id\":-2}]"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("id must be greater than or equal to 0");
}
@Test
public void invalidRequestListEntities() {
final Response response = target("/valid/validExampleList")
.request().post(Entity.json("[{\"id\":-1}, {\"id\":-2}]"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"id must be greater than or equal to 0\"," +
"\"id must be greater than or equal to 0\"]}");
}
@Test
public void throwsAConstraintViolationExceptionForEmptyRequestEntities() {
final Response response = target("/valid/validExample")
.request().post(Entity.json(null));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"The request body may not be null\"]}");
}
@Test
public void returnsValidatedMapRequestEntities() {
final Response response = target("/valid/validExampleMap")
.request().post(Entity.json("{\"one\": {\"id\":1}, \"two\": {\"id\":2}}"));
assertThat(response.getStatus()).isEqualTo(200);
Map<String, Example> map = response.readEntity(new GenericType<Map<String, Example>>() {
});
assertThat(map.get("one").id).isEqualTo(1);
assertThat(map.get("two").id).isEqualTo(2);
}
@Test
public void invalidMapRequestEntities() {
final Response response = target("/valid/validExampleMap")
.request().post(Entity.json("{\"one\": {\"id\":-1}, \"two\": {\"id\":-2}}"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.isEqualTo("{\"errors\":[\"id must be greater than or equal to 0\"," +
"\"id must be greater than or equal to 0\"]}");
}
@Test
public void returnsValidatedEmbeddedListEntities() {
final Response response = target("/valid/validExampleEmbeddedList")
.request().post(Entity.json("[ {\"examples\": [ {\"id\":1 } ] } ]"));
assertThat(response.getStatus()).isEqualTo(200);
List<ListExample> res = response.readEntity(new GenericType<List<ListExample>>() {
});
assertThat(res).hasSize(1);
assertThat(res.get(0).examples).hasSize(1);
assertThat(res.get(0).examples.get(0).id).isEqualTo(1);
}
@Test
public void invalidEmbeddedListEntities() {
final Response response = target("/valid/validExampleEmbeddedList")
.request().post(Entity.json("[ {\"examples\": [ {\"id\":1 } ] }, { } ]"));
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("examples may not be empty");
}
@Test
public void testInvalidFieldQueryParam() {
final Response response = target("/valid/bar")
.queryParam("sort", "foo")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(422);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("sortParam must match \\\"^(asc|desc)$\\\"");
}
@Test
public void missingParameterMessageContainsParameterName() {
final Response response = target("/valid/paramValidation")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param length may not be null");
}
@Test
public void emptyParameterMessageContainsParameterName() {
final Response response = target("/valid/paramValidation")
.queryParam("length", "")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param length may not be null");
}
@Test
public void maxMessageContainsParameterName() {
final Response response = target("/valid/paramValidation")
.queryParam("length", 50)
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param length must be less than or equal to 5");
}
@Test
public void minMessageContainsParameterName() {
final Response response = target("/valid/paramValidation")
.queryParam("length", 1)
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param length must be greater than or equal to 2");
}
@Test
public void paramClassPassesValidation() {
final Response response = target("/valid/paramValidation")
.queryParam("length", 3)
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void notPresentEnumParameter() {
final Response response = target("/valid/enumParam")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param choice may not be null");
}
@Test
public void invalidEnumParameter() {
final Response response = target("/valid/enumParam")
.queryParam("choice", "invalid")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param choice must be one of [OptionA, OptionB, OptionC]");
}
@Test
public void invalidBeanParamEnumParameter() {
final Response response = target("/valid/zoo")
.queryParam("choice", "invalid")
.request().get();
assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class))
.containsOnlyOnce("query param choice must be one of [OptionA, OptionB, OptionC]");
}
}