16
16
17
17
package org .springframework .boot .actuate .autoconfigure .web .servlet ;
18
18
19
+ import java .util .Collections ;
19
20
import java .util .Map ;
21
+ import java .util .function .Consumer ;
22
+
23
+ import javax .validation .Valid ;
24
+ import javax .validation .constraints .NotEmpty ;
20
25
21
26
import org .junit .jupiter .api .Test ;
22
27
25
30
import org .springframework .boot .actuate .autoconfigure .web .server .ManagementContextAutoConfiguration ;
26
31
import org .springframework .boot .actuate .endpoint .annotation .Endpoint ;
27
32
import org .springframework .boot .actuate .endpoint .annotation .ReadOperation ;
33
+ import org .springframework .boot .actuate .endpoint .web .annotation .RestControllerEndpoint ;
28
34
import org .springframework .boot .autoconfigure .AutoConfigurations ;
29
35
import org .springframework .boot .autoconfigure .web .servlet .DispatcherServletAutoConfiguration ;
30
36
import org .springframework .boot .autoconfigure .web .servlet .ServletWebServerFactoryAutoConfiguration ;
31
37
import 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 ;
32
40
import org .springframework .boot .test .context .runner .WebApplicationContextRunner ;
33
41
import org .springframework .boot .web .context .ServerPortInfoApplicationContextInitializer ;
34
42
import org .springframework .boot .web .servlet .context .AnnotationConfigServletWebServerApplicationContext ;
35
43
import org .springframework .http .MediaType ;
36
- 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 ;
37
48
import org .springframework .web .reactive .function .client .ClientResponse ;
38
49
import org .springframework .web .reactive .function .client .WebClient ;
39
50
@@ -54,40 +65,80 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
54
65
ServletManagementContextAutoConfiguration .class , WebEndpointAutoConfiguration .class ,
55
66
EndpointAutoConfiguration .class , DispatcherServletAutoConfiguration .class ,
56
67
ErrorMvcAutoConfiguration .class ))
57
- .withUserConfiguration (FailingEndpoint .class )
58
- .withInitializer (new ServerPortInfoApplicationContextInitializer ()).withPropertyValues (
59
- "server.port=0" , "management.server.port=0" , "management.endpoints.web.exposure.include=*" );
68
+ .withUserConfiguration (FailingEndpoint .class , FailingControllerEndpoint .class )
69
+ .withInitializer (new ServerPortInfoApplicationContextInitializer ())
70
+ .withPropertyValues ("server.port=0" , "management.server.port=0" ,
71
+ "management.endpoints.web.exposure.include=*" , "server.error.include-exception=true" ,
72
+ "server.error.include-message=always" , "server.error.include-binding-errors=always" );
60
73
61
74
@ Test // gh-17938
62
- @ SuppressWarnings ("unchecked" )
63
- void errorPageAndErrorControllerAreUsed () {
64
- this .runner .run ((context ) -> {
65
- String port = context .getEnvironment ().getProperty ("local.management.port" );
66
- WebClient client = WebClient .create ("http://localhost:" + port );
75
+ void errorEndpointIsUsedWithEndpoint () {
76
+ this .runner .run (withWebTestClient ((client ) -> {
67
77
ClientResponse response = client .get ().uri ("actuator/fail" ).accept (MediaType .APPLICATION_JSON ).exchange ()
68
78
.block ();
69
- Map <Object , Object > body = response .bodyToMono (Map .class ).block ();
70
- assertThat (body ).containsEntry ("message" , "" );
71
- assertThat (body ).doesNotContainKey ("trace" );
72
- });
79
+ Map <String , ?> body = getResponseBody (response );
80
+ assertThat (body ).hasEntrySatisfying ("exception" ,
81
+ (value ) -> assertThat (value ).asString ().contains ("IllegalStateException" ));
82
+ assertThat (body ).hasEntrySatisfying ("message" ,
83
+ (value ) -> assertThat (value ).asString ().contains ("Epic Fail" ));
84
+ }));
73
85
}
74
86
75
87
@ Test
76
88
void errorPageAndErrorControllerIncludeDetails () {
77
89
this .runner .withPropertyValues ("server.error.include-stacktrace=always" , "server.error.include-message=always" )
78
- .run ((context ) -> {
79
- String port = context .getEnvironment ().getProperty ("local.management.port" );
80
- WebClient client = WebClient .create ("http://localhost:" + port );
90
+ .run (withWebTestClient ((client ) -> {
81
91
ClientResponse response = client .get ().uri ("actuator/fail" ).accept (MediaType .APPLICATION_JSON )
82
92
.exchange ().block ();
83
- Map <Object , Object > body = response .bodyToMono (Map .class ).block ();
84
- assertThat (body ).containsEntry ("message" , "Epic Fail" );
93
+ Map <String , ?> body = getResponseBody (response );
94
+ assertThat (body ).hasEntrySatisfying ("message" ,
95
+ (value ) -> assertThat (value ).asString ().contains ("Epic Fail" ));
85
96
assertThat (body ).hasEntrySatisfying ("trace" , (value ) -> assertThat (value ).asString ()
86
97
.contains ("java.lang.IllegalStateException: Epic Fail" ));
87
- });
98
+ }));
99
+ }
100
+
101
+ @ Test
102
+ void errorEndpointIsUsedWithRestControllerEndpoint () {
103
+ this .runner .run (withWebTestClient ((client ) -> {
104
+ ClientResponse response = client .get ().uri ("actuator/failController" ).accept (MediaType .APPLICATION_JSON )
105
+ .exchange ().block ();
106
+ Map <String , ?> body = getResponseBody (response );
107
+ assertThat (body ).hasEntrySatisfying ("exception" ,
108
+ (value ) -> assertThat (value ).asString ().contains ("IllegalStateException" ));
109
+ assertThat (body ).hasEntrySatisfying ("message" ,
110
+ (value ) -> assertThat (value ).asString ().contains ("Epic Fail" ));
111
+ }));
112
+ }
113
+
114
+ @ Test
115
+ void errorEndpointIsUsedWithRestControllerEndpointOnBindingError () {
116
+ this .runner .run (withWebTestClient ((client ) -> {
117
+ ClientResponse response = client .post ().uri ("actuator/failController" )
118
+ .bodyValue (Collections .singletonMap ("content" , "" )).accept (MediaType .APPLICATION_JSON ).exchange ()
119
+ .block ();
120
+ Map <String , ?> body = getResponseBody (response );
121
+ assertThat (body ).hasEntrySatisfying ("exception" ,
122
+ (value ) -> assertThat (value ).asString ().contains ("MethodArgumentNotValidException" ));
123
+ assertThat (body ).hasEntrySatisfying ("message" ,
124
+ (value ) -> assertThat (value ).asString ().contains ("Validation failed" ));
125
+ assertThat (body ).hasEntrySatisfying ("errors" , (value ) -> assertThat (value ).asList ().isNotEmpty ());
126
+ }));
127
+ }
128
+
129
+ private ContextConsumer <AssertableWebApplicationContext > withWebTestClient (Consumer <WebClient > webClient ) {
130
+ return (context ) -> {
131
+ String port = context .getEnvironment ().getProperty ("local.management.port" );
132
+ WebClient client = WebClient .create ("http://localhost:" + port );
133
+ webClient .accept (client );
134
+ };
135
+ }
136
+
137
+ @ SuppressWarnings ("unchecked" )
138
+ private Map <String , ?> getResponseBody (ClientResponse response ) {
139
+ return (Map <String , ?>) response .bodyToMono (Map .class ).block ();
88
140
}
89
141
90
- @ Component
91
142
@ Endpoint (id = "fail" )
92
143
static class FailingEndpoint {
93
144
@@ -98,4 +149,35 @@ String fail() {
98
149
99
150
}
100
151
152
+ @ RestControllerEndpoint (id = "failController" )
153
+ static class FailingControllerEndpoint {
154
+
155
+ @ GetMapping
156
+ String fail () {
157
+ throw new IllegalStateException ("Epic Fail" );
158
+ }
159
+
160
+ @ PostMapping (produces = "application/json" )
161
+ @ ResponseBody
162
+ String bodyValidation (@ Valid @ RequestBody TestBody body ) {
163
+ return body .getContent ();
164
+ }
165
+
166
+ }
167
+
168
+ public static class TestBody {
169
+
170
+ @ NotEmpty
171
+ private String content ;
172
+
173
+ public String getContent () {
174
+ return this .content ;
175
+ }
176
+
177
+ public void setContent (String content ) {
178
+ this .content = content ;
179
+ }
180
+
181
+ }
182
+
101
183
}
0 commit comments