21
21
import static org .junit .jupiter .api .Assertions .assertNull ;
22
22
import static org .junit .jupiter .api .Assertions .assertThrows ;
23
23
24
+ import com .fasterxml .jackson .databind .DeserializationFeature ;
25
+ import com .fasterxml .jackson .databind .ObjectMapper ;
24
26
import com .netflix .spinnaker .kork .web .exceptions .NotFoundException ;
27
+ import java .util .HashMap ;
28
+ import java .util .Map ;
25
29
import okhttp3 .mockwebserver .MockResponse ;
26
30
import okhttp3 .mockwebserver .MockWebServer ;
27
31
import org .junit .jupiter .api .AfterAll ;
28
32
import org .junit .jupiter .api .BeforeAll ;
29
33
import org .junit .jupiter .api .Test ;
34
+ import org .junit .jupiter .params .ParameterizedTest ;
35
+ import org .junit .jupiter .params .provider .ValueSource ;
30
36
import org .springframework .http .HttpStatus ;
31
37
import retrofit .RestAdapter ;
32
38
import retrofit .client .Response ;
39
+ import retrofit .converter .JacksonConverter ;
33
40
import retrofit .http .GET ;
34
41
35
42
public class SpinnakerRetrofitErrorHandlerTest {
@@ -55,16 +62,76 @@ public static void shutdownOnce() throws Exception {
55
62
}
56
63
57
64
@ Test
58
- public void testNotFoundIsNotRetryable () throws Exception {
65
+ public void testNotFoundIsNotRetryable () {
59
66
mockWebServer .enqueue (new MockResponse ().setResponseCode (HttpStatus .NOT_FOUND .value ()));
60
67
NotFoundException notFoundException =
61
68
assertThrows (NotFoundException .class , () -> retrofitService .getFoo ());
62
69
assertNotNull (notFoundException .getRetryable ());
63
70
assertFalse (notFoundException .getRetryable ());
64
71
}
65
72
73
+ @ ParameterizedTest (name = "Deserialize response using {0}" )
74
+ // Test the different converters used to deserialize the response body to the
75
+ // SpinnakerServerException.RetrofitErrorResponseBody class:
76
+ //
77
+ // - the JacksonConverter constructed without an ObjectMapper is used in
78
+ // Clouddriver's RestAdapter to communicate with Front50Service
79
+ //
80
+ // - the JacksonConverter constructed with an ObjectMapper is used in Rosco's RestAdapter to
81
+ // communicate with Clouddriver
82
+ //
83
+ // - GSONConverter is the default converter used by Retrofit if no converter
84
+ // is set when building out the RestAdapter
85
+ @ ValueSource (
86
+ strings = {"Default_GSONConverter" , "JacksonConverter" , "JacksonConverterWithObjectMapper" })
87
+ public void testResponseWithExtraField (String retrofitConverter ) throws Exception {
88
+ Map <String , String > responseBodyMap = new HashMap <>();
89
+ responseBodyMap .put ("timestamp" , "123123123123" );
90
+ responseBodyMap .put ("message" , "Not Found error Message" );
91
+ String responseBodyString = new ObjectMapper ().writeValueAsString (responseBodyMap );
92
+
93
+ RestAdapter .Builder restAdapter =
94
+ new RestAdapter .Builder ()
95
+ .setEndpoint (mockWebServer .url ("/" ).toString ())
96
+ .setErrorHandler (SpinnakerRetrofitErrorHandler .getInstance ());
97
+
98
+ if (retrofitConverter .equals ("JacksonConverter" )) {
99
+ restAdapter .setConverter (new JacksonConverter ());
100
+ } else if (retrofitConverter .equals ("JacksonConverterWithObjectMapper" )) {
101
+ ObjectMapper objectMapper =
102
+ new ObjectMapper ()
103
+ .enable (DeserializationFeature .READ_UNKNOWN_ENUM_VALUES_AS_NULL )
104
+ .disable (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES );
105
+
106
+ restAdapter .setConverter (new JacksonConverter (objectMapper ));
107
+ }
108
+
109
+ RetrofitService retrofitServiceTestConverter =
110
+ restAdapter .build ().create (RetrofitService .class );
111
+
112
+ mockWebServer .enqueue (
113
+ new MockResponse ()
114
+ .setBody (responseBodyString )
115
+ // an arbitrary response code -- one that
116
+ // SpinnakerRetrofitErrorHandler converts to a
117
+ // SpinnakerServerException (or one of its children).
118
+ .setResponseCode (HttpStatus .INTERNAL_SERVER_ERROR .value ()));
119
+
120
+ // If the converter can not deserialize the response body to the
121
+ // SpinnakerServerException.RetrofitErrorResponseBody
122
+ // class, then a RuntimeException will be thrown with a ConversionException nested inside.
123
+ //
124
+ // java.lang.RuntimeException:
125
+ // retrofit.converter.ConversionException:
126
+ // com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field
127
+ // "..."
128
+ //
129
+ // so make sure we get a SpinnakerHttpException from calling getFoo
130
+ assertThrows (SpinnakerHttpException .class , retrofitServiceTestConverter ::getFoo );
131
+ }
132
+
66
133
@ Test
67
- public void testBadRequestIsNotRetryable () throws Exception {
134
+ public void testBadRequestIsNotRetryable () {
68
135
mockWebServer .enqueue (new MockResponse ().setResponseCode (HttpStatus .BAD_REQUEST .value ()));
69
136
SpinnakerHttpException spinnakerHttpException =
70
137
assertThrows (SpinnakerHttpException .class , () -> retrofitService .getFoo ());
@@ -73,7 +140,7 @@ public void testBadRequestIsNotRetryable() throws Exception {
73
140
}
74
141
75
142
@ Test
76
- public void testOtherClientErrorHasNullRetryable () throws Exception {
143
+ public void testOtherClientErrorHasNullRetryable () {
77
144
// Arbitrarily choose GONE as an example of a client (e.g. 4xx) error that
78
145
// we expect to have null retryable
79
146
mockWebServer .enqueue (new MockResponse ().setResponseCode (HttpStatus .GONE .value ()));
0 commit comments