package com.cloudbees.jenkins;
import com.google.common.base.Charsets;
import com.google.common.net.HttpHeaders;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.response.Header;
import com.jayway.restassured.specification.RequestSpecification;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.github.config.GitHubPluginConfig;
import org.jenkinsci.plugins.github.webhook.GHEventHeader;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.jvnet.hudson.test.JenkinsRule;
import org.kohsuke.github.GHEvent;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import static com.jayway.restassured.RestAssured.given;
import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
import static java.lang.String.format;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.apache.commons.lang3.ClassUtils.PACKAGE_SEPARATOR;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.notNullValue;
import static org.jenkinsci.plugins.github.test.HookSecretHelper.storeSecretIn;
import static org.jenkinsci.plugins.github.webhook.RequirePostWithGHHookPayload.Processor.SIGNATURE_HEADER;
/**
* @author lanwen (Merkushev Kirill)
*/
public class GitHubWebHookFullTest {
public static final String APPLICATION_JSON = "application/json";
public static final String FORM = "application/x-www-form-urlencoded";
public static final Header JSON_CONTENT_TYPE = new Header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);
public static final Header FORM_CONTENT_TYPE = new Header(HttpHeaders.CONTENT_TYPE, FORM);
public static final String NOT_NULL_VALUE = "nonnull";
private RequestSpecification spec;
@Inject
private GitHubPluginConfig config;
@ClassRule
public static JenkinsRule jenkins = new JenkinsRule();
@Rule
public ExternalResource inject = new ExternalResource() {
@Override
protected void before() throws Throwable {
jenkins.getInstance().getInjector().injectMembers(GitHubWebHookFullTest.this);
}
};
@Rule
public ExternalResource setup = new ExternalResource() {
@Override
protected void before() throws Throwable {
spec = new RequestSpecBuilder()
.setBaseUri(jenkins.getInstance().getRootUrl())
.setBasePath(GitHubWebHook.URLNAME.concat("/"))
.setConfig(newConfig()
.encoderConfig(encoderConfig()
.defaultContentCharset(Charsets.UTF_8)
.appendDefaultContentCharsetToContentTypeIfUndefined(false)))
.build();
}
};
@Test
public void shouldParseJsonWebHookFromGH() throws Exception {
given().spec(spec)
.header(eventHeader(GHEvent.PUSH))
.header(JSON_CONTENT_TYPE)
.content(classpath("payloads/push.json"))
.log().all()
.expect().log().all().statusCode(SC_OK).post();
}
@Test
public void shouldParseJsonWebHookFromGHWithSignHeader() throws Exception {
String hash = "355e155fc3d10c4e5f2c6086a01281d2e947d932";
String secret = "123";
storeSecretIn(config, secret);
given().spec(spec)
.header(eventHeader(GHEvent.PUSH))
.header(JSON_CONTENT_TYPE)
.header(SIGNATURE_HEADER, format("sha1=%s", hash))
.content(classpath(String.format("payloads/ping_hash_%s_secret_%s.json", hash, secret)))
.log().all()
.expect().log().all().statusCode(SC_OK).post();
}
@Test
public void shouldParseFormWebHookOrServiceHookFromGH() throws Exception {
given().spec(spec)
.header(eventHeader(GHEvent.PUSH))
.header(FORM_CONTENT_TYPE)
.formParam("payload", classpath("payloads/push.json"))
.log().all()
.expect().log().all().statusCode(SC_OK).post();
}
@Test
public void shouldParsePingFromGH() throws Exception {
given().spec(spec)
.header(eventHeader(GHEvent.PING))
.header(JSON_CONTENT_TYPE)
.content(classpath("payloads/ping.json"))
.log().all()
.expect().log().all()
.statusCode(SC_OK)
.post();
}
@Test
public void shouldReturnErrOnEmptyPayloadAndHeader() throws Exception {
given().spec(spec)
.log().all()
.expect().log().all()
.statusCode(SC_BAD_REQUEST)
.body(containsString("Hook should contain event type"))
.post();
}
@Test
public void shouldReturnErrOnEmptyPayload() throws Exception {
given().spec(spec)
.header(eventHeader(GHEvent.PUSH))
.log().all()
.expect().log().all()
.statusCode(SC_BAD_REQUEST)
.body(containsString("Hook should contain payload"))
.post();
}
@Test
public void shouldReturnErrOnGetReq() throws Exception {
given().spec(spec)
.log().all().expect().log().all()
.statusCode(SC_METHOD_NOT_ALLOWED)
.get();
}
@Test
public void shouldProcessSelfTest() throws Exception {
given().spec(spec)
.header(new Header(GitHubWebHook.URL_VALIDATION_HEADER, NOT_NULL_VALUE))
.log().all()
.expect().log().all()
.statusCode(SC_OK)
.header(GitHubWebHook.X_INSTANCE_IDENTITY, notNullValue())
.post();
}
public Header eventHeader(GHEvent event) {
return eventHeader(event.name().toLowerCase());
}
public Header eventHeader(String event) {
return new Header(GHEventHeader.PayloadHandler.EVENT_HEADER, event);
}
public static String classpath(String path) {
return classpath(GitHubWebHookFullTest.class, path);
}
public static String classpath(Class<?> clazz, String path) {
try {
return IOUtils.toString(clazz.getClassLoader().getResourceAsStream(
clazz.getName().replace(PACKAGE_SEPARATOR, File.separator) + File.separator + path
), Charsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(format("Can't load %s for class %s", path, clazz), e);
}
}
}