github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/prstatus/prstatus_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 prstatus
    18  
    19  import (
    20  	"context"
    21  	"encoding/gob"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"reflect"
    26  	"testing"
    27  	"time"
    28  
    29  	"golang.org/x/oauth2"
    30  
    31  	gogithub "github.com/google/go-github/github"
    32  	"github.com/gorilla/sessions"
    33  	"github.com/sirupsen/logrus"
    34  	"sigs.k8s.io/yaml"
    35  
    36  	"k8s.io/test-infra/pkg/ghclient"
    37  	"k8s.io/test-infra/prow/config"
    38  	"k8s.io/test-infra/prow/github"
    39  )
    40  
    41  type MockQueryHandler struct {
    42  	prs        []PullRequest
    43  	contextMap map[int][]Context
    44  }
    45  
    46  func (mh *MockQueryHandler) QueryPullRequests(ctx context.Context, ghc githubClient, query string) ([]PullRequest, error) {
    47  	return mh.prs, nil
    48  }
    49  
    50  func (mh *MockQueryHandler) GetHeadContexts(ghc githubClient, pr PullRequest) ([]Context, error) {
    51  	return mh.contextMap[int(pr.Number)], nil
    52  }
    53  
    54  func (mh *MockQueryHandler) GetUser(*ghclient.Client) (*gogithub.User, error) {
    55  	login := "random_user"
    56  	return &gogithub.User{
    57  		Login: &login,
    58  	}, nil
    59  }
    60  
    61  type fgc struct {
    62  	combinedStatus *github.CombinedStatus
    63  }
    64  
    65  func (c *fgc) Query(context.Context, interface{}, map[string]interface{}) error {
    66  	return nil
    67  }
    68  
    69  func (c *fgc) GetCombinedStatus(org, repo, ref string) (*github.CombinedStatus, error) {
    70  	return c.combinedStatus, nil
    71  }
    72  
    73  func newMockQueryHandler(prs []PullRequest, contextMap map[int][]Context) *MockQueryHandler {
    74  	return &MockQueryHandler{
    75  		prs:        prs,
    76  		contextMap: contextMap,
    77  	}
    78  }
    79  
    80  func createMockAgent(repos []string, config *config.GithubOAuthConfig) *DashboardAgent {
    81  	return &DashboardAgent{
    82  		repos: repos,
    83  		goac:  config,
    84  		log:   logrus.WithField("unit-test", "dashboard-agent"),
    85  	}
    86  }
    87  
    88  func TestHandlePrStatusWithoutLogin(t *testing.T) {
    89  	repos := []string{"mock/repo", "kubernetes/test-infra", "foo/bar"}
    90  	mockCookieStore := sessions.NewCookieStore([]byte("secret-key"))
    91  	mockConfig := &config.GithubOAuthConfig{
    92  		CookieStore: mockCookieStore,
    93  	}
    94  	mockAgent := createMockAgent(repos, mockConfig)
    95  	mockData := UserData{
    96  		Login: false,
    97  	}
    98  
    99  	rr := httptest.NewRecorder()
   100  	request := httptest.NewRequest(http.MethodGet, "/pr-data.js", nil)
   101  
   102  	mockQueryHandler := newMockQueryHandler(nil, nil)
   103  	prHandler := mockAgent.HandlePrStatus(mockQueryHandler)
   104  	prHandler.ServeHTTP(rr, request)
   105  	if rr.Code != http.StatusOK {
   106  		t.Fatalf("Bad status code: %d", rr.Code)
   107  	}
   108  	response := rr.Result()
   109  	defer response.Body.Close()
   110  	body, err := ioutil.ReadAll(response.Body)
   111  	if err != nil {
   112  		t.Fatalf("Error with reading response body: %v", err)
   113  	}
   114  	var dataReturned UserData
   115  	if err := yaml.Unmarshal(body, &dataReturned); err != nil {
   116  		t.Errorf("Error with unmarshaling response: %v", err)
   117  	}
   118  	if !reflect.DeepEqual(dataReturned, mockData) {
   119  		t.Errorf("Invalid user data. Got %v, expected %v", dataReturned, mockData)
   120  	}
   121  }
   122  
   123  func TestHandlePrStatusWithInvalidToken(t *testing.T) {
   124  	logrus.SetLevel(logrus.ErrorLevel)
   125  	repos := []string{"mock/repo", "kubernetes/test-infra", "foo/bar"}
   126  	mockCookieStore := sessions.NewCookieStore([]byte("secret-key"))
   127  	mockConfig := &config.GithubOAuthConfig{
   128  		CookieStore: mockCookieStore,
   129  	}
   130  	mockAgent := createMockAgent(repos, mockConfig)
   131  	mockQueryHandler := newMockQueryHandler([]PullRequest{}, map[int][]Context{})
   132  
   133  	rr := httptest.NewRecorder()
   134  	request := httptest.NewRequest(http.MethodGet, "/pr-data.js", nil)
   135  	request.AddCookie(&http.Cookie{Name: tokenSession, Value: "garbage"})
   136  	prHandler := mockAgent.HandlePrStatus(mockQueryHandler)
   137  	prHandler.ServeHTTP(rr, request)
   138  	if rr.Code != http.StatusOK {
   139  		t.Fatalf("Bad status code: %d", rr.Code)
   140  	}
   141  	response := rr.Result()
   142  	defer response.Body.Close()
   143  
   144  	body, err := ioutil.ReadAll(response.Body)
   145  	if err != nil {
   146  		t.Fatalf("Error with reading response body: %v", err)
   147  	}
   148  
   149  	var dataReturned UserData
   150  	if err := yaml.Unmarshal(body, &dataReturned); err != nil {
   151  		t.Errorf("Error with unmarshaling response: %v", err)
   152  	}
   153  
   154  	expectedData := UserData{Login: false}
   155  	if !reflect.DeepEqual(dataReturned, expectedData) {
   156  		t.Fatalf("Invalid user data. Got %v, expected %v.", dataReturned, expectedData)
   157  	}
   158  }
   159  
   160  func TestHandlePrStatusWithLogin(t *testing.T) {
   161  	repos := []string{"mock/repo", "kubernetes/test-infra", "foo/bar"}
   162  	mockCookieStore := sessions.NewCookieStore([]byte("secret-key"))
   163  	mockConfig := &config.GithubOAuthConfig{
   164  		CookieStore: mockCookieStore,
   165  	}
   166  	mockAgent := createMockAgent(repos, mockConfig)
   167  
   168  	testCases := []struct {
   169  		prs          []PullRequest
   170  		contextMap   map[int][]Context
   171  		expectedData UserData
   172  	}{
   173  		{
   174  			prs:        []PullRequest{},
   175  			contextMap: map[int][]Context{},
   176  			expectedData: UserData{
   177  				Login: true,
   178  			},
   179  		},
   180  		{
   181  			prs: []PullRequest{
   182  				{
   183  					Number: 0,
   184  					Title:  "random pull request",
   185  				},
   186  				{
   187  					Number: 1,
   188  					Title:  "This is a test",
   189  				},
   190  				{
   191  					Number: 2,
   192  					Title:  "test pull request",
   193  				},
   194  			},
   195  			contextMap: map[int][]Context{
   196  				0: {
   197  					{
   198  						Context:     "gofmt-job",
   199  						Description: "job succeed",
   200  						State:       "SUCCESS",
   201  					},
   202  				},
   203  				1: {
   204  					{
   205  						Context:     "verify-bazel-job",
   206  						Description: "job failed",
   207  						State:       "FAILURE",
   208  					},
   209  				},
   210  				2: {
   211  					{
   212  						Context:     "gofmt-job",
   213  						Description: "job succeed",
   214  						State:       "SUCCESS",
   215  					},
   216  					{
   217  						Context:     "verify-bazel-job",
   218  						Description: "job failed",
   219  						State:       "FAILURE",
   220  					},
   221  				},
   222  			},
   223  			expectedData: UserData{
   224  				Login: true,
   225  				PullRequestsWithContexts: []PullRequestWithContexts{
   226  					{
   227  						PullRequest: PullRequest{
   228  							Number: 0,
   229  							Title:  "random pull request",
   230  						},
   231  						Contexts: []Context{
   232  							{
   233  								Context:     "gofmt-job",
   234  								Description: "job succeed",
   235  								State:       "SUCCESS",
   236  							},
   237  						},
   238  					},
   239  					{
   240  						PullRequest: PullRequest{
   241  							Number: 1,
   242  							Title:  "This is a test",
   243  						},
   244  						Contexts: []Context{
   245  							{
   246  								Context:     "verify-bazel-job",
   247  								Description: "job failed",
   248  								State:       "FAILURE",
   249  							},
   250  						},
   251  					},
   252  					{
   253  						PullRequest: PullRequest{
   254  							Number: 2,
   255  							Title:  "test pull request",
   256  						},
   257  						Contexts: []Context{
   258  							{
   259  								Context:     "gofmt-job",
   260  								Description: "job succeed",
   261  								State:       "SUCCESS",
   262  							},
   263  							{
   264  								Context:     "verify-bazel-job",
   265  								Description: "job failed",
   266  								State:       "FAILURE",
   267  							},
   268  						},
   269  					},
   270  				},
   271  			},
   272  		},
   273  	}
   274  	for id, testcase := range testCases {
   275  		t.Logf("Test %d:", id)
   276  		rr := httptest.NewRecorder()
   277  		request := httptest.NewRequest(http.MethodGet, "/pr-data.js", nil)
   278  		mockSession, err := sessions.GetRegistry(request).Get(mockCookieStore, tokenSession)
   279  		if err != nil {
   280  			t.Errorf("Error with creating mock session: %v", err)
   281  		}
   282  		gob.Register(oauth2.Token{})
   283  		token := &oauth2.Token{AccessToken: "secret-token", Expiry: time.Now().Add(time.Duration(24*365) * time.Hour)}
   284  		mockSession.Values[tokenKey] = token
   285  		mockSession.Values[loginKey] = "random_user"
   286  		mockQueryHandler := newMockQueryHandler(testcase.prs, testcase.contextMap)
   287  		prHandler := mockAgent.HandlePrStatus(mockQueryHandler)
   288  		prHandler.ServeHTTP(rr, request)
   289  		if rr.Code != http.StatusOK {
   290  			t.Fatalf("Bad status code: %d", rr.Code)
   291  		}
   292  		response := rr.Result()
   293  		body, err := ioutil.ReadAll(response.Body)
   294  		if err != nil {
   295  			t.Fatalf("Error with reading response body: %v", err)
   296  		}
   297  		var dataReturned UserData
   298  		if err := yaml.Unmarshal(body, &dataReturned); err != nil {
   299  			t.Errorf("Error with unmarshaling response: %v", err)
   300  		}
   301  		if !reflect.DeepEqual(dataReturned, testcase.expectedData) {
   302  			t.Fatalf("Invalid user data. Got %v, expected %v.", dataReturned, testcase.expectedData)
   303  		}
   304  		t.Logf("Passed")
   305  		response.Body.Close()
   306  	}
   307  }
   308  
   309  func TestGetHeadContexts(t *testing.T) {
   310  	repos := []string{"mock/repo", "kubernetes/test-infra", "foo/bar"}
   311  	mockCookieStore := sessions.NewCookieStore([]byte("secret-key"))
   312  	mockConfig := &config.GithubOAuthConfig{
   313  		CookieStore: mockCookieStore,
   314  	}
   315  	mockAgent := createMockAgent(repos, mockConfig)
   316  	testCases := []struct {
   317  		combinedStatus   *github.CombinedStatus
   318  		pr               PullRequest
   319  		expectedContexts []Context
   320  	}{
   321  		{
   322  			combinedStatus:   &github.CombinedStatus{},
   323  			pr:               PullRequest{},
   324  			expectedContexts: []Context{},
   325  		},
   326  		{
   327  			combinedStatus: &github.CombinedStatus{
   328  				Statuses: []github.Status{
   329  					{
   330  						State:       "FAILURE",
   331  						Description: "job failed",
   332  						Context:     "gofmt-job",
   333  					},
   334  					{
   335  						State:       "SUCCESS",
   336  						Description: "job succeed",
   337  						Context:     "k8s-job",
   338  					},
   339  					{
   340  						State:       "PENDING",
   341  						Description: "triggered",
   342  						Context:     "test-job",
   343  					},
   344  				},
   345  			},
   346  			pr: PullRequest{},
   347  			expectedContexts: []Context{
   348  				{
   349  					Context:     "gofmt-job",
   350  					Description: "job failed",
   351  					State:       "FAILURE",
   352  				},
   353  				{
   354  					State:       "SUCCESS",
   355  					Description: "job succeed",
   356  					Context:     "k8s-job",
   357  				},
   358  				{
   359  					State:       "PENDING",
   360  					Description: "triggered",
   361  					Context:     "test-job",
   362  				},
   363  			},
   364  		},
   365  	}
   366  	for id, testcase := range testCases {
   367  		t.Logf("Test %d:", id)
   368  		contexts, err := mockAgent.GetHeadContexts(&fgc{
   369  			combinedStatus: testcase.combinedStatus,
   370  		}, testcase.pr)
   371  		if err != nil {
   372  			t.Fatalf("Error with getting head contexts")
   373  		}
   374  		if !reflect.DeepEqual(contexts, testcase.expectedContexts) {
   375  			t.Fatalf("Invalid user data. Got %v, expected %v.", contexts, testcase.expectedContexts)
   376  		}
   377  		t.Logf("Passed")
   378  	}
   379  }
   380  
   381  func TestConstructSearchQuery(t *testing.T) {
   382  	repos := []string{"mock/repo", "kubernetes/test-infra", "foo/bar"}
   383  	mockCookieStore := sessions.NewCookieStore([]byte("secret-key"))
   384  	mockConfig := &config.GithubOAuthConfig{
   385  		CookieStore: mockCookieStore,
   386  	}
   387  	mockAgent := createMockAgent(repos, mockConfig)
   388  	query := mockAgent.ConstructSearchQuery("random_username")
   389  	mockQuery := "is:pr state:open author:random_username repo:\"mock/repo\" repo:\"kubernetes/test-infra\" repo:\"foo/bar\""
   390  	if query != mockQuery {
   391  		t.Errorf("Invalid query. Got: %v, expected %v", query, mockQuery)
   392  	}
   393  }