package github import ( "bytes" "io/ioutil" "net/http" "strings" "testing" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/runtime" "github.com/openshift/origin/pkg/build/api" "github.com/openshift/origin/pkg/build/webhook" ) var testBuildConfig = &api.BuildConfig{ Spec: api.BuildConfigSpec{ Triggers: []api.BuildTriggerPolicy{ { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret101", }, }, { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret100", }, }, { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret102", }, }, }, CommonSpec: api.CommonSpec{ Source: api.BuildSource{ Git: &api.GitBuildSource{ URI: "git://github.com/my/repo.git", }, }, Strategy: mockBuildStrategy, }, }, } var mockBuildStrategy = api.BuildStrategy{ SourceStrategy: &api.SourceBuildStrategy{ From: kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/image", }, }, } type okBuildConfigInstantiator struct{} func (*okBuildConfigInstantiator) Instantiate(namespace string, request *api.BuildRequest) (*api.Build, error) { return &api.Build{}, nil } type fakeResponder struct { called bool statusCode int object runtime.Object err error } func (r *fakeResponder) Object(statusCode int, obj runtime.Object) { if r.called { panic("called twice") } r.called = true r.statusCode = statusCode r.object = obj } func (r *fakeResponder) Error(err error) { if r.called { panic("called twice") } r.called = true r.err = err } var buildConfig = &api.BuildConfig{ Spec: api.BuildConfigSpec{ Triggers: []api.BuildTriggerPolicy{ { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret100", }, }, }, CommonSpec: api.CommonSpec{ Source: api.BuildSource{ Git: &api.GitBuildSource{}, }, }, }, } func GivenRequest(method string) *http.Request { req, _ := http.NewRequest(method, "http://someurl.com", nil) return req } func TestVerifyRequestForMethod(t *testing.T) { req := GivenRequest("GET") plugin := New() revision, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err == nil || !strings.Contains(err.Error(), "unsupported HTTP method") { t.Errorf("Expected unsupported HTTP method, got %v", err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } if revision != nil { t.Error("Expected the 'revision' return value to be nil") } } func TestWrongSecret(t *testing.T) { req := GivenRequest("POST") plugin := New() revision, _, proceed, err := plugin.Extract(buildConfig, "wrongsecret", "", req) if err != webhook.ErrSecretMismatch { t.Errorf("Expected %v, got %v", webhook.ErrSecretMismatch, err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } if revision != nil { t.Error("Expected the 'revision' return value to be nil") } } func TestMissingEvent(t *testing.T) { req := GivenRequest("POST") req.Header.Add("Content-Type", "application/json") plugin := New() revision, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err == nil || !strings.Contains(err.Error(), "missing X-GitHub-Event or X-Gogs-Event") { t.Errorf("Expected missing X-GitHub-Event or X-Gogs-Event, got %v", err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } if revision != nil { t.Error("Expected the 'revision' return value to be nil") } } func TestWrongGitHubEvent(t *testing.T) { req := GivenRequest("POST") req.Header.Add("Content-Type", "application/json") req.Header.Add("X-GitHub-Event", "wrong") plugin := New() revision, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err == nil || !strings.Contains(err.Error(), "Unknown X-GitHub-Event or X-Gogs-Event") { t.Errorf("Expected missing Unknown X-GitHub-Event or X-Gogs-Event, got %v", err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } if revision != nil { t.Error("Expected the 'revision' return value to be nil") } } func TestJsonPingEvent(t *testing.T) { req := postFile("X-GitHub-Event", "ping", "pingevent.json", "http://some.url", http.StatusOK, t) plugin := New() _, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err != nil { t.Errorf("Unexpected error: %v", err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } } func TestJsonPushEventError(t *testing.T) { req := post("X-GitHub-Event", "push", []byte{}, "http://some.url", http.StatusBadRequest, t) plugin := New() revision, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err == nil || !strings.Contains(err.Error(), "unexpected end of JSON input") { t.Errorf("Expected unexpected end of JSON input, got %v", err) } if proceed { t.Error("Expected 'proceed' return value to be 'false'") } if revision != nil { t.Error("Expected the 'revision' return value to be nil") } } func TestJsonGitHubPushEvent(t *testing.T) { req := postFile("X-GitHub-Event", "push", "pushevent.json", "http://some.url", http.StatusOK, t) plugin := New() _, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err != nil { t.Errorf("Unexpected error: %v", err) } if !proceed { t.Error("Expected 'proceed' return value to be 'true'") } } func TestJsonGitHubPushEventWithCharset(t *testing.T) { req := postFileWithCharset("X-GitHub-Event", "push", "pushevent.json", "http://some.url", "application/json; charset=utf-8", http.StatusOK, t) plugin := New() _, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err != nil { t.Errorf("Unexpected error: %v", err) } if !proceed { t.Error("Expected 'proceed' return value to be 'true'") } } func TestJsonGogsPushEvent(t *testing.T) { req := postFile("X-Gogs-Event", "push", "pushevent.json", "http://some.url", http.StatusOK, t) plugin := New() _, _, proceed, err := plugin.Extract(buildConfig, "secret100", "", req) if err != nil { t.Errorf("Unexpected error: %v", err) } if !proceed { t.Error("Expected 'proceed' return value to be 'true'") } } func postFile(eventHeader, eventName, filename, url string, expStatusCode int, t *testing.T) *http.Request { return postFileWithCharset(eventHeader, eventName, filename, url, "application/json", expStatusCode, t) } func postFileWithCharset(eventHeader, eventName, filename, url, charset string, expStatusCode int, t *testing.T) *http.Request { data, err := ioutil.ReadFile("testdata/" + filename) if err != nil { t.Errorf("Failed to open %s: %v", filename, err) } return postWithCharset(eventHeader, eventName, data, url, charset, expStatusCode, t) } func post(eventHeader, eventName string, data []byte, url string, expStatusCode int, t *testing.T) *http.Request { return postWithCharset(eventHeader, eventName, data, url, "application/json", expStatusCode, t) } func postWithCharset(eventHeader, eventName string, data []byte, url, charset string, expStatusCode int, t *testing.T) *http.Request { req, err := http.NewRequest("POST", url, bytes.NewReader(data)) if err != nil { t.Errorf("Error creating POST request: %v", err) } req.Header.Add("Content-Type", charset) req.Header.Add(eventHeader, eventName) return req } type testContext struct { plugin WebHook buildCfg *api.BuildConfig req *http.Request path string } func setup(t *testing.T, filename, eventType, ref string) *testContext { context := testContext{ plugin: WebHook{}, buildCfg: &api.BuildConfig{ Spec: api.BuildConfigSpec{ Triggers: []api.BuildTriggerPolicy{ { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret101", }, }, { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret100", }, }, { Type: api.GitHubWebHookBuildTriggerType, GitHubWebHook: &api.WebHookTrigger{ Secret: "secret102", }, }, }, CommonSpec: api.CommonSpec{ Source: api.BuildSource{ Git: &api.GitBuildSource{ URI: "git://github.com/my/repo.git", Ref: ref, }, }, Strategy: mockBuildStrategy, }, }, }, path: "/foobar", } event, err := ioutil.ReadFile("testdata/" + filename) if err != nil { t.Errorf("Failed to open %s: %v", filename, err) } req, err := http.NewRequest("POST", "http://origin.com", bytes.NewReader(event)) if err != nil { t.Errorf("Failed to create a new request (%s)", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Github-Event", eventType) context.req = req return &context } func TestExtractForAPingEvent(t *testing.T) { //setup context := setup(t, "pingevent.json", "ping", "") //execute _, _, proceed, err := context.plugin.Extract(context.buildCfg, "secret101", context.path, context.req) //validation if err != nil { t.Errorf("Error while extracting build info: %s", err) } if proceed { t.Errorf("The 'proceed' return value should equal 'false' %t", proceed) } } func TestExtractProvidesValidBuildForAPushEvent(t *testing.T) { //setup context := setup(t, "pushevent.json", "push", "") //execute revision, _, proceed, err := context.plugin.Extract(context.buildCfg, "secret101", context.path, context.req) //validation if err != nil { t.Errorf("Error while extracting build info: %s", err) } if !proceed { t.Errorf("The 'proceed' return value should equal 'true' %t", proceed) } if revision == nil { t.Fatal("Expecting the revision to not be nil") } if revision.Git.Commit != "9bdc3a26ff933b32f3e558636b58aea86a69f051" { t.Error("Expecting the revision to contain the commit id from the push event") } } func TestExtractProvidesValidBuildForAPushEventOtherThanMaster(t *testing.T) { //setup context := setup(t, "pushevent-not-master-branch.json", "push", "my_other_branch") //execute revision, _, proceed, err := context.plugin.Extract(context.buildCfg, "secret101", context.path, context.req) //validation if err != nil { t.Errorf("Error while extracting build info: %s", err) } if !proceed { t.Errorf("The 'proceed' return value should equal 'true' %t", proceed) } if revision == nil { t.Fatal("Expecting the revision to not be nil") } if revision.Git.Commit != "9bdc3a26ff933b32f3e558636b58aea86a69f051" { t.Error("Expecting the revision to contain the commit id from the push event") } } func TestExtractSkipsBuildForUnmatchedBranches(t *testing.T) { //setup context := setup(t, "pushevent.json", "push", "wrongref") //execute _, _, proceed, _ := context.plugin.Extract(context.buildCfg, "secret101", context.path, context.req) if proceed { t.Errorf("Expecting to not continue from this event because the branch is not for this buildConfig '%s'", context.buildCfg.Spec.Source.Git.Ref) } }