11package com .cloudbees .jenkins ;
22
33import com .google .common .base .Charsets ;
4- import com .google .common .net .HttpHeaders ;
5- import io .restassured .builder .RequestSpecBuilder ;
6- import io .restassured .http .Header ;
7- import io .restassured .specification .RequestSpecification ;
84import jakarta .inject .Inject ;
95import org .apache .commons .io .IOUtils ;
106import org .jenkinsci .plugins .github .config .GitHubPluginConfig ;
117import org .jenkinsci .plugins .github .webhook .GHEventHeader ;
128import org .jenkinsci .plugins .github .webhook .GHEventPayload ;
9+ import org .junit .jupiter .api .AfterEach ;
1310import org .junit .jupiter .api .BeforeEach ;
1411import org .junit .jupiter .api .Test ;
1512import org .jvnet .hudson .test .JenkinsRule ;
1815
1916import java .io .File ;
2017import java .io .IOException ;
18+ import java .net .URI ;
19+ import java .util .Locale ;
20+ import java .net .URLEncoder ;
21+ import java .net .http .HttpClient ;
22+ import java .net .http .HttpRequest ;
23+ import java .net .http .HttpResponse ;
24+ import java .nio .charset .StandardCharsets ;
2125
22- import static io .restassured .RestAssured .given ;
23- import static io .restassured .config .EncoderConfig .encoderConfig ;
24- import static io .restassured .config .RestAssuredConfig .newConfig ;
2526import static jakarta .servlet .http .HttpServletResponse .SC_BAD_REQUEST ;
2627import static jakarta .servlet .http .HttpServletResponse .SC_METHOD_NOT_ALLOWED ;
2728import static jakarta .servlet .http .HttpServletResponse .SC_OK ;
2829import static java .lang .String .format ;
2930import static org .apache .commons .lang3 .ClassUtils .PACKAGE_SEPARATOR ;
31+ import static org .hamcrest .MatcherAssert .assertThat ;
3032import static org .hamcrest .Matchers .containsString ;
33+ import static org .hamcrest .Matchers .is ;
3134import static org .hamcrest .Matchers .notNullValue ;
3235import static org .jenkinsci .plugins .github .test .HookSecretHelper .removeSecretIn ;
3336import static org .jenkinsci .plugins .github .test .HookSecretHelper .storeSecretIn ;
34- import static org .jenkinsci .plugins .github .webhook .RequirePostWithGHHookPayload .Processor .*;
37+ import static org .jenkinsci .plugins .github .webhook .RequirePostWithGHHookPayload .Processor .SHA256_PREFIX ;
38+ import static org .jenkinsci .plugins .github .webhook .RequirePostWithGHHookPayload .Processor .SIGNATURE_HEADER ;
39+ import static org .jenkinsci .plugins .github .webhook .RequirePostWithGHHookPayload .Processor .SIGNATURE_HEADER_SHA256 ;
3540
3641/**
3742 * @author lanwen (Merkushev Kirill)
@@ -43,133 +48,131 @@ public class GitHubWebHookFullTest {
4348 public static final String APPLICATION_JSON = GHEventPayload .PayloadHandler .APPLICATION_JSON ;
4449 public static final String FORM = GHEventPayload .PayloadHandler .FORM_URLENCODED ;
4550
46- public static final Header JSON_CONTENT_TYPE = new Header (HttpHeaders .CONTENT_TYPE , APPLICATION_JSON );
47- public static final Header FORM_CONTENT_TYPE = new Header (HttpHeaders .CONTENT_TYPE , FORM );
48- public static final String NOT_NULL_VALUE = "nonnull" ;
49-
50- private RequestSpecification spec ;
51-
5251 @ Inject
5352 private GitHubPluginConfig config ;
5453
5554 private JenkinsRule jenkins ;
55+ private HttpClient httpClient ;
5656
5757 @ BeforeEach
5858 void before (JenkinsRule rule ) throws Throwable {
5959 jenkins = rule ;
6060 jenkins .getInstance ().getInjector ().injectMembers (this );
61+ httpClient = HttpClient .newHttpClient ();
62+ }
6163
62- spec = new RequestSpecBuilder ()
63- .setConfig (newConfig ()
64- .encoderConfig (encoderConfig ()
65- .defaultContentCharset (Charsets .UTF_8 .name ())
66- // GitHub doesn't add charsets, so don't test with them
67- .appendDefaultContentCharsetToContentTypeIfUndefined (false )))
68- .build ();
64+ @ AfterEach
65+ void after () throws Exception {
66+ if ((Object ) httpClient instanceof AutoCloseable closeable ) { // TODO: replace with httpClient.close() once jenkins.baseline is Java 21+
67+ closeable .close ();
68+ }
6969 }
7070
7171 @ Test
7272 void shouldParseJsonWebHookFromGH () throws Exception {
7373 removeSecretIn (config );
74- given ().spec (spec )
75- .header (eventHeader (GHEvent .PUSH ))
76- .header (JSON_CONTENT_TYPE )
77- .body (classpath ("payloads/push.json" ))
78- .log ().all ()
79- .expect ().log ().all ().statusCode (SC_OK ).request ().post (getPath ());
74+ HttpResponse <String > response = httpClient .send (
75+ HttpRequest .newBuilder (URI .create (getPath ()))
76+ .POST (HttpRequest .BodyPublishers .ofString (classpath ("payloads/push.json" )))
77+ .header ("Content-Type" , APPLICATION_JSON )
78+ .header (GHEventHeader .PayloadHandler .EVENT_HEADER , GHEvent .PUSH .name ().toLowerCase (Locale .ROOT ))
79+ .build (),
80+ HttpResponse .BodyHandlers .ofString ());
81+ assertThat ("status" , response .statusCode (), is (SC_OK ));
8082 }
8183
82-
8384 @ Test
8485 void shouldParseJsonWebHookFromGHWithSignHeader () throws Exception {
8586 String hash = "355e155fc3d10c4e5f2c6086a01281d2e947d932" ;
8687 String hash256 = "85e61999573c7023720a12375e1e55d18a0870e1ef880736f6ffc9273d0519e3" ;
8788 String secret = "123" ;
8889
8990 storeSecretIn (config , secret );
90- given ().spec (spec )
91- .header (eventHeader (GHEvent .PUSH ))
92- .header (JSON_CONTENT_TYPE )
93- .header (SIGNATURE_HEADER , format ("sha1=%s" , hash ))
94- .header (SIGNATURE_HEADER_SHA256 , format ("%s%s" , SHA256_PREFIX , hash256 ))
95- .body (classpath (String .format ("payloads/ping_hash_%s_secret_%s.json" , hash , secret )))
96- .log ().all ()
97- .expect ().log ().all ().statusCode (SC_OK ).request ().post (getPath ());
91+ HttpResponse <String > response = httpClient .send (
92+ HttpRequest .newBuilder (URI .create (getPath ()))
93+ .POST (HttpRequest .BodyPublishers .ofString (
94+ classpath (format ("payloads/ping_hash_%s_secret_%s.json" , hash , secret ))))
95+ .header ("Content-Type" , APPLICATION_JSON )
96+ .header (GHEventHeader .PayloadHandler .EVENT_HEADER , GHEvent .PUSH .name ().toLowerCase (Locale .ROOT ))
97+ .header (SIGNATURE_HEADER , format ("sha1=%s" , hash ))
98+ .header (SIGNATURE_HEADER_SHA256 , format ("%s%s" , SHA256_PREFIX , hash256 ))
99+ .build (),
100+ HttpResponse .BodyHandlers .ofString ());
101+ assertThat ("status" , response .statusCode (), is (SC_OK ));
98102 }
99103
100104 @ Test
101105 void shouldParseFormWebHookOrServiceHookFromGH () throws Exception {
102- given ().spec (spec )
103- .header (eventHeader (GHEvent .PUSH ))
104- .header (FORM_CONTENT_TYPE )
105- .formParam ("payload" , classpath ("payloads/push.json" ))
106- .log ().all ()
107- .expect ().log ().all ().statusCode (SC_OK ).request ().post (getPath ());
106+ String encoded = "payload=" + URLEncoder .encode (classpath ("payloads/push.json" ), StandardCharsets .UTF_8 );
107+ HttpResponse <String > response = httpClient .send (
108+ HttpRequest .newBuilder (URI .create (getPath ()))
109+ .POST (HttpRequest .BodyPublishers .ofString (encoded ))
110+ .header ("Content-Type" , FORM )
111+ .header (GHEventHeader .PayloadHandler .EVENT_HEADER , GHEvent .PUSH .name ().toLowerCase (Locale .ROOT ))
112+ .build (),
113+ HttpResponse .BodyHandlers .ofString ());
114+ assertThat ("status" , response .statusCode (), is (SC_OK ));
108115 }
109116
110117 @ Test
111118 void shouldParsePingFromGH () throws Exception {
112- given ().spec (spec )
113- .header (eventHeader (GHEvent .PING ))
114- .header (JSON_CONTENT_TYPE )
115- .body (classpath ("payloads/ping.json" ))
116- .log ().all ()
117- .expect ().log ().all ()
118- .statusCode (SC_OK )
119- .request ()
120- .post (getPath ());
119+ HttpResponse <String > response = httpClient .send (
120+ HttpRequest .newBuilder (URI .create (getPath ()))
121+ .POST (HttpRequest .BodyPublishers .ofString (classpath ("payloads/ping.json" )))
122+ .header ("Content-Type" , APPLICATION_JSON )
123+ .header (GHEventHeader .PayloadHandler .EVENT_HEADER , GHEvent .PING .name ().toLowerCase (Locale .ROOT ))
124+ .build (),
125+ HttpResponse .BodyHandlers .ofString ());
126+ assertThat ("status" , response .statusCode (), is (SC_OK ));
121127 }
122128
123129 @ Test
124130 void shouldReturnErrOnEmptyPayloadAndHeader () throws Exception {
125- given (). spec ( spec )
126- . log (). all ( )
127- . expect (). log (). all ( )
128- . statusCode ( SC_BAD_REQUEST )
129- . body ( containsString ( "Hook should contain event type" ))
130- . request ()
131- . post ( getPath ( ));
131+ HttpResponse < String > response = httpClient . send (
132+ HttpRequest . newBuilder ( URI . create ( getPath ()) )
133+ . POST ( HttpRequest . BodyPublishers . noBody () )
134+ . build (),
135+ HttpResponse . BodyHandlers . ofString ());
136+ assertThat ( "status" , response . statusCode (), is ( SC_BAD_REQUEST ));
137+ assertThat ( "body" , response . body (), containsString ( "Hook should contain event type" ));
132138 }
133139
134140 @ Test
135141 void shouldReturnErrOnEmptyPayload () throws Exception {
136- given (). spec ( spec )
137- . header ( eventHeader ( GHEvent . PUSH ))
138- . log (). all ( )
139- . expect (). log ().all ( )
140- . statusCode ( SC_BAD_REQUEST )
141- . body ( containsString ( "Hook should contain payload" ))
142- . request ()
143- . post ( getPath ( ));
142+ HttpResponse < String > response = httpClient . send (
143+ HttpRequest . newBuilder ( URI . create ( getPath () ))
144+ . POST ( HttpRequest . BodyPublishers . noBody () )
145+ . header ( GHEventHeader . PayloadHandler . EVENT_HEADER , GHEvent . PUSH . name ().toLowerCase ( Locale . ROOT ) )
146+ . build (),
147+ HttpResponse . BodyHandlers . ofString ());
148+ assertThat ( "status" , response . statusCode (), is ( SC_BAD_REQUEST ));
149+ assertThat ( "body" , response . body (), containsString ( "Hook should contain payload" ));
144150 }
145151
146152 @ Test
147153 void shouldReturnErrOnGetReq () throws Exception {
148- given ().spec (spec )
149- .log ().all ().expect ().log ().all ()
150- .statusCode (SC_METHOD_NOT_ALLOWED )
151- .request ()
152- .get (getPath ());
154+ HttpResponse <String > response = httpClient .send (
155+ HttpRequest .newBuilder (URI .create (getPath ()))
156+ .GET ()
157+ .build (),
158+ HttpResponse .BodyHandlers .ofString ());
159+ assertThat ("status" , response .statusCode (), is (SC_METHOD_NOT_ALLOWED ));
153160 }
154161
155162 @ Test
156163 void shouldProcessSelfTest () throws Exception {
157- given (). spec ( spec )
158- . header ( new Header ( GitHubWebHook . URL_VALIDATION_HEADER , NOT_NULL_VALUE ))
159- . log (). all ( )
160- . expect (). log (). all ( )
161- . statusCode ( SC_OK )
162- . header ( GitHubWebHook . X_INSTANCE_IDENTITY , notNullValue ())
163- . request ()
164- . post ( getPath ());
164+ HttpResponse < String > response = httpClient . send (
165+ HttpRequest . newBuilder ( URI . create ( getPath () ))
166+ . POST ( HttpRequest . BodyPublishers . noBody () )
167+ . header ( GitHubWebHook . URL_VALIDATION_HEADER , "nonnull" )
168+ . build (),
169+ HttpResponse . BodyHandlers . ofString ());
170+ assertThat ( "status" , response . statusCode (), is ( SC_OK ));
171+ assertThat ( "identity header" , response . headers (). firstValue ( GitHubWebHook . X_INSTANCE_IDENTITY ). orElse ( null ), notNullValue ());
165172 }
166173
167- public Header eventHeader (GHEvent event ) {
168- return eventHeader (event .name ().toLowerCase ());
169- }
170-
171- public Header eventHeader (String event ) {
172- return new Header (GHEventHeader .PayloadHandler .EVENT_HEADER , event );
174+ private String getPath () {
175+ return jenkins .getInstance ().getRootUrl () + GitHubWebHook .URLNAME .concat ("/" );
173176 }
174177
175178 public static String classpath (String path ) {
@@ -185,8 +188,4 @@ public static String classpath(Class<?> clazz, String path) {
185188 throw new RuntimeException (format ("Can't load %s for class %s" , path , clazz ), e );
186189 }
187190 }
188-
189- private String getPath (){
190- return jenkins .getInstance ().getRootUrl () + GitHubWebHook .URLNAME .concat ("/" );
191- }
192191}
0 commit comments