github.com/abayer/test-infra@v0.0.5/prow/plugins/assign/assign_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 assign
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/sirupsen/logrus"
    23  
    24  	"k8s.io/test-infra/prow/github"
    25  )
    26  
    27  type fakeClient struct {
    28  	assigned   map[string]int
    29  	unassigned map[string]int
    30  
    31  	requested    map[string]int
    32  	unrequested  map[string]int
    33  	contributors map[string]bool
    34  
    35  	commented bool
    36  }
    37  
    38  func (c *fakeClient) UnassignIssue(owner, repo string, number int, assignees []string) error {
    39  	for _, who := range assignees {
    40  		c.unassigned[who]++
    41  	}
    42  
    43  	return nil
    44  }
    45  
    46  func (c *fakeClient) AssignIssue(owner, repo string, number int, assignees []string) error {
    47  	var missing github.MissingUsers
    48  	for _, who := range assignees {
    49  		if who != "evil" {
    50  			c.assigned[who]++
    51  		} else {
    52  			missing.Users = append(missing.Users, who)
    53  		}
    54  	}
    55  
    56  	if len(missing.Users) == 0 {
    57  		return nil
    58  	}
    59  	return missing
    60  }
    61  
    62  func (c *fakeClient) RequestReview(org, repo string, number int, logins []string) error {
    63  	var missing github.MissingUsers
    64  	for _, user := range logins {
    65  		if c.contributors[user] {
    66  			c.requested[user]++
    67  		} else {
    68  			missing.Users = append(missing.Users, user)
    69  		}
    70  	}
    71  	if len(missing.Users) > 0 {
    72  		return missing
    73  	}
    74  	return nil
    75  }
    76  
    77  func (c *fakeClient) UnrequestReview(org, repo string, number int, logins []string) error {
    78  	for _, user := range logins {
    79  		c.unrequested[user]++
    80  	}
    81  	return nil
    82  }
    83  
    84  func (c *fakeClient) CreateComment(owner, repo string, number int, comment string) error {
    85  	c.commented = comment != ""
    86  	return nil
    87  }
    88  
    89  func newFakeClient(contribs []string) *fakeClient {
    90  	c := &fakeClient{
    91  		contributors: make(map[string]bool),
    92  		requested:    make(map[string]int),
    93  		unrequested:  make(map[string]int),
    94  		assigned:     make(map[string]int),
    95  		unassigned:   make(map[string]int),
    96  	}
    97  	for _, user := range contribs {
    98  		c.contributors[user] = true
    99  	}
   100  	return c
   101  }
   102  
   103  func TestParseLogins(t *testing.T) {
   104  	var testcases = []struct {
   105  		name   string
   106  		text   string
   107  		logins []string
   108  	}{
   109  		{
   110  			name: "empty",
   111  			text: "",
   112  		},
   113  		{
   114  			name:   "one",
   115  			text:   " @jungle",
   116  			logins: []string{"jungle"},
   117  		},
   118  		{
   119  			name:   "two",
   120  			text:   " @erick @fejta",
   121  			logins: []string{"erick", "fejta"},
   122  		},
   123  		{
   124  			name:   "one team",
   125  			text:   " @kubernetes/sig-testing-misc",
   126  			logins: []string{"kubernetes/sig-testing-misc"},
   127  		},
   128  		{
   129  			name:   "two teams",
   130  			text:   " @kubernetes/sig-testing-misc @kubernetes/sig-testing-bugs",
   131  			logins: []string{"kubernetes/sig-testing-misc", "kubernetes/sig-testing-bugs"},
   132  		},
   133  	}
   134  	for _, tc := range testcases {
   135  		l := parseLogins(tc.text)
   136  		if len(l) != len(tc.logins) {
   137  			t.Errorf("For case %s, expected %s and got %s", tc.name, tc.logins, l)
   138  		}
   139  		for n, who := range l {
   140  			if tc.logins[n] != who {
   141  				t.Errorf("For case %s, expected %s and got %s", tc.name, tc.logins, l)
   142  			}
   143  		}
   144  	}
   145  }
   146  
   147  // TestAssignAndReview tests that the handle function uses the github client
   148  // to correctly create and/or delete assignments and PR review requests.
   149  func TestAssignAndReview(t *testing.T) {
   150  	var testcases = []struct {
   151  		name        string
   152  		body        string
   153  		commenter   string
   154  		assigned    []string
   155  		unassigned  []string
   156  		requested   []string
   157  		unrequested []string
   158  		commented   bool
   159  	}{
   160  		{
   161  			name:      "unrelated comment",
   162  			body:      "uh oh",
   163  			commenter: "o",
   164  		},
   165  		{
   166  			name:      "assign on open",
   167  			body:      "/assign",
   168  			commenter: "rando",
   169  			assigned:  []string{"rando"},
   170  		},
   171  		{
   172  			name:      "assign me",
   173  			body:      "/assign",
   174  			commenter: "rando",
   175  			assigned:  []string{"rando"},
   176  		},
   177  		{
   178  			name:       "unassign myself",
   179  			body:       "/unassign",
   180  			commenter:  "rando",
   181  			unassigned: []string{"rando"},
   182  		},
   183  		{
   184  			name:      "tab completion",
   185  			body:      "/assign @fejta ",
   186  			commenter: "rando",
   187  			assigned:  []string{"fejta"},
   188  		},
   189  		{
   190  			name:      "no @ works too",
   191  			body:      "/assign fejta",
   192  			commenter: "rando",
   193  			assigned:  []string{"fejta"},
   194  		},
   195  		{
   196  			name:       "multi commands",
   197  			body:       "/assign @fejta\n/unassign @spxtr",
   198  			commenter:  "rando",
   199  			assigned:   []string{"fejta"},
   200  			unassigned: []string{"spxtr"},
   201  		},
   202  		{
   203  			name:      "interesting names",
   204  			body:      "/assign @hello-world @allow_underscore",
   205  			commenter: "rando",
   206  			assigned:  []string{"hello-world", "allow_underscore"},
   207  		},
   208  		{
   209  			name:      "bad login",
   210  			commenter: "rando",
   211  			body:      "/assign @Invalid$User",
   212  		},
   213  		{
   214  			name:      "bad login, no @",
   215  			commenter: "rando",
   216  			body:      "/assign Invalid$User",
   217  		},
   218  		{
   219  			name:      "assign friends",
   220  			body:      "/assign @bert @ernie",
   221  			commenter: "rando",
   222  			assigned:  []string{"bert", "ernie"},
   223  		},
   224  		{
   225  			name:       "unassign buddies",
   226  			body:       "/unassign @ashitaka @eboshi",
   227  			commenter:  "san",
   228  			unassigned: []string{"ashitaka", "eboshi"},
   229  		},
   230  		{
   231  			name:       "unassign buddies, trailing space.",
   232  			body:       "/unassign @ashitaka @eboshi \r",
   233  			commenter:  "san",
   234  			unassigned: []string{"ashitaka", "eboshi"},
   235  		},
   236  		{
   237  			name:      "evil commenter",
   238  			body:      "/assign @merlin",
   239  			commenter: "evil",
   240  			assigned:  []string{"merlin"},
   241  		},
   242  		{
   243  			name:      "evil commenter self assign",
   244  			body:      "/assign",
   245  			commenter: "evil",
   246  			commented: true,
   247  		},
   248  		{
   249  			name:      "evil assignee",
   250  			body:      "/assign @evil @merlin",
   251  			commenter: "innocent",
   252  			assigned:  []string{"merlin"},
   253  			commented: true,
   254  		},
   255  		{
   256  			name:       "evil unassignee",
   257  			body:       "/unassign @evil @merlin",
   258  			commenter:  "innocent",
   259  			unassigned: []string{"evil", "merlin"},
   260  		},
   261  		{
   262  			name:      "review on open",
   263  			body:      "/cc @merlin",
   264  			commenter: "rando",
   265  			requested: []string{"merlin"},
   266  		},
   267  		{
   268  			name:      "tab completion",
   269  			body:      "/cc @cjwagner ",
   270  			commenter: "rando",
   271  			requested: []string{"cjwagner"},
   272  		},
   273  		{
   274  			name:      "no @ works too",
   275  			body:      "/cc cjwagner ",
   276  			commenter: "rando",
   277  			requested: []string{"cjwagner"},
   278  		},
   279  		{
   280  			name:        "multi commands",
   281  			body:        "/cc @cjwagner\n/uncc @spxtr",
   282  			commenter:   "rando",
   283  			requested:   []string{"cjwagner"},
   284  			unrequested: []string{"spxtr"},
   285  		},
   286  		{
   287  			name:      "interesting names",
   288  			body:      "/cc @hello-world @allow_underscore",
   289  			commenter: "rando",
   290  			requested: []string{"hello-world", "allow_underscore"},
   291  		},
   292  		{
   293  			name:      "bad login",
   294  			commenter: "rando",
   295  			body:      "/cc @Invalid$User",
   296  		},
   297  		{
   298  			name:      "bad login",
   299  			commenter: "rando",
   300  			body:      "/cc Invalid$User",
   301  		},
   302  		{
   303  			name:      "request multiple",
   304  			body:      "/cc @cjwagner @merlin",
   305  			commenter: "rando",
   306  			requested: []string{"cjwagner", "merlin"},
   307  		},
   308  		{
   309  			name:        "unrequest buddies",
   310  			body:        "/uncc @ashitaka @eboshi",
   311  			commenter:   "san",
   312  			unrequested: []string{"ashitaka", "eboshi"},
   313  		},
   314  		{
   315  			name:      "evil commenter",
   316  			body:      "/cc @merlin",
   317  			commenter: "evil",
   318  			requested: []string{"merlin"},
   319  		},
   320  		{
   321  			name:      "evil reviewer requested",
   322  			body:      "/cc @evil @merlin",
   323  			commenter: "innocent",
   324  			requested: []string{"merlin"},
   325  			commented: true,
   326  		},
   327  		{
   328  			name:        "evil reviewer unrequested",
   329  			body:        "/uncc @evil @merlin",
   330  			commenter:   "innocent",
   331  			unrequested: []string{"evil", "merlin"},
   332  		},
   333  		{
   334  			name:        "multi command types",
   335  			body:        "/assign @fejta\n/unassign @spxtr @cjwagner\n/uncc @merlin \n/cc @cjwagner",
   336  			commenter:   "rando",
   337  			assigned:    []string{"fejta"},
   338  			unassigned:  []string{"spxtr", "cjwagner"},
   339  			requested:   []string{"cjwagner"},
   340  			unrequested: []string{"merlin"},
   341  		},
   342  		{
   343  			name:      "request review self",
   344  			body:      "/cc",
   345  			commenter: "cjwagner",
   346  			requested: []string{"cjwagner"},
   347  		},
   348  		{
   349  			name:        "unrequest review self",
   350  			body:        "/uncc",
   351  			commenter:   "cjwagner",
   352  			unrequested: []string{"cjwagner"},
   353  		},
   354  		{
   355  			name:        "request review self, with unrequest friend, with trailing space.",
   356  			body:        "/cc \n/uncc @spxtr ",
   357  			commenter:   "cjwagner",
   358  			requested:   []string{"cjwagner"},
   359  			unrequested: []string{"spxtr"},
   360  		},
   361  		{
   362  			name:      "request team review",
   363  			body:      "/cc @kubernetes/sig-testing-misc",
   364  			commenter: "rando",
   365  			requested: []string{"kubernetes/sig-testing-misc"},
   366  		},
   367  		{
   368  			name:        "unrequest team review",
   369  			body:        "/uncc @kubernetes/sig-testing-misc",
   370  			commenter:   "rando",
   371  			unrequested: []string{"kubernetes/sig-testing-misc"},
   372  		},
   373  	}
   374  	for _, tc := range testcases {
   375  		fc := newFakeClient([]string{"hello-world", "allow_underscore", "cjwagner", "merlin", "kubernetes/sig-testing-misc"})
   376  		e := github.GenericCommentEvent{
   377  			Body:   tc.body,
   378  			User:   github.User{Login: tc.commenter},
   379  			Repo:   github.Repo{Name: "repo", Owner: github.User{Login: "org"}},
   380  			Number: 5,
   381  		}
   382  		if err := handle(newAssignHandler(e, fc, logrus.WithField("plugin", pluginName))); err != nil {
   383  			t.Errorf("For case %s, didn't expect error from handle: %v", tc.name, err)
   384  			continue
   385  		}
   386  		if err := handle(newReviewHandler(e, fc, logrus.WithField("plugin", pluginName))); err != nil {
   387  			t.Errorf("For case %s, didn't expect error from handle: %v", tc.name, err)
   388  			continue
   389  		}
   390  
   391  		if tc.commented != fc.commented {
   392  			t.Errorf("For case %s, expect commented: %v, got commented %v", tc.name, tc.commented, fc.commented)
   393  		}
   394  
   395  		if len(fc.assigned) != len(tc.assigned) {
   396  			t.Errorf("For case %s, assigned actual %v != expected %s", tc.name, fc.assigned, tc.assigned)
   397  		} else {
   398  			for _, who := range tc.assigned {
   399  				if n, ok := fc.assigned[who]; !ok || n < 1 {
   400  					t.Errorf("For case %s, assigned actual %v != expected %s", tc.name, fc.assigned, tc.assigned)
   401  					break
   402  				}
   403  			}
   404  		}
   405  		if len(fc.unassigned) != len(tc.unassigned) {
   406  			t.Errorf("For case %s, unassigned %v != %s", tc.name, fc.unassigned, tc.unassigned)
   407  		} else {
   408  			for _, who := range tc.unassigned {
   409  				if n, ok := fc.unassigned[who]; !ok || n < 1 {
   410  					t.Errorf("For case %s, unassigned %v != %s", tc.name, fc.unassigned, tc.unassigned)
   411  					break
   412  				}
   413  			}
   414  		}
   415  
   416  		if len(fc.requested) != len(tc.requested) {
   417  			t.Errorf("For case %s, requested actual %v != expected %s", tc.name, fc.requested, tc.requested)
   418  		} else {
   419  			for _, who := range tc.requested {
   420  				if n, ok := fc.requested[who]; !ok || n < 1 {
   421  					t.Errorf("For case %s, requested actual %v != expected %s", tc.name, fc.requested, tc.requested)
   422  					break
   423  				}
   424  			}
   425  		}
   426  		if len(fc.unrequested) != len(tc.unrequested) {
   427  			t.Errorf("For case %s, unrequested %v != %s", tc.name, fc.unrequested, tc.unrequested)
   428  		} else {
   429  			for _, who := range tc.unrequested {
   430  				if n, ok := fc.unrequested[who]; !ok || n < 1 {
   431  					t.Errorf("For case %s, unrequested %v != %s", tc.name, fc.unrequested, tc.unrequested)
   432  					break
   433  				}
   434  			}
   435  		}
   436  	}
   437  }