19
19
import java .util .Arrays ;
20
20
import java .util .Collections ;
21
21
import java .util .Map ;
22
- import java .util .function .BiConsumer ;
23
22
import java .util .function .Consumer ;
24
23
25
24
import org .junit .Test ;
39
38
import org .springframework .boot .actuate .endpoint .web .EndpointMediaTypes ;
40
39
import org .springframework .boot .actuate .endpoint .web .PathMapper ;
41
40
import org .springframework .boot .actuate .endpoint .web .annotation .WebEndpointDiscoverer ;
42
- import org .springframework .boot .web .embedded .netty .NettyReactiveWebServerFactory ;
41
+ import org .springframework .boot .autoconfigure .AutoConfigurations ;
42
+ import org .springframework .boot .autoconfigure .web .reactive .HttpHandlerAutoConfiguration ;
43
+ import org .springframework .boot .autoconfigure .web .reactive .ReactiveWebServerFactoryAutoConfiguration ;
44
+ import org .springframework .boot .autoconfigure .web .reactive .WebFluxAutoConfiguration ;
45
+ import org .springframework .boot .test .context .assertj .AssertableReactiveWebApplicationContext ;
46
+ import org .springframework .boot .test .context .runner .ContextConsumer ;
47
+ import org .springframework .boot .test .context .runner .ReactiveWebApplicationContextRunner ;
43
48
import org .springframework .boot .web .reactive .context .AnnotationConfigReactiveWebServerApplicationContext ;
44
- import org .springframework .boot .web .reactive .context .ReactiveWebServerInitializedEvent ;
45
49
import org .springframework .context .ApplicationContext ;
46
- import org .springframework .context .ApplicationListener ;
47
50
import org .springframework .context .annotation .Bean ;
48
51
import org .springframework .context .annotation .Configuration ;
49
52
import org .springframework .context .annotation .Import ;
50
53
import org .springframework .core .convert .support .DefaultConversionService ;
51
54
import org .springframework .http .HttpStatus ;
52
55
import org .springframework .http .MediaType ;
53
- import org .springframework .http .server .reactive .HttpHandler ;
54
56
import org .springframework .test .web .reactive .server .WebTestClient ;
55
57
import org .springframework .util .Base64Utils ;
56
58
import org .springframework .web .cors .CorsConfiguration ;
57
- import org .springframework .web .reactive .config .EnableWebFlux ;
58
- import org .springframework .web .server .adapter .WebHttpHandlerBuilder ;
59
59
60
60
import static org .mockito .ArgumentMatchers .any ;
61
61
import static org .mockito .ArgumentMatchers .eq ;
67
67
* Tests for {@link CloudFoundryWebFluxEndpointHandlerMapping}.
68
68
*
69
69
* @author Madhura Bhave
70
+ * @author Stephane Nicoll
70
71
*/
71
72
public class CloudFoundryWebFluxEndpointIntegrationTests {
72
73
@@ -76,47 +77,54 @@ public class CloudFoundryWebFluxEndpointIntegrationTests {
76
77
private static ReactiveCloudFoundrySecurityService securityService = mock (
77
78
ReactiveCloudFoundrySecurityService .class );
78
79
80
+ private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner (
81
+ AnnotationConfigReactiveWebServerApplicationContext ::new )
82
+ .withConfiguration (
83
+ AutoConfigurations .of (WebFluxAutoConfiguration .class ,
84
+ HttpHandlerAutoConfiguration .class ,
85
+ ReactiveWebServerFactoryAutoConfiguration .class ))
86
+ .withUserConfiguration (TestEndpointConfiguration .class )
87
+ .withPropertyValues ("server.port=0" );
88
+
79
89
@ Test
80
90
public void operationWithSecurityInterceptorForbidden () {
81
91
given (tokenValidator .validate (any ())).willReturn (Mono .empty ());
82
92
given (securityService .getAccessLevel (any (), eq ("app-id" )))
83
93
.willReturn (Mono .just (AccessLevel .RESTRICTED ));
84
- load (TestEndpointConfiguration .class ,
85
- (client ) -> client .get ().uri ("/cfApplication/test" )
86
- .accept (MediaType .APPLICATION_JSON )
87
- .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
88
- .expectStatus ().isEqualTo (HttpStatus .FORBIDDEN ));
94
+ this .contextRunner .run (withWebTestClient ((client ) -> client .get ()
95
+ .uri ("/cfApplication/test" ).accept (MediaType .APPLICATION_JSON )
96
+ .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
97
+ .expectStatus ().isEqualTo (HttpStatus .FORBIDDEN )));
89
98
}
90
99
91
100
@ Test
92
101
public void operationWithSecurityInterceptorSuccess () {
93
102
given (tokenValidator .validate (any ())).willReturn (Mono .empty ());
94
103
given (securityService .getAccessLevel (any (), eq ("app-id" )))
95
104
.willReturn (Mono .just (AccessLevel .FULL ));
96
- load (TestEndpointConfiguration .class ,
97
- (client ) -> client .get ().uri ("/cfApplication/test" )
98
- .accept (MediaType .APPLICATION_JSON )
99
- .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
100
- .expectStatus ().isEqualTo (HttpStatus .OK ));
105
+ this .contextRunner .run (withWebTestClient ((client ) -> client .get ()
106
+ .uri ("/cfApplication/test" ).accept (MediaType .APPLICATION_JSON )
107
+ .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
108
+ .expectStatus ().isEqualTo (HttpStatus .OK )));
101
109
}
102
110
103
111
@ Test
104
112
public void responseToOptionsRequestIncludesCorsHeaders () {
105
- load ( TestEndpointConfiguration . class , (client ) -> client .options ()
113
+ this . contextRunner . run ( withWebTestClient ( (client ) -> client .options ()
106
114
.uri ("/cfApplication/test" ).accept (MediaType .APPLICATION_JSON )
107
115
.header ("Access-Control-Request-Method" , "POST" )
108
116
.header ("Origin" , "http://example.com" ).exchange ().expectStatus ().isOk ()
109
117
.expectHeader ()
110
118
.valueEquals ("Access-Control-Allow-Origin" , "http://example.com" )
111
- .expectHeader ().valueEquals ("Access-Control-Allow-Methods" , "GET,POST" ));
119
+ .expectHeader ().valueEquals ("Access-Control-Allow-Methods" , "GET,POST" ))) ;
112
120
}
113
121
114
122
@ Test
115
123
public void linksToOtherEndpointsWithFullAccess () {
116
124
given (tokenValidator .validate (any ())).willReturn (Mono .empty ());
117
125
given (securityService .getAccessLevel (any (), eq ("app-id" )))
118
126
.willReturn (Mono .just (AccessLevel .FULL ));
119
- load ( TestEndpointConfiguration . class , (client ) -> client .get ()
127
+ this . contextRunner . run ( withWebTestClient ( (client ) -> client .get ()
120
128
.uri ("/cfApplication" ).accept (MediaType .APPLICATION_JSON )
121
129
.header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
122
130
.expectStatus ().isOk ().expectBody ().jsonPath ("_links.length()" )
@@ -128,37 +136,35 @@ public void linksToOtherEndpointsWithFullAccess() {
128
136
.isEqualTo (false ).jsonPath ("_links.test.href" ).isNotEmpty ()
129
137
.jsonPath ("_links.test.templated" ).isEqualTo (false )
130
138
.jsonPath ("_links.test-part.href" ).isNotEmpty ()
131
- .jsonPath ("_links.test-part.templated" ).isEqualTo (true ));
139
+ .jsonPath ("_links.test-part.templated" ).isEqualTo (true ))) ;
132
140
}
133
141
134
142
@ Test
135
143
public void linksToOtherEndpointsForbidden () {
136
144
CloudFoundryAuthorizationException exception = new CloudFoundryAuthorizationException (
137
145
Reason .INVALID_TOKEN , "invalid-token" );
138
146
willThrow (exception ).given (tokenValidator ).validate (any ());
139
- load (TestEndpointConfiguration .class ,
140
- (client ) -> client .get ().uri ("/cfApplication" )
141
- .accept (MediaType .APPLICATION_JSON )
142
- .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
143
- .expectStatus ().isUnauthorized ());
147
+ this .contextRunner .run (withWebTestClient ((client ) -> client .get ()
148
+ .uri ("/cfApplication" ).accept (MediaType .APPLICATION_JSON )
149
+ .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
150
+ .expectStatus ().isUnauthorized ()));
144
151
}
145
152
146
153
@ Test
147
154
public void linksToOtherEndpointsWithRestrictedAccess () {
148
155
given (tokenValidator .validate (any ())).willReturn (Mono .empty ());
149
156
given (securityService .getAccessLevel (any (), eq ("app-id" )))
150
157
.willReturn (Mono .just (AccessLevel .RESTRICTED ));
151
- load (TestEndpointConfiguration .class ,
152
- (client ) -> client .get ().uri ("/cfApplication" )
153
- .accept (MediaType .APPLICATION_JSON )
154
- .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
155
- .expectStatus ().isOk ().expectBody ().jsonPath ("_links.length()" )
156
- .isEqualTo (2 ).jsonPath ("_links.self.href" ).isNotEmpty ()
157
- .jsonPath ("_links.self.templated" ).isEqualTo (false )
158
- .jsonPath ("_links.info.href" ).isNotEmpty ()
159
- .jsonPath ("_links.info.templated" ).isEqualTo (false )
160
- .jsonPath ("_links.env" ).doesNotExist ().jsonPath ("_links.test" )
161
- .doesNotExist ().jsonPath ("_links.test-part" ).doesNotExist ());
158
+ this .contextRunner .run (withWebTestClient ((client ) -> client .get ()
159
+ .uri ("/cfApplication" ).accept (MediaType .APPLICATION_JSON )
160
+ .header ("Authorization" , "bearer " + mockAccessToken ()).exchange ()
161
+ .expectStatus ().isOk ().expectBody ().jsonPath ("_links.length()" )
162
+ .isEqualTo (2 ).jsonPath ("_links.self.href" ).isNotEmpty ()
163
+ .jsonPath ("_links.self.templated" ).isEqualTo (false )
164
+ .jsonPath ("_links.info.href" ).isNotEmpty ()
165
+ .jsonPath ("_links.info.templated" ).isEqualTo (false ).jsonPath ("_links.env" )
166
+ .doesNotExist ().jsonPath ("_links.test" ).doesNotExist ()
167
+ .jsonPath ("_links.test-part" ).doesNotExist ()));
162
168
}
163
169
164
170
private AnnotationConfigReactiveWebServerApplicationContext createApplicationContext (
@@ -168,23 +174,14 @@ private AnnotationConfigReactiveWebServerApplicationContext createApplicationCon
168
174
return context ;
169
175
}
170
176
171
- private void load (Class <?> configuration , Consumer <WebTestClient > clientConsumer ) {
172
- BiConsumer <ApplicationContext , WebTestClient > consumer = (context ,
173
- client ) -> clientConsumer .accept (client );
174
- AnnotationConfigReactiveWebServerApplicationContext context = createApplicationContext (
175
- configuration , CloudFoundryReactiveConfiguration .class );
176
- context .refresh ();
177
- try {
178
- consumer .accept (context , WebTestClient .bindToServer ()
179
- .baseUrl ("http://localhost:" + getPort (context )).build ());
180
- }
181
- finally {
182
- context .close ();
183
- }
184
- }
185
-
186
- protected int getPort (AnnotationConfigReactiveWebServerApplicationContext context ) {
187
- return context .getBean (CloudFoundryReactiveConfiguration .class ).port ;
177
+ private ContextConsumer <AssertableReactiveWebApplicationContext > withWebTestClient (
178
+ Consumer <WebTestClient > clientConsumer ) {
179
+ return (context ) -> {
180
+ int port = ((AnnotationConfigReactiveWebServerApplicationContext ) context
181
+ .getSourceApplicationContext ()).getWebServer ().getPort ();
182
+ clientConsumer .accept (WebTestClient .bindToServer ()
183
+ .baseUrl ("http://localhost:" + port ).build ());
184
+ };
188
185
}
189
186
190
187
private String mockAccessToken () {
@@ -194,11 +191,8 @@ private String mockAccessToken() {
194
191
}
195
192
196
193
@ Configuration
197
- @ EnableWebFlux
198
194
static class CloudFoundryReactiveConfiguration {
199
195
200
- private int port ;
201
-
202
196
@ Bean
203
197
public CloudFoundrySecurityInterceptor interceptor () {
204
198
return new CloudFoundrySecurityInterceptor (tokenValidator , securityService ,
@@ -242,21 +236,6 @@ public EndpointDelegate endpointDelegate() {
242
236
return mock (EndpointDelegate .class );
243
237
}
244
238
245
- @ Bean
246
- public NettyReactiveWebServerFactory netty () {
247
- return new NettyReactiveWebServerFactory (0 );
248
- }
249
-
250
- @ Bean
251
- public HttpHandler httpHandler (ApplicationContext applicationContext ) {
252
- return WebHttpHandlerBuilder .applicationContext (applicationContext ).build ();
253
- }
254
-
255
- @ Bean
256
- public ApplicationListener <ReactiveWebServerInitializedEvent > serverInitializedListener () {
257
- return (event ) -> this .port = event .getWebServer ().getPort ();
258
- }
259
-
260
239
}
261
240
262
241
@ Endpoint (id = "test" )
0 commit comments