1111
1212import com .retailsvc .http .Request ;
1313import com .retailsvc .http .SchemeValidator ;
14+ import com .retailsvc .http .TypeMapper ;
1415import com .retailsvc .http .spec .HttpMethod ;
1516import com .retailsvc .http .spec .Operation ;
1617import com .retailsvc .http .spec .security .SecurityRequirement ;
2324import com .sun .net .httpserver .HttpExchange ;
2425import java .io .ByteArrayOutputStream ;
2526import java .net .URI ;
27+ import java .nio .charset .StandardCharsets ;
2628import java .util .List ;
2729import java .util .Map ;
2830import java .util .Optional ;
@@ -49,7 +51,8 @@ void allowsRequestWhenValidatorReturnsPrincipal() throws Exception {
4951 Map .of ("bearerAuth" , (req , cred ) -> Optional .of ("user-1" ));
5052
5153 SecurityFilter filter =
52- new SecurityFilter (Map .of ("getX" , op ), schemes , List .of (), validators , false );
54+ new SecurityFilter (
55+ Map .of ("getX" , op ), schemes , List .of (), validators , false , mockJsonMapper ());
5356
5457 HttpExchange ex = mock (HttpExchange .class );
5558 Headers headers = new Headers ();
@@ -88,7 +91,8 @@ void passesThroughWhenOperationHasNoSecurity() throws Exception {
8891 Optional .empty ()); // inherits root, root is empty
8992
9093 SecurityFilter filter =
91- new SecurityFilter (Map .of ("getY" , op ), Map .of (), List .of (), Map .of (), false );
94+ new SecurityFilter (
95+ Map .of ("getY" , op ), Map .of (), List .of (), Map .of (), false , mockJsonMapper ());
9296
9397 HttpExchange ex = mock (HttpExchange .class );
9498 Chain chain = mock (Chain .class );
@@ -117,7 +121,8 @@ void missingCredentialReturns401WithBearerChallenge() throws Exception {
117121 Map .of ("bearerAuth" , new HttpBearer (Optional .empty ())),
118122 List .of (),
119123 Map .of ("bearerAuth" , (req , cred ) -> Optional .of ("never-called" )),
120- false );
124+ false ,
125+ mockJsonMapper ());
121126
122127 HttpExchange ex = mock (HttpExchange .class );
123128 Headers headers = new Headers ();
@@ -159,7 +164,8 @@ void deniedValidatorReturns403WithoutChallenge() throws Exception {
159164 Map .of ("bearerAuth" , new HttpBearer (Optional .empty ())),
160165 List .of (),
161166 Map .of ("bearerAuth" , (req , cred ) -> Optional .empty ()),
162- false );
167+ false ,
168+ mockJsonMapper ());
163169
164170 HttpExchange ex = mock (HttpExchange .class );
165171 Headers headers = new Headers ();
@@ -196,7 +202,8 @@ void apiKeyMissingReturnsApiKeyChallengeHeader() throws Exception {
196202 Map .of ("apiKeyAuth" , new ApiKey ("X-API-Key" , Location .HEADER )),
197203 List .of (),
198204 Map .of ("apiKeyAuth" , (req , cred ) -> Optional .of ("ok" )),
199- false );
205+ false ,
206+ mockJsonMapper ());
200207
201208 HttpExchange ex = mock (HttpExchange .class );
202209 when (ex .getRequestHeaders ()).thenReturn (new Headers ());
@@ -241,7 +248,8 @@ void andGroupRequiresAllSchemesToSucceed() throws Exception {
241248 "bearerAuth" , (req , cred ) -> Optional .of ("bearer-principal" ));
242249
243250 SecurityFilter filter =
244- new SecurityFilter (Map .of ("getX" , op ), schemes , List .of (), validators , false );
251+ new SecurityFilter (
252+ Map .of ("getX" , op ), schemes , List .of (), validators , false , mockJsonMapper ());
245253
246254 HttpExchange ex = mock (HttpExchange .class );
247255 Headers headers = new Headers ();
@@ -295,7 +303,8 @@ void orFallsBackToSecondGroupWhenFirstDenied() throws Exception {
295303 "bearerAuth" , (req , cred ) -> Optional .of ("bearer-ok" ));
296304
297305 SecurityFilter filter =
298- new SecurityFilter (Map .of ("getX" , op ), schemes , List .of (), validators , false );
306+ new SecurityFilter (
307+ Map .of ("getX" , op ), schemes , List .of (), validators , false , mockJsonMapper ());
299308
300309 HttpExchange ex = mock (HttpExchange .class );
301310 Headers headers = new Headers ();
@@ -339,7 +348,8 @@ void externalAuthBypassesEverything() throws Exception {
339348 Map .of ("bearerAuth" , new HttpBearer (Optional .empty ())),
340349 List .of (),
341350 Map .of (), // NO validators
342- /* externalAuth= */ true );
351+ /* externalAuth= */ true ,
352+ mockJsonMapper ());
343353
344354 HttpExchange ex = mock (HttpExchange .class );
345355 Chain chain = mock (Chain .class );
@@ -351,4 +361,24 @@ void externalAuthBypassesEverything() throws Exception {
351361 private static Request newMinimalRequest (String operationId ) {
352362 return new Request (new byte [0 ], null , null , operationId , Map .of (), null , h -> null );
353363 }
364+
365+ private static TypeMapper mockJsonMapper () {
366+ return new TypeMapper () {
367+ @ Override
368+ public Object readFrom (byte [] body , String contentTypeHeader ) {
369+ return null ;
370+ }
371+
372+ @ Override
373+ public byte [] writeTo (Object value ) {
374+ if (value instanceof ProblemDetail pd ) {
375+ return String .format (
376+ "{\" type\" :\" %s\" ,\" title\" :\" %s\" ,\" status\" :%d,\" detail\" :\" %s\" }" ,
377+ pd .type (), pd .title (), pd .status (), pd .detail ())
378+ .getBytes (StandardCharsets .UTF_8 );
379+ }
380+ return "{}" .getBytes (StandardCharsets .UTF_8 );
381+ }
382+ };
383+ }
354384}
0 commit comments