/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed 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.springframework.boot.autoconfigure.web.servlet.error;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link DefaultErrorViewResolver}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class DefaultErrorViewResolverTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
private DefaultErrorViewResolver resolver;
@Mock
private TemplateAvailabilityProvider templateAvailabilityProvider;
private ResourceProperties resourceProperties;
private Map<String, Object> model = new HashMap<>();
private HttpServletRequest request = new MockHttpServletRequest();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.refresh();
this.resourceProperties = new ResourceProperties();
TemplateAvailabilityProviders templateAvailabilityProviders = new TestTemplateAvailabilityProviders(
this.templateAvailabilityProvider);
this.resolver = new DefaultErrorViewResolver(applicationContext,
this.resourceProperties, templateAvailabilityProviders);
}
@Test
public void createWhenApplicationContextIsNullShouldThrowException()
throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("ApplicationContext must not be null");
new DefaultErrorViewResolver(null, new ResourceProperties());
}
@Test
public void createWhenResourcePropertiesIsNullShouldThrowException()
throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("ResourceProperties must not be null");
new DefaultErrorViewResolver(mock(ApplicationContext.class), null);
}
@Test
public void resolveWhenNoMatchShouldReturnNull() throws Exception {
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
assertThat(resolved).isNull();
}
@Test
public void resolveWhenExactTemplateMatchShouldReturnTemplate() throws Exception {
given(this.templateAvailabilityProvider.isTemplateAvailable(eq("error/404"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class))).willReturn(true);
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
assertThat(resolved).isNotNull();
assertThat(resolved.getViewName()).isEqualTo("error/404");
verify(this.templateAvailabilityProvider).isTemplateAvailable(eq("error/404"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class));
verifyNoMoreInteractions(this.templateAvailabilityProvider);
}
@Test
public void resolveWhenSeries5xxTemplateMatchShouldReturnTemplate() throws Exception {
given(this.templateAvailabilityProvider.isTemplateAvailable(eq("error/5xx"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class))).willReturn(true);
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.SERVICE_UNAVAILABLE, this.model);
assertThat(resolved.getViewName()).isEqualTo("error/5xx");
}
@Test
public void resolveWhenSeries4xxTemplateMatchShouldReturnTemplate() throws Exception {
given(this.templateAvailabilityProvider.isTemplateAvailable(eq("error/4xx"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class))).willReturn(true);
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
assertThat(resolved.getViewName()).isEqualTo("error/4xx");
}
@Test
public void resolveWhenExactResourceMatchShouldReturnResource() throws Exception {
setResourceLocation("/exact");
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
MockHttpServletResponse response = render(resolved);
assertThat(response.getContentAsString().trim()).isEqualTo("exact/404");
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);
}
@Test
public void resolveWhenSeries4xxResourceMatchShouldReturnResource() throws Exception {
setResourceLocation("/4xx");
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
MockHttpServletResponse response = render(resolved);
assertThat(response.getContentAsString().trim()).isEqualTo("4xx/4xx");
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);
}
@Test
public void resolveWhenSeries5xxResourceMatchShouldReturnResource() throws Exception {
setResourceLocation("/5xx");
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.INTERNAL_SERVER_ERROR, this.model);
MockHttpServletResponse response = render(resolved);
assertThat(response.getContentAsString().trim()).isEqualTo("5xx/5xx");
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);
}
@Test
public void resolveWhenTemplateAndResourceMatchShouldFavorTemplate()
throws Exception {
setResourceLocation("/exact");
given(this.templateAvailabilityProvider.isTemplateAvailable(eq("error/404"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class))).willReturn(true);
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
assertThat(resolved.getViewName()).isEqualTo("error/404");
}
@Test
public void resolveWhenExactResourceMatchAndSeriesTemplateMatchShouldFavorResource()
throws Exception {
setResourceLocation("/exact");
given(this.templateAvailabilityProvider.isTemplateAvailable(eq("error/4xx"),
any(Environment.class), any(ClassLoader.class),
any(ResourceLoader.class))).willReturn(true);
ModelAndView resolved = this.resolver.resolveErrorView(this.request,
HttpStatus.NOT_FOUND, this.model);
MockHttpServletResponse response = render(resolved);
assertThat(response.getContentAsString().trim()).isEqualTo("exact/404");
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);
}
@Test
public void orderShouldBeLowest() throws Exception {
assertThat(this.resolver.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
}
@Test
public void setOrderShouldChangeOrder() throws Exception {
this.resolver.setOrder(123);
assertThat(this.resolver.getOrder()).isEqualTo(123);
}
private void setResourceLocation(String path) {
String packageName = getClass().getPackage().getName();
this.resourceProperties.setStaticLocations(new String[] {
"classpath:" + packageName.replace('.', '/') + path + "/" });
}
private MockHttpServletResponse render(ModelAndView modelAndView) throws Exception {
MockHttpServletResponse response = new MockHttpServletResponse();
modelAndView.getView().render(this.model, this.request, response);
return response;
}
private static class TestTemplateAvailabilityProviders
extends TemplateAvailabilityProviders {
TestTemplateAvailabilityProviders(TemplateAvailabilityProvider provider) {
super(Collections.singletonList(provider));
}
}
}