github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/slackevents/slackevents_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 slackevents
    18  
    19  import (
    20  	"encoding/json"
    21  	"strings"
    22  	"testing"
    23  
    24  	"sigs.k8s.io/prow/pkg/config"
    25  	"sigs.k8s.io/prow/pkg/github"
    26  	"sigs.k8s.io/prow/pkg/github/fakegithub"
    27  	"sigs.k8s.io/prow/pkg/plugins"
    28  	"sigs.k8s.io/prow/pkg/slack"
    29  )
    30  
    31  type FakeClient struct {
    32  	SentMessages map[string][]string
    33  }
    34  
    35  func (fk *FakeClient) WriteMessage(text string, channel string) error {
    36  	fk.SentMessages[channel] = append(fk.SentMessages[channel], text)
    37  	return nil
    38  }
    39  
    40  func TestPush(t *testing.T) {
    41  	var pushStr = `{
    42    "ref": "refs/heads/master",
    43    "before": "d73a75b4b1ddb63870954b9a60a63acaa4cb1ca5",
    44    "after": "045a6dca07840efaf3311450b615e19b5c75f787",
    45    "created": false,
    46    "deleted": false,
    47    "forced": false,
    48    "compare": "https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...045a6dca0784",
    49    "commits": [
    50      {
    51        "id": "8427d5a27478c80167fd66affe1bd7cd01d3f9a8",
    52        "message": "Decrease fluentd cpu request",
    53        "url": "https://github.com/kubernetes/kubernetes/commit/8427d5a27478c80167fd66affe1bd7cd01d3f9a8"
    54      },
    55      {
    56        "id": "045a6dca07840efaf3311450b615e19b5c75f787",
    57        "message": "Merge pull request #47906 from gmarek/fluentd\n\nDecrese fluentd cpu request\n\nFix #47905\r\n\r\ncc @piosz - this should fix your tests.\r\ncc @dchen1107",
    58        "url": "https://github.com/kubernetes/kubernetes/commit/045a6dca07840efaf3311450b615e19b5c75f787"
    59      }
    60    ],
    61    "repository": {
    62      "id": 20580498,
    63      "name": "kubernetes",
    64      "owner": {
    65  	"name": "kubernetes",
    66  	"login": "kubernetes"
    67      },
    68      "url": "https://github.com/kubernetes/kubernetes"
    69    },
    70    "pusher": {
    71      "name": "k8s-merge-robot",
    72      "email": "k8s-merge-robot@users.noreply.github.com"
    73    }
    74  }`
    75  
    76  	var pushEv github.PushEvent
    77  	if err := json.Unmarshal([]byte(pushStr), &pushEv); err != nil {
    78  		t.Fatalf("Failed to parse Push Notification: %s", err)
    79  	}
    80  
    81  	// Non bot user merged the PR
    82  	pushEvManual := pushEv
    83  	pushEvManual.Pusher.Name = "Jester Tester"
    84  	pushEvManual.Pusher.Email = "tester@users.noreply.github.com"
    85  	pushEvManual.Sender.Login = "tester"
    86  	pushEvManual.Ref = "refs/heads/master"
    87  
    88  	pushEvManualBranchExempted := pushEv
    89  	pushEvManualBranchExempted.Pusher.Name = "Warren Teened"
    90  	pushEvManualBranchExempted.Pusher.Email = "wteened@users.noreply.github.com"
    91  	pushEvManualBranchExempted.Sender.Login = "WTeened"
    92  	pushEvManualBranchExempted.Ref = "refs/heads/warrens-branch"
    93  
    94  	pushEvManualNotBranchExempted := pushEvManualBranchExempted
    95  	pushEvManualNotBranchExempted.Ref = "refs/heads/master"
    96  
    97  	pushEvManualCreated := pushEvManual
    98  	pushEvManualCreated.Created = true
    99  	pushEvManualCreated.Ref = "refs/heads/release-1.99"
   100  	pushEvManualCreated.Compare = "https://github.com/kubernetes/kubernetes/compare/045a6dca0784"
   101  
   102  	pushEvManualDeleted := pushEvManual
   103  	pushEvManualDeleted.Deleted = true
   104  	pushEvManualDeleted.Ref = "refs/heads/release-1.99"
   105  	pushEvManualDeleted.Compare = "https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...000000000000"
   106  
   107  	pushEvManualForced := pushEvManual
   108  	pushEvManualForced.Forced = true
   109  
   110  	noMessages := map[string][]string{}
   111  	stdWarningMessages := map[string][]string{
   112  		"sig-contribex":  {"*Warning:* tester (<@tester>) manually merged 2 commit(s) into master: https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...045a6dca0784"},
   113  		"kubernetes-foo": {"*Warning:* tester (<@tester>) manually merged 2 commit(s) into master: https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...045a6dca0784"}}
   114  
   115  	createdWarningMessages := map[string][]string{
   116  		"sig-contribex":  {"*Warning:* tester (<@tester>) pushed a new branch (release-1.99): https://github.com/kubernetes/kubernetes/compare/045a6dca0784"},
   117  		"kubernetes-foo": {"*Warning:* tester (<@tester>) pushed a new branch (release-1.99): https://github.com/kubernetes/kubernetes/compare/045a6dca0784"}}
   118  
   119  	deletedWarningMessages := map[string][]string{
   120  		"sig-contribex":  {"*Warning:* tester (<@tester>) deleted a branch (release-1.99): https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...000000000000"},
   121  		"kubernetes-foo": {"*Warning:* tester (<@tester>) deleted a branch (release-1.99): https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...000000000000"}}
   122  
   123  	forcedWarningMessages := map[string][]string{
   124  		"sig-contribex":  {"*Warning:* tester (<@tester>) *force* merged 2 commit(s) into master: https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...045a6dca0784"},
   125  		"kubernetes-foo": {"*Warning:* tester (<@tester>) *force* merged 2 commit(s) into master: https://github.com/kubernetes/kubernetes/compare/d73a75b4b1dd...045a6dca0784"}}
   126  
   127  	type testCase struct {
   128  		name             string
   129  		pushReq          github.PushEvent
   130  		expectedMessages map[string][]string
   131  	}
   132  
   133  	testcases := []testCase{
   134  		{
   135  			name:             "If PR merged manually by a user, we send message to sig-contribex and kubernetes-foo.",
   136  			pushReq:          pushEvManual,
   137  			expectedMessages: stdWarningMessages,
   138  		},
   139  		{
   140  			name:             "If PR force merged by a user, we send message to sig-contribex and kubernetes-foo with force merge message.",
   141  			pushReq:          pushEvManualForced,
   142  			expectedMessages: forcedWarningMessages,
   143  		},
   144  		{
   145  			name:             "If PR merged by k8s merge bot we should NOT send message to sig-contribex and kubernetes-foo.",
   146  			pushReq:          pushEv,
   147  			expectedMessages: noMessages,
   148  		},
   149  		{
   150  			name:             "If PR merged by a user not in the exemption list but in THIS branch exemption list, we should NOT send a message to sig-contribex and kubernetes-foo.",
   151  			pushReq:          pushEvManualBranchExempted,
   152  			expectedMessages: noMessages,
   153  		},
   154  		{
   155  			name:             "If PR merged by a user not in the exemption list, in a branch exemption list, but not THIS branch exemption list, we should send a message to sig-contribex and kubernetes-foo.",
   156  			pushReq:          pushEvManualBranchExempted,
   157  			expectedMessages: noMessages,
   158  		},
   159  		{
   160  			name:             "If a branch is created by a non-exempted user, we send message to sig-contribex and kubernetes-foo with branch created message.",
   161  			pushReq:          pushEvManualCreated,
   162  			expectedMessages: createdWarningMessages,
   163  		},
   164  		{
   165  			name:             "If a branch is deleted by a non-exempted user, we send message to sig-contribex and kubernetes-foo with branch deleted message.",
   166  			pushReq:          pushEvManualDeleted,
   167  			expectedMessages: deletedWarningMessages,
   168  		},
   169  	}
   170  
   171  	pc := client{
   172  		SlackConfig: plugins.Slack{
   173  			MergeWarnings: []plugins.MergeWarning{
   174  				{
   175  					Repos:       []string{"kubernetes/kubernetes"},
   176  					Channels:    []string{"kubernetes-foo", "sig-contribex"},
   177  					ExemptUsers: []string{"k8s-merge-robot"},
   178  					ExemptBranches: map[string][]string{
   179  						"warrens-branch": {"wteened"},
   180  					},
   181  				},
   182  			},
   183  		},
   184  		SlackClient: slack.NewFakeClient(),
   185  	}
   186  
   187  	//should not fail if slackClient is nil
   188  	for _, tc := range testcases {
   189  		if err := notifyOnSlackIfManualMerge(pc, tc.pushReq); err != nil {
   190  			t.Fatalf("Didn't expect error if slack client is nil: %s", err)
   191  		}
   192  	}
   193  
   194  	//repeat the tests with a fake slack client
   195  	for _, tc := range testcases {
   196  		slackClient := &FakeClient{
   197  			SentMessages: make(map[string][]string),
   198  		}
   199  		pc.SlackClient = slackClient
   200  
   201  		if err := notifyOnSlackIfManualMerge(pc, tc.pushReq); err != nil {
   202  			t.Fatalf("Didn't expect error: %s", err)
   203  		}
   204  		if len(tc.expectedMessages) != len(slackClient.SentMessages) {
   205  			t.Fatalf("Test: %s The number of messages sent do not tally. Expecting %d messages but received %d messages.",
   206  				tc.name, len(tc.expectedMessages), len(slackClient.SentMessages))
   207  		}
   208  		for k, v := range tc.expectedMessages {
   209  			if _, ok := slackClient.SentMessages[k]; !ok {
   210  				t.Fatalf("Test: %s Messages is not sent to channel %s", tc.name, k)
   211  			}
   212  			if strings.Compare(v[0], slackClient.SentMessages[k][0]) != 0 {
   213  				t.Fatalf("Expecting message: %s\nReceived message: %s", v, slackClient.SentMessages[k])
   214  			}
   215  			if len(v) != len(slackClient.SentMessages[k]) {
   216  				t.Fatalf("Test: %s All messages are not delivered to the channel ", tc.name)
   217  			}
   218  		}
   219  	}
   220  
   221  }
   222  
   223  // Make sure we are sending message to proper sig mentions
   224  func TestComment(t *testing.T) {
   225  	orgMember := "cjwagner"
   226  	bot := "k8s-ci-robot"
   227  	type testCase struct {
   228  		name             string
   229  		action           github.GenericCommentEventAction
   230  		body             string
   231  		expectedMessages map[string][]string
   232  		issueLabels      []string
   233  		commenter        string
   234  	}
   235  	testcases := []testCase{
   236  		{
   237  			name:             "If sig mentioned then we send a message to the sig with the body of the comment",
   238  			action:           github.GenericCommentActionCreated,
   239  			body:             "@kubernetes/sig-node-misc This issue needs update.",
   240  			expectedMessages: map[string][]string{"sig-node": {"This issue needs update."}},
   241  			commenter:        orgMember,
   242  		},
   243  		{
   244  			name:             "Don't sent message if comment isn't new.",
   245  			action:           github.GenericCommentActionEdited,
   246  			body:             "@kubernetes/sig-node-misc This issue needs update.",
   247  			expectedMessages: map[string][]string{},
   248  			commenter:        orgMember,
   249  		},
   250  		{
   251  			name:             "Don't sent message if commenter is the bot.",
   252  			action:           github.GenericCommentActionEdited,
   253  			body:             "@kubernetes/sig-node-misc This issue needs update.",
   254  			expectedMessages: map[string][]string{},
   255  			commenter:        bot,
   256  		},
   257  		{
   258  			name:             "If multiple sigs mentioned, we send a message to each sig with the body of the comment",
   259  			action:           github.GenericCommentActionCreated,
   260  			body:             "@kubernetes/sig-node-misc, @kubernetes/sig-api-machinery-misc Message sent to multiple sigs.",
   261  			expectedMessages: map[string][]string{"sig-api-machinery": {"Message sent to multiple sigs."}, "sig-node": {"Message sent to multiple sigs."}},
   262  			commenter:        orgMember,
   263  		},
   264  		{
   265  			name:             "If multiple sigs mentioned, but only one channel is allowed, only send to one channel.",
   266  			action:           github.GenericCommentActionCreated,
   267  			body:             "@kubernetes/sig-node-misc, @kubernetes/sig-testing-misc Message sent to multiple sigs.",
   268  			expectedMessages: map[string][]string{"sig-node": {"Message sent to multiple sigs."}},
   269  			issueLabels:      []string{},
   270  			commenter:        orgMember,
   271  		},
   272  		{
   273  			name:             "Message should not be sent if the pattern for the channel does not match",
   274  			action:           github.GenericCommentActionCreated,
   275  			body:             "@kubernetes/node-misc No message sent",
   276  			expectedMessages: map[string][]string{},
   277  			commenter:        orgMember,
   278  		},
   279  		{
   280  			name:             "Message sent only if the pattern for the channel match",
   281  			action:           github.GenericCommentActionCreated,
   282  			body:             "@kubernetes/node-misc @kubernetes/sig-api-machinery-bugs Message sent to matching sigs.",
   283  			expectedMessages: map[string][]string{"sig-api-machinery": {"Message sent to matching sigs."}},
   284  			commenter:        orgMember,
   285  		},
   286  	}
   287  
   288  	for _, tc := range testcases {
   289  		fakeSlackClient := &FakeClient{
   290  			SentMessages: make(map[string][]string),
   291  		}
   292  		client := client{
   293  			GitHubClient: fakegithub.NewFakeClient(),
   294  			SlackClient:  fakeSlackClient,
   295  			SlackConfig:  plugins.Slack{MentionChannels: []string{"sig-node", "sig-api-machinery"}},
   296  		}
   297  		e := github.GenericCommentEvent{
   298  			Action: tc.action,
   299  			Body:   tc.body,
   300  			User:   github.User{Login: tc.commenter},
   301  		}
   302  
   303  		if err := echoToSlack(client, e); err != nil {
   304  			t.Fatalf("For case %s, didn't expect error from label test: %v", tc.name, err)
   305  		}
   306  		if len(tc.expectedMessages) != len(fakeSlackClient.SentMessages) {
   307  			t.Fatalf("The number of messages sent do not tally. Expecting %d messages but received %d messages.",
   308  				len(tc.expectedMessages), len(fakeSlackClient.SentMessages))
   309  		}
   310  		for k, v := range tc.expectedMessages {
   311  			if _, ok := fakeSlackClient.SentMessages[k]; !ok {
   312  				t.Fatalf("Messages is not sent to channel %s", k)
   313  			}
   314  			if len(v) != len(fakeSlackClient.SentMessages[k]) {
   315  				t.Fatalf("All messages are not delivered to the channel %s", k)
   316  			}
   317  		}
   318  	}
   319  }
   320  
   321  func TestHelpProvider(t *testing.T) {
   322  	enabledRepos := []config.OrgRepo{
   323  		{Org: "org1", Repo: "repo"},
   324  		{Org: "org2", Repo: "repo"},
   325  	}
   326  	cases := []struct {
   327  		name         string
   328  		config       *plugins.Configuration
   329  		enabledRepos []config.OrgRepo
   330  		err          bool
   331  	}{
   332  		{
   333  			name:         "Empty config",
   334  			config:       &plugins.Configuration{},
   335  			enabledRepos: enabledRepos,
   336  		},
   337  		{
   338  			name: "All configs enabled",
   339  			config: &plugins.Configuration{
   340  				Slack: plugins.Slack{
   341  					MentionChannels: []string{"chan1", "chan2"},
   342  					MergeWarnings: []plugins.MergeWarning{
   343  						{
   344  							Repos:       []string{"org2/repo"},
   345  							Channels:    []string{"chan1", "chan2"},
   346  							ExemptUsers: []string{"k8s-merge-robot"},
   347  							ExemptBranches: map[string][]string{
   348  								"warrens-branch": {"wteened"},
   349  							},
   350  						},
   351  					},
   352  				},
   353  			},
   354  			enabledRepos: enabledRepos,
   355  		},
   356  	}
   357  	for _, c := range cases {
   358  		t.Run(c.name, func(t *testing.T) {
   359  			_, err := helpProvider(c.config, c.enabledRepos)
   360  			if err != nil && !c.err {
   361  				t.Fatalf("helpProvider error: %v", err)
   362  			}
   363  		})
   364  	}
   365  }