sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/pluginhelp/hook/hook_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 hook
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"reflect"
    24  	"sort"
    25  	"testing"
    26  
    27  	"github.com/sirupsen/logrus"
    28  
    29  	"k8s.io/apimachinery/pkg/util/sets"
    30  	prowconfig "sigs.k8s.io/prow/pkg/config"
    31  	"sigs.k8s.io/prow/pkg/github"
    32  	"sigs.k8s.io/prow/pkg/pluginhelp"
    33  	"sigs.k8s.io/prow/pkg/pluginhelp/externalplugins"
    34  	"sigs.k8s.io/prow/pkg/plugins"
    35  )
    36  
    37  type fakeGitHubClient map[string][]string
    38  
    39  func (fghc fakeGitHubClient) GetRepos(org string, _ bool) ([]github.Repo, error) {
    40  	var repos []github.Repo
    41  	for _, repo := range fghc[org] {
    42  		repos = append(repos, github.Repo{FullName: fmt.Sprintf("%s/%s", org, repo)})
    43  	}
    44  	return repos, nil
    45  }
    46  
    47  type fakePluginAgent plugins.Configuration
    48  
    49  func (fpa fakePluginAgent) Config() *plugins.Configuration {
    50  	config := plugins.Configuration(fpa)
    51  	return &config
    52  }
    53  
    54  func TestGeneratePluginHelp(t *testing.T) {
    55  	orgToRepos := map[string][]string{"org1": {"repo1", "repo2", "repo3"}, "org2": {"repo1"}}
    56  	fghc := fakeGitHubClient(orgToRepos)
    57  
    58  	normalHelp := map[string]pluginhelp.PluginHelp{
    59  		"org-plugin": {Description: "org-plugin", Config: map[string]string{"": "overall config"}},
    60  		"repo-plugin1": {
    61  			Description: "repo-plugin1",
    62  			Config: map[string]string{
    63  				"org1/repo1": "repo1 config",
    64  				"org1/repo2": "repo2 config",
    65  			},
    66  		},
    67  		"repo-plugin2": {Description: "repo-plugin2", Config: map[string]string{}},
    68  		"repo-plugin3": {Description: "repo-plugin3", Config: map[string]string{}},
    69  	}
    70  	helpfulExternalHelp := pluginhelp.PluginHelp{Description: "helpful-external"}
    71  	noHelpSever := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    72  		http.Error(w, "404 Not Found", http.StatusNotFound)
    73  	}))
    74  	defer noHelpSever.Close()
    75  	mux := http.NewServeMux()
    76  	externalplugins.ServeExternalPluginHelp(
    77  		mux,
    78  		logrus.WithField("plugin", "helpful-external"),
    79  		func(enabledRepos []prowconfig.OrgRepo) (*pluginhelp.PluginHelp, error) {
    80  			if got, expected := enabledRepos, []prowconfig.OrgRepo{{Org: "org1", Repo: "repo1"}}; !reflect.DeepEqual(got, expected) {
    81  				t.Errorf("Plugin 'helpful-external' expected to be enabled on repos %q, but got %q.", expected, got)
    82  			}
    83  			return &helpfulExternalHelp, nil
    84  		},
    85  	)
    86  	helpfulServer := httptest.NewServer(mux)
    87  	defer helpfulServer.Close()
    88  
    89  	config := &plugins.Configuration{
    90  		Plugins: plugins.Plugins{
    91  			"org1":       {Plugins: []string{"org-plugin"}},
    92  			"org1/repo1": {Plugins: []string{"repo-plugin1", "no-help-plugin"}},
    93  			"org1/repo2": {Plugins: []string{"repo-plugin1", "repo-plugin2"}},
    94  			"org2/repo1": {Plugins: []string{"repo-plugin3"}},
    95  		},
    96  		ExternalPlugins: map[string][]plugins.ExternalPlugin{
    97  			"org1/repo1": {
    98  				{Name: "no-endpoint-external", Endpoint: "http://no-endpoint-external", Events: []string{"issue_comment"}},
    99  				{Name: "no-help-external", Endpoint: noHelpSever.URL, Events: []string{"issue_comment"}},
   100  				{Name: "helpful-external", Endpoint: helpfulServer.URL, Events: []string{"pull_request", "issue"}},
   101  			},
   102  		},
   103  	}
   104  	fpa := fakePluginAgent(*config)
   105  
   106  	expectedAllRepos := []string{"org1/repo1", "org1/repo2", "org1/repo3", "org2/repo1"}
   107  
   108  	normalExpectedReposForPlugin := map[string][]string{
   109  		"org-plugin":     {"org1/repo1", "org1/repo2", "org1/repo3"},
   110  		"repo-plugin1":   {"org1/repo1", "org1/repo2"},
   111  		"repo-plugin2":   {"org1/repo2"},
   112  		"repo-plugin3":   {"org2/repo1"},
   113  		"no-help-plugin": {"org1/repo1"},
   114  	}
   115  	normalExpectedPluginsForRepo := map[string][]string{
   116  		"":           {"org-plugin", "repo-plugin1", "repo-plugin2", "repo-plugin3", "no-help-plugin"},
   117  		"org1":       {"org-plugin"},
   118  		"org1/repo1": {"repo-plugin1", "no-help-plugin"},
   119  		"org1/repo2": {"repo-plugin1", "repo-plugin2"},
   120  		"org2/repo1": {"repo-plugin3"},
   121  	}
   122  	normalExpectedEvents := map[string][]string{
   123  		"org-plugin":     {"issue_comment"},
   124  		"repo-plugin1":   {"issue"},
   125  		"repo-plugin2":   {"pull_request"},
   126  		"repo-plugin3":   {"pull_request_review", "pull_request_review_comment"},
   127  		"no-help-plugin": {"issue_comment"},
   128  	}
   129  
   130  	externalExpectedPluginsForRepo := map[string][]string{
   131  		"":           {"no-endpoint-external", "no-help-external", "helpful-external"},
   132  		"org1/repo1": {"no-endpoint-external", "no-help-external", "helpful-external"},
   133  	}
   134  	externalExpectedEvents := map[string][]string{
   135  		"no-endpoint-external": {"issue_comment"},
   136  		"no-help-external":     {"issue_comment"},
   137  		"helpful-external":     {"pull_request", "issue"},
   138  	}
   139  
   140  	registerNormalPlugins(t, normalExpectedEvents, normalHelp, normalExpectedReposForPlugin)
   141  
   142  	help := NewHelpAgent(fpa, fghc).GeneratePluginHelp()
   143  	if help == nil {
   144  		t.Fatal("NewHelpAgent returned nil HelpAgent struct pointer.")
   145  	}
   146  	if got, expected := sets.New[string](help.AllRepos...), sets.New[string](expectedAllRepos...); !got.Equal(expected) {
   147  		t.Errorf("Expected 'AllRepos' to be %q, but got %q.", sets.List(expected), sets.List(got))
   148  	}
   149  	checkPluginsForRepo := func(expected, got map[string][]string) {
   150  		for _, plugins := range expected {
   151  			sort.Strings(plugins)
   152  		}
   153  		for _, plugins := range got {
   154  			sort.Strings(plugins)
   155  		}
   156  		if !reflect.DeepEqual(expected, got) {
   157  			t.Errorf("Expected repo->plugin map %v, but got %v.", expected, got)
   158  		}
   159  	}
   160  	checkPluginsForRepo(normalExpectedPluginsForRepo, help.RepoPlugins)
   161  	checkPluginsForRepo(externalExpectedPluginsForRepo, help.RepoExternalPlugins)
   162  
   163  	checkPluginHelp := func(plugin string, expected, got pluginhelp.PluginHelp, eventsForPlugin []string) {
   164  		sort.Strings(eventsForPlugin)
   165  		sort.Strings(got.Events)
   166  		if expected, got := eventsForPlugin, got.Events; !reflect.DeepEqual(expected, got) {
   167  			t.Errorf("Expected plugin '%s' to subscribe to events %q, but got %q.", plugin, expected, got)
   168  		}
   169  		// Events field is correct, everything else should match the input exactly.
   170  		got.Events = nil
   171  		if !reflect.DeepEqual(got, expected) {
   172  			t.Errorf("Expected plugin '%s' to have help: %v, but got %v.", plugin, expected, got)
   173  		}
   174  	}
   175  
   176  	for plugin, expected := range normalHelp {
   177  		checkPluginHelp(plugin, expected, help.PluginHelp[plugin], normalExpectedEvents[plugin])
   178  	}
   179  	checkPluginHelp("helpful-external", helpfulExternalHelp, help.ExternalPluginHelp["helpful-external"], externalExpectedEvents["helpful-external"])
   180  }
   181  
   182  func registerNormalPlugins(t *testing.T, pluginsToEvents map[string][]string, pluginHelp map[string]pluginhelp.PluginHelp, expectedRepos map[string][]string) {
   183  	for plugin, events := range pluginsToEvents {
   184  		plugin := plugin
   185  		helpProvider := func(_ *plugins.Configuration, enabledRepos []prowconfig.OrgRepo) (*pluginhelp.PluginHelp, error) {
   186  			if got, expected := sets.New[string](prowconfig.OrgReposToStrings(enabledRepos)...), sets.New[string](expectedRepos[plugin]...); !got.Equal(expected) {
   187  				t.Errorf("Plugin '%s' expected to be enabled on repos %q, but got %q.", plugin, sets.List(expected), sets.List(got))
   188  			}
   189  			help := pluginHelp[plugin]
   190  			return &help, nil
   191  		}
   192  		for _, event := range events {
   193  			switch event {
   194  			case "issue_comment":
   195  				plugins.RegisterIssueCommentHandler(plugin, nil, helpProvider)
   196  			case "issue":
   197  				plugins.RegisterIssueHandler(plugin, nil, helpProvider)
   198  			case "pull_request":
   199  				plugins.RegisterPullRequestHandler(plugin, nil, helpProvider)
   200  			case "pull_request_review":
   201  				plugins.RegisterReviewEventHandler(plugin, nil, helpProvider)
   202  			case "pull_request_review_comment":
   203  				plugins.RegisterReviewCommentEventHandler(plugin, nil, helpProvider)
   204  			default:
   205  				t.Fatalf("Invalid test! Unknown event type '%s' for plugin '%s'.", event, plugin)
   206  			}
   207  		}
   208  	}
   209  }