github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/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  	"sigs.k8s.io/prow/pkg/github"
    33  	"sigs.k8s.io/prow/pkg/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  	contentLength["/this_is_fine.png"] = "317624"
   106  	contentLength["/this_is_not_fine.png"] = "645595"
   107  	contentLength["/this_is_unbearable.jpg"] = "34241"
   108  
   109  	// fake server for images
   110  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   111  		if s, ok := contentLength[r.URL.Path]; ok {
   112  			body := "binary image"
   113  			w.Header().Set("Content-Length", s)
   114  			io.WriteString(w, body)
   115  		} else {
   116  			t.Errorf("Cannot find content length for %s", r.URL.Path)
   117  		}
   118  	}))
   119  	defer ts2.Close()
   120  
   121  	// setup a stock valid request
   122  	url := ts2.URL + "/dog.jpg"
   123  	b, err := json.Marshal(&dogResult{
   124  		URL: url,
   125  	})
   126  	if err != nil {
   127  		t.Errorf("Failed to encode test data: %v", err)
   128  	}
   129  
   130  	// create test cases for handling http responses
   131  	validResponse := string(b)
   132  	var testcases = []struct {
   133  		name     string
   134  		comment  string
   135  		path     string
   136  		response string
   137  		expected string
   138  		isValid  bool
   139  	}{
   140  		{
   141  			name:     "valid",
   142  			comment:  "/woof",
   143  			path:     "/valid",
   144  			response: validResponse,
   145  			expected: url,
   146  			isValid:  true,
   147  		},
   148  		{
   149  			name:     "invalid JSON",
   150  			comment:  "/woof",
   151  			path:     "/bad-json",
   152  			response: `{"bad-blob": "not-a-url"`,
   153  			isValid:  false,
   154  		},
   155  		{
   156  			name:     "invalid URL",
   157  			comment:  "/woof",
   158  			path:     "/bad-url",
   159  			response: `{"url": "not a url.."}`,
   160  			isValid:  false,
   161  		},
   162  		{
   163  			name:     "mp4 doggo unsupported :(",
   164  			comment:  "/woof",
   165  			path:     "/mp4-doggo",
   166  			response: fmt.Sprintf(`{"url": "%s/doggo.mp4"}`, ts2.URL),
   167  			isValid:  false,
   168  		},
   169  		{
   170  			name:     "image too big",
   171  			comment:  "/woof",
   172  			path:     "/too-big",
   173  			response: fmt.Sprintf(`{"url": "%s/bigdog.jpg"}`, ts2.URL),
   174  			isValid:  false,
   175  		},
   176  		{
   177  			name:     "this is fine",
   178  			comment:  "/this-is-fine",
   179  			expected: "this_is_fine.png",
   180  			isValid:  true,
   181  		},
   182  		{
   183  			name:     "this is not fine",
   184  			comment:  "/this-is-not-fine",
   185  			expected: "this_is_not_fine.png",
   186  			isValid:  true,
   187  		},
   188  		{
   189  			name:     "this is unbearable",
   190  			comment:  "/this-is-unbearable",
   191  			expected: "this_is_unbearable.jpg",
   192  			isValid:  true,
   193  		},
   194  	}
   195  
   196  	// fake server for image urls
   197  	pathToResponse := make(map[string]string)
   198  	for _, testcase := range testcases {
   199  		pathToResponse[testcase.path] = testcase.response
   200  	}
   201  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   202  		if r, ok := pathToResponse[r.URL.Path]; ok {
   203  			io.WriteString(w, r)
   204  		} else {
   205  			io.WriteString(w, validResponse)
   206  		}
   207  	}))
   208  	defer ts.Close()
   209  
   210  	// run test for each case
   211  	for _, testcase := range testcases {
   212  		dog, err := realPack(ts.URL + testcase.path).readDog("")
   213  		if testcase.isValid && err != nil {
   214  			t.Errorf("For case %s, didn't expect error: %v", testcase.name, err)
   215  		} else if !testcase.isValid && err == nil {
   216  			t.Errorf("For case %s, expected error, received dog: %s", testcase.name, dog)
   217  		}
   218  
   219  		if !testcase.isValid {
   220  			continue
   221  		}
   222  
   223  		// github fake client
   224  		fc := fakegithub.NewFakeClient()
   225  		fc.IssueComments = make(map[int][]github.IssueComment)
   226  
   227  		// fully test handling a comment
   228  		e := &github.GenericCommentEvent{
   229  			Action:     github.GenericCommentActionCreated,
   230  			Body:       testcase.comment,
   231  			Number:     5,
   232  			IssueState: "open",
   233  		}
   234  		err = handle(fc, logrus.WithField("plugin", pluginName), e, realPack(ts.URL), ts2.URL+"/")
   235  		if err != nil {
   236  			t.Errorf("tc %s: For comment %s, didn't expect error: %v", testcase.name, testcase.comment, err)
   237  		}
   238  
   239  		if len(fc.IssueComments[5]) != 1 {
   240  			t.Errorf("tc %s: should have commented", testcase.name)
   241  		}
   242  		if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, testcase.expected) {
   243  			t.Errorf("tc %s: missing image url: %s from comment: %v", testcase.name, testcase.expected, c.Body)
   244  		}
   245  	}
   246  }
   247  
   248  // Small, unit tests
   249  func TestDogs(t *testing.T) {
   250  	var testcases = []struct {
   251  		name          string
   252  		action        github.GenericCommentEventAction
   253  		body          string
   254  		state         string
   255  		pr            bool
   256  		shouldComment bool
   257  	}{
   258  		{
   259  			name:          "ignore edited comment",
   260  			state:         "open",
   261  			action:        github.GenericCommentActionEdited,
   262  			body:          "/woof",
   263  			shouldComment: false,
   264  		},
   265  		{
   266  			name:          "leave dog on pr",
   267  			state:         "open",
   268  			action:        github.GenericCommentActionCreated,
   269  			body:          "/woof",
   270  			pr:            true,
   271  			shouldComment: true,
   272  		},
   273  		{
   274  			name:          "leave dog on issue",
   275  			state:         "open",
   276  			action:        github.GenericCommentActionCreated,
   277  			body:          "/woof",
   278  			shouldComment: true,
   279  		},
   280  		{
   281  			name:          "leave dog on issue, trailing space",
   282  			state:         "open",
   283  			action:        github.GenericCommentActionCreated,
   284  			body:          "/woof \r",
   285  			shouldComment: true,
   286  		},
   287  		{
   288  			name:          "leave dog on issue, trailing /bark",
   289  			state:         "open",
   290  			action:        github.GenericCommentActionCreated,
   291  			body:          "/bark",
   292  			shouldComment: true,
   293  		},
   294  		{
   295  			name:          "leave dog on issue, trailing /bark, trailing space",
   296  			state:         "open",
   297  			action:        github.GenericCommentActionCreated,
   298  			body:          "/bark \r",
   299  			shouldComment: true,
   300  		},
   301  		{
   302  			name:          "leave this-is-fine on pr",
   303  			state:         "open",
   304  			action:        github.GenericCommentActionCreated,
   305  			body:          "/this-is-fine",
   306  			pr:            true,
   307  			shouldComment: true,
   308  		},
   309  		{
   310  			name:          "leave this-is-not-fine on pr",
   311  			state:         "open",
   312  			action:        github.GenericCommentActionCreated,
   313  			body:          "/this-is-not-fine",
   314  			pr:            true,
   315  			shouldComment: true,
   316  		},
   317  		{
   318  			name:          "leave this-is-unbearable on pr",
   319  			state:         "open",
   320  			action:        github.GenericCommentActionCreated,
   321  			body:          "/this-is-unbearable",
   322  			pr:            true,
   323  			shouldComment: true,
   324  		},
   325  	}
   326  	for _, tc := range testcases {
   327  		fc := fakegithub.NewFakeClient()
   328  		fc.IssueComments = make(map[int][]github.IssueComment)
   329  		e := &github.GenericCommentEvent{
   330  			Action:     tc.action,
   331  			Body:       tc.body,
   332  			Number:     5,
   333  			IssueState: tc.state,
   334  			IsPR:       tc.pr,
   335  		}
   336  		err := handle(fc, logrus.WithField("plugin", pluginName), e, fakePack("doge"), "")
   337  		if err != nil {
   338  			t.Errorf("For case %s, didn't expect error: %v", tc.name, err)
   339  		}
   340  		if tc.shouldComment && len(fc.IssueComments[5]) != 1 {
   341  			t.Errorf("For case %s, should have commented.", tc.name)
   342  		} else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 {
   343  			t.Errorf("For case %s, should not have commented.", tc.name)
   344  		}
   345  	}
   346  }