/*
 * Copyright 2016 Shredder121.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.shredder121.gh_event_api.handler;

import static com.jayway.restassured.RestAssured.given;
import static com.jayway.restassured.http.ContentType.JSON;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.runner.RunWith;
import org.kohsuke.github.GHContent;
import org.kohsuke.github.GitHub;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;

@RunWith(SpringJUnit4ClassRunner.class)
@WebIntegrationTest({"spring.main.show-banner=false"})
@DirtiesContext
public abstract class AbstractHandlerTest {

    private static final String DEVELOPER_GITHUB_COM_REVISION = "f5713bd67e1885cee9f3c6e41f03386792c5851c";
    private static final Map<String, GHContent> eventPayloadMap;

    static {
        try {
            GitHub github;
            try {
                github = GitHub.connect();
            } catch (IOException noAuthorizationDetails) {
                // This is fine, and still gives you 60 requests per hour, so one-off test runs still work
                github = GitHub.connectAnonymously();
            }

            List<GHContent> directoryContent = github.getRepository("github/developer.github.com")
                    .getDirectoryContent("lib/webhooks", DEVELOPER_GITHUB_COM_REVISION);

            // Map the event name (fileNameWithoutPayloadJsonSuffix) to the event's payload.json file
            //             push/pull_request/* -> lib/webhooks/*.payload.json
            eventPayloadMap = Maps.uniqueIndex(
                    directoryContent, AbstractHandlerTest::fileNameWithoutPayloadJsonSuffix);
        } catch (IOException ex) {
            throw Throwables.propagate(ex);
        }
    }

    private static String fileNameWithoutPayloadJsonSuffix(GHContent content) {
        String fileName = content.getName();
        return fileName.substring(0, fileName.indexOf('.'));
    }

    private final String event;
    private final String hmac;

    @Rule
    public final ErrorCollector errorCollector = new ErrorCollector();

    protected final CountDownLatch completion = new CountDownLatch(1);

    @Autowired
    private AbstractTestHandlerBean handlerBean;

    protected AbstractHandlerTest(String event, String hmac) {
        this.event = event;
        /* This will be the HMAC as reported by https://www.freeformatter.com/hmac-generator.html */
        this.hmac = hmac;
    }

    @Before
    public final void prepareTest() {
        handlerBean.setCountDownLatch(completion);
        handlerBean.setErrorCollector(errorCollector);
    }

    @Test
    public void test() {
        given().headers(
                "X-GitHub-Event", event,
                "X-Hub-Signature", "sha1=" + hmac)
        .and().body(getBody()).with().contentType(JSON)
        .expect().statusCode(HttpStatus.OK.value())
        .when().post();
    }

    @After
    public void awaitCompletion() throws InterruptedException {
        if (!completion.await(10, SECONDS)) {
            fail("Timeout during execution");
        }
    }

    private String getBody() {
        GHContent payloadFile = eventPayloadMap.get(event);
        try (InputStream stream = payloadFile.read()) {
            return minimize(stream);
        } catch (IOException ex) {
            throw Throwables.propagate(ex);
        }
    }

    public String minimize(final InputStream stream) throws IOException {
        ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();
        Map<String, Object> content = mapper.reader().forType(Map.class).readValue(stream);
        return mapper.writer(new MinimalPrettyPrinter(null/*minimizes*/)).writeValueAsString(content);
    }
}

@@ -763,98 +763,8 @@
 SON;
-%0Aimport static java.util.concurrent.TimeUnit.SECONDS;%0Aimport static org.junit.Assert.fail;
 %0A%0Aim
@@ -911,32 +911,8 @@
 h;%0A%0A
-import org.junit.After;%0A
 impo
@@ -1001,30 +1001,134 @@
 t.rules.
-ErrorCollector
+DisableOnDebug;%0Aimport org.junit.rules.ErrorCollector;%0Aimport org.junit.rules.TestRule;%0Aimport org.junit.rules.Timeout
 ;%0Aimport
@@ -3414,24 +3414,112 @@
 llector();%0A%0A
+    @Rule%0A    public final TestRule timeout = new DisableOnDebug(Timeout.seconds(10));%0A%0A
     protecte
@@ -4059,24 +4059,52 @@
  void test()
+ throws InterruptedException
  %7B%0A        g
@@ -4347,191 +4347,36 @@
 ();%0A
-    %7D%0A%0A    @After%0A    public void awaitCompletion() throws InterruptedException %7B%0A        if (!completion.await(10, SECONDS)) %7B%0A            fail(%22Timeout during execution%22);%0A        %7D
+%0A        completion.await();
 %0A   

Disable test timeout on debug