/*
* 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.actuate.endpoint.mvc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HealthMvcEndpoint}.
*
* @author Christian Dupuis
* @author Dave Syer
* @author Andy Wilkinson
* @author EddĂș MelĂ©ndez
* @author Madhura Bhave
*/
public class HealthMvcEndpointTests {
private static final List<String> SECURITY_ROLES = new ArrayList<>(
Arrays.asList("HERO"));
private HttpServletRequest request = new MockHttpServletRequest();
private HealthEndpoint endpoint = null;
private HealthMvcEndpoint mvc = null;
private HttpServletRequest defaultUser = createAuthenticationRequest("ROLE_ACTUATOR");
private HttpServletRequest hero = createAuthenticationRequest("HERO");
private HttpServletRequest createAuthenticationRequest(String role) {
MockServletContext servletContext = new MockServletContext();
servletContext.declareRoles(role);
return new MockHttpServletRequest(servletContext);
}
@Before
public void init() {
this.endpoint = mock(HealthEndpoint.class);
given(this.endpoint.isEnabled()).willReturn(true);
this.mvc = new HealthMvcEndpoint(this.endpoint);
}
@Test
public void up() {
given(this.endpoint.invoke()).willReturn(new Health.Builder().up().build());
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
}
@SuppressWarnings("unchecked")
@Test
public void down() {
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof ResponseEntity).isTrue();
ResponseEntity<Health> response = (ResponseEntity<Health>) result;
assertThat(response.getBody().getStatus() == Status.DOWN).isTrue();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
}
@Test
@SuppressWarnings("unchecked")
public void customMapping() {
given(this.endpoint.invoke())
.willReturn(new Health.Builder().status("OK").build());
this.mvc.setStatusMapping(
Collections.singletonMap("OK", HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof ResponseEntity).isTrue();
ResponseEntity<Health> response = (ResponseEntity<Health>) result;
assertThat(response.getBody().getStatus().equals(new Status("OK"))).isTrue();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
@SuppressWarnings("unchecked")
public void customMappingWithRelaxedName() {
given(this.endpoint.invoke())
.willReturn(new Health.Builder().outOfService().build());
this.mvc.setStatusMapping(Collections.singletonMap("out-OF-serVice",
HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof ResponseEntity).isTrue();
ResponseEntity<Health> response = (ResponseEntity<Health>) result;
assertThat(response.getBody().getStatus().equals(Status.OUT_OF_SERVICE)).isTrue();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void presenceOfRightRoleShouldExposeDetails() {
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test
public void managementSecurityDisabledShouldExposeDetails() throws Exception {
this.mvc = new HealthMvcEndpoint(this.endpoint, false);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test
public void rightRoleNotPresentShouldNotExposeDetails() {
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.hero, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isNull();
}
@Test
public void rightAuthorityPresentShouldExposeDetails() throws Exception {
this.mvc = new HealthMvcEndpoint(this.endpoint, true, SECURITY_ROLES);
Authentication principal = mock(Authentication.class);
Set<SimpleGrantedAuthority> authorities = Collections
.singleton(new SimpleGrantedAuthority("HERO"));
doReturn(authorities).when(principal).getAuthorities();
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, principal);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test
public void customRolePresentShouldExposeDetails() {
this.mvc = new HealthMvcEndpoint(this.endpoint, true, SECURITY_ROLES);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.hero, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test
public void customRoleShouldNotExposeDetailsForDefaultRole() {
this.mvc = new HealthMvcEndpoint(this.endpoint, true, SECURITY_ROLES);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isNull();
}
@Test
public void customRoleFromListShouldExposeDetails() {
// gh-8314
this.mvc = new HealthMvcEndpoint(this.endpoint, true,
Arrays.asList("HERO", "USER"));
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.hero, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test
public void customRoleFromListShouldNotExposeDetailsForDefaultRole() {
// gh-8314
this.mvc = new HealthMvcEndpoint(this.endpoint, true,
Arrays.asList("HERO", "USER"));
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isNull();
}
@Test
public void healthIsCached() {
given(this.endpoint.getTimeToLive()).willReturn(10000L);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.defaultUser, null);
assertThat(result instanceof Health).isTrue();
Health health = (Health) result;
assertThat(health.getStatus() == Status.UP).isTrue();
assertThat(health.getDetails()).hasSize(1);
assertThat(health.getDetails().get("foo")).isEqualTo("bar");
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
result = this.mvc.invoke(this.request, null); // insecure now
assertThat(result instanceof Health).isTrue();
health = (Health) result;
// so the result is cached
assertThat(health.getStatus() == Status.UP).isTrue();
// but the details are hidden
assertThat(health.getDetails()).isEmpty();
}
@Test
public void noCachingWhenTimeToLiveIsZero() {
given(this.endpoint.getTimeToLive()).willReturn(0L);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
result = this.mvc.invoke(this.request, null);
@SuppressWarnings("unchecked")
Health health = ((ResponseEntity<Health>) result).getBody();
assertThat(health.getStatus() == Status.DOWN).isTrue();
}
@Test
public void newValueIsReturnedOnceTtlExpires() throws InterruptedException {
given(this.endpoint.getTimeToLive()).willReturn(50L);
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.request, null);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
Thread.sleep(100);
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
result = this.mvc.invoke(this.request, null);
@SuppressWarnings("unchecked")
Health health = ((ResponseEntity<Health>) result).getBody();
assertThat(health.getStatus() == Status.DOWN).isTrue();
}
}