github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/dog/dog_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dog
    18  
    19  import (
    20  	"encoding/json"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"regexp"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/sirupsen/logrus"
    31  
    32  	"k8s.io/test-infra/prow/github"
    33  	"k8s.io/test-infra/prow/github/fakegithub"
    34  )
    35  
    36  type fakePack string
    37  
    38  var human = flag.Bool("human", false, "Enable to run additional manual tests")
    39  
    40  func (c fakePack) readDog(dogURL string) (string, error) {
    41  	if dogURL != "" {
    42  		return dogURL, nil
    43  	}
    44  	return string(c), nil
    45  }
    46  
    47  func TestRealDog(t *testing.T) {
    48  	if !*human {
    49  		t.Skip("Real dogs disabled for automation. Manual users can add --human [--category=foo]")
    50  	}
    51  	if dog, err := dogURL.readDog(""); err != nil {
    52  		t.Errorf("Could not read dog from %s: %v", dogURL, err)
    53  	} else {
    54  		fmt.Println(dog)
    55  	}
    56  }
    57  
    58  func TestFormat(t *testing.T) {
    59  	re := regexp.MustCompile(`\[!\[.+\]\(.+\)\]\(.+\)`)
    60  	basicURL := "http://example.com"
    61  	testcases := []struct {
    62  		name string
    63  		url  string
    64  		err  bool
    65  	}{
    66  		{
    67  			name: "basically works",
    68  			url:  basicURL,
    69  			err:  false,
    70  		},
    71  		{
    72  			name: "empty url",
    73  			url:  "",
    74  			err:  true,
    75  		},
    76  		{
    77  			name: "bad url",
    78  			url:  "http://this is not a url",
    79  			err:  true,
    80  		},
    81  	}
    82  	for _, tc := range testcases {
    83  		ret, err := FormatURL(tc.url)
    84  		switch {
    85  		case tc.err:
    86  			if err == nil {
    87  				t.Errorf("%s: failed to raise an error", tc.name)
    88  			}
    89  		case err != nil:
    90  			t.Errorf("%s: unexpected error: %v", tc.name, err)
    91  		case !re.MatchString(ret):
    92  			t.Errorf("%s: bad return value: %s", tc.name, ret)
    93  		}
    94  	}
    95  }
    96  
    97  // Medium integration test (depends on ability to open a TCP port)
    98  func TestHttpResponse(t *testing.T) {
    99  
   100  	// create test cases for handling content length of images
   101  	contentLength := make(map[string]string)
   102  	contentLength["/dog.jpg"] = "717987"
   103  	contentLength["/doggo.mp4"] = "37943259"
   104  	contentLength["/bigdog.jpg"] = "12647753"
   105  
   106  	// fake server for images
   107  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   108  		if s, ok := contentLength[r.URL.Path]; ok {
   109  			body := "binary image"
   110  			w.Header().Set("Content-Length", s)
   111  			io.WriteString(w, body)
   112  		} else {
   113  			t.Errorf("Cannot find content length for %s", r.URL.Path)
   114  		}
   115  	}))
   116  	defer ts2.Close()
   117  
   118  	// setup a stock valid request
   119  	url := ts2.URL + "/dog.jpg"
   120  	b, err := json.Marshal(&dogResult{
   121  		URL: url,
   122  	})
   123  	if err != nil {
   124  		t.Errorf("Failed to encode test data: %v", err)
   125  	}
   126  
   127  	// create test cases for handling http responses
   128  	validResponse := string(b)
   129  	var testcases = []struct {
   130  		name     string
   131  		comment  string
   132  		path     string
   133  		response string
   134  		expected string
   135  		isValid  bool
   136  	}{
   137  		{
   138  			name:     "valid",
   139  			comment:  "/woof",
   140  			path:     "/valid",
   141  			response: validResponse,
   142  			expected: url,
   143  			isValid:  true,
   144  		},
   145  		{
   146  			name:     "invalid JSON",
   147  			comment:  "/woof",
   148  			path:     "/bad-json",
   149  			response: `{"bad-blob": "not-a-url"`,
   150  			isValid:  false,
   151  		},
   152  		{
   153  			name:     "invalid URL",
   154  			comment:  "/woof",
   155  			path:     "/bad-url",
   156  			response: `{"url": "not a url.."}`,
   157  			isValid:  false,
   158  		},
   159  		{
   160  			name:     "mp4 doggo unsupported :(",
   161  			comment:  "/woof",
   162  			path:     "/mp4-doggo",
   163  			response: fmt.Sprintf(`{"url": "%s/doggo.mp4"}`, ts2.URL),
   164  			isValid:  false,
   165  		},
   166  		{
   167  			name:     "image too big",
   168  			comment:  "/woof",
   169  			path:     "/too-big",
   170  			response: fmt.Sprintf(`{"url": "%s/bigdog.jpg"}`, ts2.URL),
   171  			isValid:  false,
   172  		},
   173  		{
   174  			name:     "this is fine",
   175  			comment:  "/this-is-fine",
   176  			expected: "this_is_fine.png",
   177  			isValid:  true,
   178  		},
   179  		{
   180  			name:     "this is not fine",
   181  			comment:  "/this-is-not-fine",
   182  			expected: "this_is_not_fine.png",
   183  			isValid:  true,
   184  		},
   185  		{
   186  			name:     "this is unbearable",
   187  			comment:  "/this-is-unbearable",
   188  			expected: "this_is_unbearable.jpg",
   189  			isValid:  true,
   190  		},
   191  	}
   192  
   193  	// fake server for image urls
   194  	pathToResponse := make(map[string]string)
   195  	for _, testcase := range testcases {
   196  		pathToResponse[testcase.path] = testcase.response
   197  	}
   198  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   199  		if r, ok := pathToResponse[r.URL.Path]; ok {
   200  			io.WriteString(w, r)
   201  		} else {
   202  			io.WriteString(w, validResponse)
   203  		}
   204  	}))
   205  	defer ts.Close()
   206  
   207  	// run test for each case
   208  	for _, testcase := range testcases {
   209  		dog, err := realPack(ts.URL + testcase.path).readDog("")
   210  		if testcase.isValid && err != nil {
   211  			t.Errorf("For case %s, didn't expect error: %v", testcase.name, err)
   212  		} else if !testcase.isValid && err == nil {
   213  			t.Errorf("For case %s, expected error, received dog: %s", testcase.name, dog)
   214  		}
   215  
   216  		if !testcase.isValid {
   217  			continue
   218  		}
   219  
   220  		// github fake client
   221  		fc := &fakegithub.FakeClient{
   222  			IssueComments: make(map[int][]github.IssueComment),
   223  		}
   224  
   225  		// fully test handling a comment
   226  		e := &github.GenericCommentEvent{
   227  			Action:     github.GenericCommentActionCreated,
   228  			Body:       testcase.comment,
   229  			Number:     5,
   230  			IssueState: "open",
   231  		}
   232  		err = handle(fc, logrus.WithField("plugin", pluginName), e, realPack(ts.URL))
   233  		if err != nil {
   234  			t.Errorf("tc %s: For comment %s, didn't expect error: %v", testcase.name, testcase.comment, err)
   235  		}
   236  
   237  		if len(fc.IssueComments[5]) != 1 {
   238  			t.Errorf("tc %s: should have commented", testcase.name)
   239  		}
   240  		if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, testcase.expected) {
   241  			t.Errorf("tc %s: missing image url: %s from comment: %v", testcase.name, testcase.expected, c.Body)
   242  		}
   243  	}
   244  }
   245  
   246  // Small, unit tests
   247  func TestDogs(t *testing.T) {
   248  	var testcases = []struct {
   249  		name          string
   250  		action        github.GenericCommentEventAction
   251  		body          string
   252  		state         string
   253  		pr            bool
   254  		shouldComment bool
   255  	}{
   256  		{
   257  			name:          "ignore edited comment",
   258  			state:         "open",
   259  			action:        github.GenericCommentActionEdited,
   260  			body:          "/woof",
   261  			shouldComment: false,
   262  		},
   263  		{
   264  			name:          "leave dog on pr",
   265  			state:         "open",
   266  			action:        github.GenericCommentActionCreated,
   267  			body:          "/woof",
   268  			pr:            true,
   269  			shouldComment: true,
   270  		},
   271  		{
   272  			name:          "leave dog on issue",
   273  			state:         "open",
   274  			action:        github.GenericCommentActionCreated,
   275  			body:          "/woof",
   276  			shouldComment: true,
   277  		},
   278  		{
   279  			name:          "leave dog on issue, trailing space",
   280  			state:         "open",
   281  			action:        github.GenericCommentActionCreated,
   282  			body:          "/woof \r",
   283  			shouldComment: true,
   284  		},
   285  		{
   286  			name:          "leave dog on issue, trailing /bark",
   287  			state:         "open",
   288  			action:        github.GenericCommentActionCreated,
   289  			body:          "/bark",
   290  			shouldComment: true,
   291  		},
   292  		{
   293  			name:          "leave dog on issue, trailing /bark, trailing space",
   294  			state:         "open",
   295  			action:        github.GenericCommentActionCreated,
   296  			body:          "/bark \r",
   297  			shouldComment: true,
   298  		},
   299  		{
   300  			name:          "leave this-is-fine on pr",
   301  			state:         "open",
   302  			action:        github.GenericCommentActionCreated,
   303  			body:          "/this-is-fine",
   304  			pr:            true,
   305  			shouldComment: true,
   306  		},
   307  		{
   308  			name:          "leave this-is-not-fine on pr",
   309  			state:         "open",
   310  			action:        github.GenericCommentActionCreated,
   311  			body:          "/this-is-not-fine",
   312  			pr:            true,
   313  			shouldComment: true,
   314  		},
   315  		{
   316  			name:          "leave this-is-unbearable on pr",
   317  			state:         "open",
   318  			action:        github.GenericCommentActionCreated,
   319  			body:          "/this-is-unbearable",
   320  			pr:            true,
   321  			shouldComment: true,
   322  		},
   323  	}
   324  	for _, tc := range testcases {
   325  		fc := &fakegithub.FakeClient{
   326  			IssueComments: make(map[int][]github.IssueComment),
   327  		}
   328  		e := &github.GenericCommentEvent{
   329  			Action:     tc.action,
   330  			Body:       tc.body,
   331  			Number:     5,
   332  			IssueState: tc.state,
   333  			IsPR:       tc.pr,
   334  		}
   335  		err := handle(fc, logrus.WithField("plugin", pluginName), e, fakePack("doge"))
   336  		if err != nil {
   337  			t.Errorf("For case %s, didn't expect error: %v", tc.name, err)
   338  		}
   339  		if tc.shouldComment && len(fc.IssueComments[5]) != 1 {
   340  			t.Errorf("For case %s, should have commented.", tc.name)
   341  		} else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 {
   342  			t.Errorf("For case %s, should not have commented.", tc.name)
   343  		}
   344  	}
   345  }