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);
        }
    }
}

@@ -1326,16 +1326,97 @@
 lValue;%0A
+import static org.jenkinsci.plugins.github.test.HookSecretHelper.removeSecretIn;%0A
 import s
@@ -3219,32 +3219,64 @@
 ows Exception %7B%0A
+        removeSecretIn(config);%0A
         given().

fix flaky test for webhook without sign