github.com/abayer/test-infra@v0.0.5/robots/commenter/main_test.go (about)

     1  /*
     2  Copyright 2017 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 main
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"k8s.io/test-infra/prow/github"
    23  	"strconv"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  func TestParseHTMLURL(t *testing.T) {
    30  	cases := []struct {
    31  		name string
    32  		url  string
    33  		org  string
    34  		repo string
    35  		num  int
    36  		fail bool
    37  	}{
    38  		{
    39  			name: "normal issue",
    40  			url:  "https://github.com/org/repo/issues/1234",
    41  			org:  "org",
    42  			repo: "repo",
    43  			num:  1234,
    44  		},
    45  		{
    46  			name: "normal pull",
    47  			url:  "https://github.com/pull-org/pull-repo/pull/5555",
    48  			org:  "pull-org",
    49  			repo: "pull-repo",
    50  			num:  5555,
    51  		},
    52  		{
    53  			name: "different host",
    54  			url:  "ftp://gitlab.whatever/org/repo/issues/6666",
    55  			org:  "org",
    56  			repo: "repo",
    57  			num:  6666,
    58  		},
    59  		{
    60  			name: "string issue",
    61  			url:  "https://github.com/org/repo/issues/future",
    62  			fail: true,
    63  		},
    64  		{
    65  			name: "weird issue",
    66  			url:  "https://k8s-gubernator.appspot.com/build/kubernetes-jenkins/logs/ci-kubernetes-e2e-gci-gce/11947/",
    67  			fail: true,
    68  		},
    69  	}
    70  
    71  	for _, tc := range cases {
    72  		org, repo, num, err := parseHTMLURL(tc.url)
    73  		if err != nil && !tc.fail {
    74  			t.Errorf("%s: should not have produced error: %v", tc.name, err)
    75  		} else if err == nil && tc.fail {
    76  			t.Errorf("%s: failed to produce an error", tc.name)
    77  		} else {
    78  			if org != tc.org {
    79  				t.Errorf("%s: org %s != expected %s", tc.name, org, tc.org)
    80  			}
    81  			if repo != tc.repo {
    82  				t.Errorf("%s: repo %s != expected %s", tc.name, repo, tc.repo)
    83  			}
    84  			if num != tc.num {
    85  				t.Errorf("%s: num %d != expected %d", tc.name, num, tc.num)
    86  			}
    87  		}
    88  	}
    89  }
    90  
    91  func TestMakeQuery(t *testing.T) {
    92  	cases := []struct {
    93  		name       string
    94  		query      string
    95  		closed     bool
    96  		dur        time.Duration
    97  		expected   []string
    98  		unexpected []string
    99  		err        bool
   100  	}{
   101  		{
   102  			name:       "basic query",
   103  			query:      "hello world",
   104  			expected:   []string{"hello world", "is:open"},
   105  			unexpected: []string{"updated:", "openhello", "worldis"},
   106  		},
   107  		{
   108  			name:       "basic closed",
   109  			query:      "hello world",
   110  			closed:     true,
   111  			expected:   []string{"hello world"},
   112  			unexpected: []string{"is:open"},
   113  		},
   114  		{
   115  			name:     "basic duration",
   116  			query:    "hello",
   117  			dur:      1 * time.Hour,
   118  			expected: []string{"hello", "updated:<"},
   119  		},
   120  		{
   121  			name:       "weird characters not escaped",
   122  			query:      "oh yeah!@#$&*()",
   123  			expected:   []string{"!", "@", "#", " "},
   124  			unexpected: []string{"%", "+"},
   125  		},
   126  		{
   127  			name:   "include closed with is:open query errors",
   128  			query:  "hello is:open",
   129  			closed: true,
   130  			err:    true,
   131  		},
   132  		{
   133  			name:  "is:closed without includeClosed",
   134  			query: "hello is:closed",
   135  			err:   true,
   136  		},
   137  	}
   138  
   139  	for _, tc := range cases {
   140  		actual, err := makeQuery(tc.query, tc.closed, tc.dur)
   141  		if err != nil && !tc.err {
   142  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   143  		} else if err == nil && tc.err {
   144  			t.Errorf("%s: failed to raise an error", tc.name)
   145  		}
   146  		for _, e := range tc.expected {
   147  			if !strings.Contains(actual, e) {
   148  				t.Errorf("%s: could not find %s in %s", tc.name, e, actual)
   149  			}
   150  		}
   151  		for _, u := range tc.unexpected {
   152  			if strings.Contains(actual, u) {
   153  				t.Errorf("%s: should not have found %s in %s", tc.name, u, actual)
   154  			}
   155  		}
   156  	}
   157  }
   158  
   159  func makeIssue(owner, repo string, number int, title string) github.Issue {
   160  	return github.Issue{
   161  		HTMLURL: fmt.Sprintf("fake://localhost/%s/%s/pull/%d", owner, repo, number),
   162  		Title:   title,
   163  	}
   164  }
   165  
   166  type fakeClient struct {
   167  	comments []int
   168  	issues   []github.Issue
   169  }
   170  
   171  // Fakes Creating a client, using the same signature as github.Client
   172  func (c *fakeClient) CreateComment(owner, repo string, number int, comment string) error {
   173  	if strings.Contains(comment, "error") || repo == "error" {
   174  		return errors.New(comment)
   175  	}
   176  	c.comments = append(c.comments, number)
   177  	return nil
   178  }
   179  
   180  // Fakes searching for issues, using the same signature as github.Client
   181  func (c *fakeClient) FindIssues(query, sort string, asc bool) ([]github.Issue, error) {
   182  	if strings.Contains(query, "error") {
   183  		return nil, errors.New(query)
   184  	}
   185  	ret := []github.Issue{}
   186  	for _, i := range c.issues {
   187  		if strings.Contains(i.Title, query) {
   188  			ret = append(ret, i)
   189  		}
   190  	}
   191  	return ret, nil
   192  }
   193  
   194  func TestRun(t *testing.T) {
   195  	manyIssues := []github.Issue{}
   196  	manyComments := []int{}
   197  	for i := 0; i < 100; i++ {
   198  		manyIssues = append(manyIssues, makeIssue("o", "r", i, "many "+strconv.Itoa(i)))
   199  		manyComments = append(manyComments, i)
   200  	}
   201  
   202  	cases := []struct {
   203  		name     string
   204  		query    string
   205  		comment  string
   206  		template bool
   207  		ceiling  int
   208  		client   fakeClient
   209  		expected []int
   210  		err      bool
   211  	}{
   212  		{
   213  			name:     "find all",
   214  			query:    "many",
   215  			comment:  "found you",
   216  			client:   fakeClient{issues: manyIssues},
   217  			expected: manyComments,
   218  		},
   219  		{
   220  			name:     "find first 10",
   221  			query:    "many",
   222  			ceiling:  10,
   223  			comment:  "hey",
   224  			client:   fakeClient{issues: manyIssues},
   225  			expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
   226  		},
   227  		{
   228  			name:    "find none",
   229  			query:   "none",
   230  			comment: "this should not happen",
   231  			client:  fakeClient{issues: manyIssues},
   232  		},
   233  		{
   234  			name:    "search error",
   235  			query:   "this search should error",
   236  			comment: "comment",
   237  			client:  fakeClient{issues: manyIssues},
   238  			err:     true,
   239  		},
   240  		{
   241  			name:    "comment error",
   242  			query:   "problematic",
   243  			comment: "rolo tomassi",
   244  			client: fakeClient{issues: []github.Issue{
   245  				makeIssue("o", "r", 1, "problematic this should work"),
   246  				makeIssue("o", "error", 2, "problematic expect an error"),
   247  				makeIssue("o", "r", 3, "problematic works as well"),
   248  			}},
   249  			err:      true,
   250  			expected: []int{1, 3},
   251  		},
   252  		{
   253  			name:     "template comment",
   254  			query:    "67",
   255  			client:   fakeClient{issues: manyIssues},
   256  			comment:  "https://gubernator.k8s.io/pr/{{.Org}}/{{.Repo}}/{{.Number}}",
   257  			template: true,
   258  			expected: []int{67},
   259  		},
   260  		{
   261  			name:     "bad template errors",
   262  			query:    "67",
   263  			client:   fakeClient{issues: manyIssues},
   264  			comment:  "Bad {{.UnknownField}}",
   265  			template: true,
   266  			err:      true,
   267  		},
   268  	}
   269  
   270  	for _, tc := range cases {
   271  		ignoreSorting := ""
   272  		ignoreOrder := false
   273  		err := run(&tc.client, tc.query, ignoreSorting, ignoreOrder, makeCommenter(tc.comment, tc.template), tc.ceiling)
   274  		if tc.err && err == nil {
   275  			t.Errorf("%s: failed to received an error", tc.name)
   276  			continue
   277  		}
   278  		if !tc.err && err != nil {
   279  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   280  			continue
   281  		}
   282  		if len(tc.expected) != len(tc.client.comments) {
   283  			t.Errorf("%s: expected comments %v != actual %v", tc.name, tc.expected, tc.client.comments)
   284  			continue
   285  		}
   286  		missing := []int{}
   287  		for _, e := range tc.expected {
   288  			found := false
   289  			for _, cmt := range tc.client.comments {
   290  				if cmt == e {
   291  					found = true
   292  					break
   293  				}
   294  			}
   295  			if !found {
   296  				missing = append(missing, e)
   297  			}
   298  		}
   299  		if len(missing) > 0 {
   300  			t.Errorf("%s: missing %v from actual comments %v", tc.name, missing, tc.client.comments)
   301  		}
   302  	}
   303  }
   304  
   305  func TestMakeCommenter(t *testing.T) {
   306  	m := meta{
   307  		Number: 10,
   308  		Org:    "org",
   309  		Repo:   "repo",
   310  		Issue: github.Issue{
   311  			Number:  10,
   312  			HTMLURL: "url",
   313  			Title:   "title",
   314  		},
   315  	}
   316  	cases := []struct {
   317  		name     string
   318  		comment  string
   319  		template bool
   320  		expected string
   321  		err      bool
   322  	}{
   323  		{
   324  			name:     "string works",
   325  			comment:  "hello world {{.Number}} {{.Invalid}}",
   326  			expected: "hello world {{.Number}} {{.Invalid}}",
   327  		},
   328  		{
   329  			name:     "template works",
   330  			comment:  "N={{.Number}} R={{.Repo}} O={{.Org}} U={{.Issue.HTMLURL}} T={{.Issue.Title}}",
   331  			template: true,
   332  			expected: "N=10 R=repo O=org U=url T=title",
   333  		},
   334  		{
   335  			name:     "bad template errors",
   336  			comment:  "Bad {{.UnknownField}} Template",
   337  			expected: "Bad ",
   338  			template: true,
   339  			err:      true,
   340  		},
   341  	}
   342  
   343  	for _, tc := range cases {
   344  		c := makeCommenter(tc.comment, tc.template)
   345  		actual, err := c(m)
   346  		if actual != tc.expected {
   347  			t.Errorf("%s: expected '%s' != actual '%s'", tc.name, tc.expected, actual)
   348  		}
   349  		if err != nil && !tc.err {
   350  			t.Errorf("%s: unexpected err: %v", tc.name, err)
   351  		}
   352  		if err == nil && tc.err {
   353  			t.Errorf("%s: failed to raise an exception", tc.name)
   354  		}
   355  	}
   356  }