// +build integration,!no-etcd

package integration

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/version"

	"github.com/openshift/origin/pkg/api/latest"
	"github.com/openshift/origin/pkg/api/v1beta1"
	buildapi "github.com/openshift/origin/pkg/build/api"
	buildregistry "github.com/openshift/origin/pkg/build/registry/build"
	buildconfigregistry "github.com/openshift/origin/pkg/build/registry/buildconfig"
	buildetcd "github.com/openshift/origin/pkg/build/registry/etcd"
	"github.com/openshift/origin/pkg/build/webhook"
	"github.com/openshift/origin/pkg/build/webhook/github"
	osclient "github.com/openshift/origin/pkg/client"
)

func init() {
	requireEtcd()
}

func TestWebhookGithubPush(t *testing.T) {
	osClient, url := setup(t)

	// create buildconfig
	buildConfig := &buildapi.BuildConfig{
		JSONBase: kubeapi.JSONBase{
			ID: "pushbuild",
		},
		DesiredInput: buildapi.BuildInput{
			Type:      buildapi.DockerBuildType,
			SourceURI: "http://my.docker/build",
			ImageTag:  "namespace/builtimage",
		},
		Secret: "secret101",
	}
	if _, err := osClient.CreateBuildConfig(buildConfig); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// trigger build event sending push notification
	postFile("push", "pushevent.json", url+"pushbuild/secret101/github", http.StatusOK, t)

	// get a list of builds
	builds, err := osClient.ListBuilds(labels.Everything())
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if len(builds.Items) != 1 {
		t.Fatalf("Expected one build, got %#v", builds)
	}
	actual := builds.Items[0]
	if actual.Status != buildapi.BuildNew {
		t.Errorf("Expected %s, got %s", buildapi.BuildNew, actual.Status)
	}
	if !reflect.DeepEqual(actual.Input, buildConfig.DesiredInput) {
		t.Errorf("Expected %#v, got %#v", buildConfig.DesiredInput, actual.Input)
	}

	cleanup(osClient, t)
}

func TestWebhookGithubPing(t *testing.T) {
	osClient, url := setup(t)

	// create buildconfig
	buildConfig := &buildapi.BuildConfig{
		JSONBase: kubeapi.JSONBase{
			ID: "pingbuild",
		},
		DesiredInput: buildapi.BuildInput{
			Type:      buildapi.DockerBuildType,
			SourceURI: "http://my.docker/build",
			ImageTag:  "namespace/builtimage",
		},
		Secret: "secret101",
	}
	if _, err := osClient.CreateBuildConfig(buildConfig); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// trigger build event sending push notification
	postFile("ping", "pingevent.json", url+"pingbuild/secret101/github", http.StatusOK, t)

	// get a list of builds
	builds, err := osClient.ListBuilds(labels.Everything())
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if len(builds.Items) != 0 {
		t.Fatalf("Unexpected build appeared, got %#v", builds)
	}

	cleanup(osClient, t)
}

func setup(t *testing.T) (*osclient.Client, string) {
	etcdClient := newEtcdClient()
	helper, _ := master.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
	m := master.New(&master.Config{
		EtcdHelper: helper,
	})
	interfaces, _ := latest.InterfacesFor(latest.Version)
	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner})
	storage := map[string]apiserver.RESTStorage{
		"builds":       buildregistry.NewREST(buildRegistry),
		"buildConfigs": buildconfigregistry.NewREST(buildRegistry),
	}

	osMux := http.NewServeMux()
	apiserver.NewAPIGroup(m.API_v1beta1()).InstallREST(osMux, "/api/v1beta1")
	osPrefix := "/osapi/v1beta1"
	apiserver.NewAPIGroup(storage, v1beta1.Codec, osPrefix, interfaces.SelfLinker).InstallREST(osMux, osPrefix)
	apiserver.InstallSupport(osMux)
	s := httptest.NewServer(osMux)

	kubeclient := client.NewOrDie(&client.Config{Host: s.URL, Version: klatest.Version})
	osClient := osclient.NewOrDie(&client.Config{Host: s.URL, Version: latest.Version})

	whPrefix := osPrefix + "/buildConfigHooks/"
	osMux.Handle(whPrefix, http.StripPrefix(whPrefix,
		webhook.NewController(osClient, map[string]webhook.Plugin{
			"github": github.New(),
		})))

	info, err := kubeclient.ServerVersion()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if e, a := version.Get(), *info; !reflect.DeepEqual(e, a) {
		t.Errorf("Expected %#v, got %#v", e, a)
	}

	return osClient, s.URL + whPrefix
}

func cleanup(osClient *osclient.Client, t *testing.T) {
	builds, err := osClient.ListBuilds(labels.Everything())
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	for _, build := range builds.Items {
		if err := osClient.DeleteBuild(build.ID); err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
	buildConfigs, err := osClient.ListBuildConfigs(labels.Everything())
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	for _, bc := range buildConfigs.Items {
		if err := osClient.DeleteBuildConfig(bc.ID); err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
}

func postFile(event, filename, url string, expStatusCode int, t *testing.T) {
	client := &http.Client{}
	data, err := ioutil.ReadFile("../../pkg/build/webhook/github/fixtures/" + filename)
	if err != nil {
		t.Fatalf("Failed to open %s: %v", filename, err)
	}
	req, err := http.NewRequest("POST", url, bytes.NewReader(data))
	if err != nil {
		t.Fatalf("Error creating POST request: %v", err)
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("User-Agent", "GitHub-Hookshot/github")
	req.Header.Add("X-Github-Event", event)
	resp, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed posting webhook: %v", err)
	}
	body, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != expStatusCode {
		t.Errorf("Wrong response code, expecting %d, got %s: %s!", expStatusCode, resp.Status, string(body))
	}
}