11/*
2- * Copyright 2012-2019 the original author or authors.
2+ * Copyright 2012-2020 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1616
1717package org .springframework .boot .actuate .autoconfigure .web .servlet ;
1818
19+ import java .util .Collections ;
20+ import java .util .Map ;
21+ import java .util .function .Consumer ;
22+
23+ import javax .validation .Valid ;
24+ import javax .validation .constraints .NotEmpty ;
25+
1926import org .junit .jupiter .api .Test ;
2027
2128import org .springframework .boot .actuate .autoconfigure .endpoint .EndpointAutoConfiguration ;
2229import org .springframework .boot .actuate .autoconfigure .endpoint .web .WebEndpointAutoConfiguration ;
2330import org .springframework .boot .actuate .autoconfigure .web .server .ManagementContextAutoConfiguration ;
2431import org .springframework .boot .actuate .endpoint .annotation .Endpoint ;
2532import org .springframework .boot .actuate .endpoint .annotation .ReadOperation ;
33+ import org .springframework .boot .actuate .endpoint .web .annotation .RestControllerEndpoint ;
2634import org .springframework .boot .autoconfigure .AutoConfigurations ;
2735import org .springframework .boot .autoconfigure .web .servlet .DispatcherServletAutoConfiguration ;
2836import org .springframework .boot .autoconfigure .web .servlet .ServletWebServerFactoryAutoConfiguration ;
2937import org .springframework .boot .autoconfigure .web .servlet .error .ErrorMvcAutoConfiguration ;
38+ import org .springframework .boot .test .context .assertj .AssertableWebApplicationContext ;
39+ import org .springframework .boot .test .context .runner .ContextConsumer ;
3040import org .springframework .boot .test .context .runner .WebApplicationContextRunner ;
3141import org .springframework .boot .web .context .ServerPortInfoApplicationContextInitializer ;
3242import org .springframework .boot .web .servlet .context .AnnotationConfigServletWebServerApplicationContext ;
3343import org .springframework .http .MediaType ;
34- import org .springframework .stereotype .Component ;
44+ import org .springframework .web .bind .annotation .GetMapping ;
45+ import org .springframework .web .bind .annotation .PostMapping ;
46+ import org .springframework .web .bind .annotation .RequestBody ;
47+ import org .springframework .web .bind .annotation .ResponseBody ;
3548import org .springframework .web .reactive .function .client .ClientResponse ;
3649import org .springframework .web .reactive .function .client .WebClient ;
3750
4154 * Integration tests for {@link WebMvcEndpointChildContextConfiguration}.
4255 *
4356 * @author Phillip Webb
57+ * @author Scott Frederick
4458 */
4559class WebMvcEndpointChildContextConfigurationIntegrationTests {
4660
61+ final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner (
62+ AnnotationConfigServletWebServerApplicationContext ::new )
63+ .withConfiguration (AutoConfigurations .of (ServletWebServerFactoryAutoConfiguration .class ,
64+ ManagementContextAutoConfiguration .class , ServletManagementContextAutoConfiguration .class ,
65+ WebEndpointAutoConfiguration .class , EndpointAutoConfiguration .class ,
66+ DispatcherServletAutoConfiguration .class , ErrorMvcAutoConfiguration .class ))
67+ .withUserConfiguration (FailingEndpoint .class , FailingControllerEndpoint .class )
68+ .withInitializer (new ServerPortInfoApplicationContextInitializer ())
69+ .withPropertyValues ("server.port=0" , "management.server.port=0" ,
70+ "management.endpoints.web.exposure.include=*" , "server.error.include-exception=true" );
71+
4772 @ Test // gh-17938
48- void errorPageAndErrorControllerAreUsed () {
49- new WebApplicationContextRunner (AnnotationConfigServletWebServerApplicationContext ::new )
50- .withConfiguration (AutoConfigurations .of (ManagementContextAutoConfiguration .class ,
51- ServletWebServerFactoryAutoConfiguration .class , ServletManagementContextAutoConfiguration .class ,
52- WebEndpointAutoConfiguration .class , EndpointAutoConfiguration .class ,
53- DispatcherServletAutoConfiguration .class , ErrorMvcAutoConfiguration .class ))
54- .withUserConfiguration (FailingEndpoint .class )
55- .withInitializer (new ServerPortInfoApplicationContextInitializer ()).withPropertyValues ("server.port=0" ,
56- "management.server.port=0" , "management.endpoints.web.exposure.include=*" )
57- .run ((context ) -> {
58- String port = context .getEnvironment ().getProperty ("local.management.port" );
59- WebClient client = WebClient .create ("http://localhost:" + port );
60- ClientResponse response = client .get ().uri ("actuator/fail" ).accept (MediaType .APPLICATION_JSON )
61- .exchange ().block ();
62- assertThat (response .bodyToMono (String .class ).block ()).contains ("message\" :\" Epic Fail" );
63- });
73+ void errorEndpointIsUsedWithEndpoint () {
74+ this .contextRunner .run (withWebTestClient ((client ) -> {
75+ ClientResponse response = client .get ().uri ("actuator/fail" ).accept (MediaType .APPLICATION_JSON ).exchange ()
76+ .block ();
77+ Map <String , ?> body = getResponseBody (response );
78+ assertThat (body ).hasEntrySatisfying ("exception" ,
79+ (value ) -> assertThat (value ).asString ().contains ("IllegalStateException" ));
80+ assertThat (body ).hasEntrySatisfying ("message" ,
81+ (value ) -> assertThat (value ).asString ().contains ("Epic Fail" ));
82+ }));
83+ }
84+
85+ @ Test
86+ void errorEndpointIsUsedWithRestControllerEndpoint () {
87+ this .contextRunner .run (withWebTestClient ((client ) -> {
88+ ClientResponse response = client .get ().uri ("actuator/failController" ).accept (MediaType .APPLICATION_JSON )
89+ .exchange ().block ();
90+ Map <String , ?> body = getResponseBody (response );
91+ assertThat (body ).hasEntrySatisfying ("exception" ,
92+ (value ) -> assertThat (value ).asString ().contains ("IllegalStateException" ));
93+ assertThat (body ).hasEntrySatisfying ("message" ,
94+ (value ) -> assertThat (value ).asString ().contains ("Epic Fail" ));
95+ }));
96+ }
97+
98+ @ Test
99+ void errorEndpointIsUsedWithRestControllerEndpointOnBindingError () {
100+ this .contextRunner .run (withWebTestClient ((client ) -> {
101+ ClientResponse response = client .post ().uri ("actuator/failController" )
102+ .bodyValue (Collections .singletonMap ("content" , "" )).accept (MediaType .APPLICATION_JSON ).exchange ()
103+ .block ();
104+ Map <String , ?> body = getResponseBody (response );
105+ assertThat (body ).hasEntrySatisfying ("exception" ,
106+ (value ) -> assertThat (value ).asString ().contains ("MethodArgumentNotValidException" ));
107+ assertThat (body ).hasEntrySatisfying ("message" ,
108+ (value ) -> assertThat (value ).asString ().contains ("Validation failed" ));
109+ assertThat (body ).hasEntrySatisfying ("errors" , (value ) -> assertThat (value ).asList ().isNotEmpty ());
110+ }));
111+ }
112+
113+ private ContextConsumer <AssertableWebApplicationContext > withWebTestClient (Consumer <WebClient > webClient ) {
114+ return (context ) -> {
115+ String port = context .getEnvironment ().getProperty ("local.management.port" );
116+ WebClient client = WebClient .create ("http://localhost:" + port );
117+ webClient .accept (client );
118+ };
119+ }
120+
121+ @ SuppressWarnings ("unchecked" )
122+ private Map <String , ?> getResponseBody (ClientResponse response ) {
123+ return (Map <String , ?>) response .bodyToMono (Map .class ).block ();
64124 }
65125
66- @ Component
67126 @ Endpoint (id = "fail" )
68127 static class FailingEndpoint {
69128
@@ -74,4 +133,35 @@ String fail() {
74133
75134 }
76135
136+ @ RestControllerEndpoint (id = "failController" )
137+ static class FailingControllerEndpoint {
138+
139+ @ GetMapping
140+ String fail () {
141+ throw new IllegalStateException ("Epic Fail" );
142+ }
143+
144+ @ PostMapping (produces = "application/json" )
145+ @ ResponseBody
146+ String bodyValidation (@ Valid @ RequestBody TestBody body ) {
147+ return body .getContent ();
148+ }
149+
150+ }
151+
152+ public static class TestBody {
153+
154+ @ NotEmpty
155+ private String content ;
156+
157+ public String getContent () {
158+ return this .content ;
159+ }
160+
161+ public void setContent (String content ) {
162+ this .content = content ;
163+ }
164+
165+ }
166+
77167}
0 commit comments