github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/cmd/deck/main_test.go (about)

     1  /*
     2  Copyright 2016 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  	"bufio"
    21  	"bytes"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"net/http"
    28  	"net/http/httptest"
    29  	"net/url"
    30  	"reflect"
    31  	"strconv"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/ghodss/yaml"
    36  
    37  	"k8s.io/test-infra/prow/config"
    38  	"k8s.io/test-infra/prow/kube"
    39  	"k8s.io/test-infra/prow/pluginhelp"
    40  	"k8s.io/test-infra/prow/tide"
    41  )
    42  
    43  func TestOptions_Validate(t *testing.T) {
    44  	var testCases = []struct {
    45  		name        string
    46  		input       options
    47  		expectedErr bool
    48  	}{
    49  		{
    50  			name: "minimal set ok",
    51  			input: options{
    52  				configPath: "test",
    53  			},
    54  			expectedErr: false,
    55  		},
    56  		{
    57  			name:        "missing configpath",
    58  			input:       options{},
    59  			expectedErr: true,
    60  		},
    61  		{
    62  			name: "ok with oauth",
    63  			input: options{
    64  				configPath:            "test",
    65  				oauthURL:              "website",
    66  				githubOAuthConfigFile: "something",
    67  				cookieSecretFile:      "yum",
    68  			},
    69  			expectedErr: false,
    70  		},
    71  		{
    72  			name: "missing github config with oauth",
    73  			input: options{
    74  				configPath:       "test",
    75  				oauthURL:         "website",
    76  				cookieSecretFile: "yum",
    77  			},
    78  			expectedErr: true,
    79  		},
    80  		{
    81  			name: "missing cookie with oauth",
    82  			input: options{
    83  				configPath:            "test",
    84  				oauthURL:              "website",
    85  				githubOAuthConfigFile: "something",
    86  			},
    87  			expectedErr: true,
    88  		},
    89  	}
    90  
    91  	for _, testCase := range testCases {
    92  		err := testCase.input.Validate()
    93  		if testCase.expectedErr && err == nil {
    94  			t.Errorf("%s: expected an error but got none", testCase.name)
    95  		}
    96  		if !testCase.expectedErr && err != nil {
    97  			t.Errorf("%s: expected no error but got one: %v", testCase.name, err)
    98  		}
    99  	}
   100  }
   101  
   102  type flc int
   103  
   104  func (f flc) GetJobLog(job, id string) ([]byte, error) {
   105  	if job == "job" && id == "123" {
   106  		return []byte("hello"), nil
   107  	}
   108  	return nil, errors.New("muahaha")
   109  }
   110  
   111  func TestHandleLog(t *testing.T) {
   112  	var testcases = []struct {
   113  		name string
   114  		path string
   115  		code int
   116  	}{
   117  		{
   118  			name: "no job name",
   119  			path: "",
   120  			code: http.StatusBadRequest,
   121  		},
   122  		{
   123  			name: "job but no id",
   124  			path: "?job=job",
   125  			code: http.StatusBadRequest,
   126  		},
   127  		{
   128  			name: "id but no job",
   129  			path: "?id=123",
   130  			code: http.StatusBadRequest,
   131  		},
   132  		{
   133  			name: "id and job, found",
   134  			path: "?job=job&id=123",
   135  			code: http.StatusOK,
   136  		},
   137  		{
   138  			name: "id and job, not found",
   139  			path: "?job=ohno&id=123",
   140  			code: http.StatusNotFound,
   141  		},
   142  	}
   143  	handler := handleLog(flc(0))
   144  	for _, tc := range testcases {
   145  		req, err := http.NewRequest(http.MethodGet, "", nil)
   146  		if err != nil {
   147  			t.Fatalf("Error making request: %v", err)
   148  		}
   149  		u, err := url.Parse(tc.path)
   150  		if err != nil {
   151  			t.Fatalf("Error parsing URL: %v", err)
   152  		}
   153  		var follow = false
   154  		if ok, _ := strconv.ParseBool(u.Query().Get("follow")); ok {
   155  			follow = true
   156  		}
   157  		req.URL = u
   158  		rr := httptest.NewRecorder()
   159  		handler.ServeHTTP(rr, req)
   160  		if rr.Code != tc.code {
   161  			t.Errorf("Wrong error code. Got %v, want %v", rr.Code, tc.code)
   162  		} else if rr.Code == http.StatusOK {
   163  			if follow {
   164  				//wait a little to get the chunks
   165  				time.Sleep(2 * time.Millisecond)
   166  				reader := bufio.NewReader(rr.Body)
   167  				var buf bytes.Buffer
   168  				for {
   169  					line, err := reader.ReadBytes('\n')
   170  					if err == io.EOF {
   171  						break
   172  					}
   173  					if err != nil {
   174  						t.Fatalf("Expecting reply with content but got error: %v", err)
   175  					}
   176  					buf.Write(line)
   177  				}
   178  				if !bytes.Contains(buf.Bytes(), []byte("hello")) {
   179  					t.Errorf("Unexpected body: got %s.", buf.String())
   180  				}
   181  			} else {
   182  				resp := rr.Result()
   183  				defer resp.Body.Close()
   184  				if body, err := ioutil.ReadAll(resp.Body); err != nil {
   185  					t.Errorf("Error reading response body: %v", err)
   186  				} else if string(body) != "hello" {
   187  					t.Errorf("Unexpected body: got %s.", string(body))
   188  				}
   189  			}
   190  		}
   191  	}
   192  }
   193  
   194  type fpjc kube.ProwJob
   195  
   196  func (fc *fpjc) GetProwJob(name string) (kube.ProwJob, error) {
   197  	return kube.ProwJob(*fc), nil
   198  }
   199  
   200  // TestRerun just checks that the result can be unmarshaled properly, has an
   201  // updated status, and has equal spec.
   202  func TestRerun(t *testing.T) {
   203  	fc := fpjc(kube.ProwJob{
   204  		Spec: kube.ProwJobSpec{
   205  			Job:  "whoa",
   206  			Type: kube.PresubmitJob,
   207  			Refs: &kube.Refs{
   208  				Org:  "org",
   209  				Repo: "repo",
   210  				Pulls: []kube.Pull{
   211  					{Number: 1},
   212  				},
   213  			},
   214  		},
   215  		Status: kube.ProwJobStatus{
   216  			State: kube.PendingState,
   217  		},
   218  	})
   219  	handler := handleRerun(&fc)
   220  	req, err := http.NewRequest(http.MethodGet, "/rerun?prowjob=wowsuch", nil)
   221  	if err != nil {
   222  		t.Fatalf("Error making request: %v", err)
   223  	}
   224  	rr := httptest.NewRecorder()
   225  	handler.ServeHTTP(rr, req)
   226  	if rr.Code != http.StatusOK {
   227  		t.Fatalf("Bad error code: %d", rr.Code)
   228  	}
   229  	resp := rr.Result()
   230  	defer resp.Body.Close()
   231  	body, err := ioutil.ReadAll(resp.Body)
   232  	if err != nil {
   233  		t.Fatalf("Error reading response body: %v", err)
   234  	}
   235  	var res kube.ProwJob
   236  	if err := yaml.Unmarshal(body, &res); err != nil {
   237  		t.Fatalf("Error unmarshaling: %v", err)
   238  	}
   239  	if res.Spec.Job != "whoa" {
   240  		t.Errorf("Wrong job, expected \"whoa\", got \"%s\"", res.Spec.Job)
   241  	}
   242  	if res.Status.State != kube.TriggeredState {
   243  		t.Errorf("Wrong state, expected \"%v\", got \"%v\"", kube.TriggeredState, res.Status.State)
   244  	}
   245  }
   246  
   247  func TestTide(t *testing.T) {
   248  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   249  		pools := []tide.Pool{
   250  			{
   251  				Org: "o",
   252  			},
   253  		}
   254  		b, err := json.Marshal(pools)
   255  		if err != nil {
   256  			t.Fatalf("Marshaling: %v", err)
   257  		}
   258  		fmt.Fprintf(w, string(b))
   259  	}))
   260  	ca := &config.Agent{}
   261  	ca.Set(&config.Config{
   262  		ProwConfig: config.ProwConfig{
   263  			Tide: config.Tide{
   264  				Queries: []config.TideQuery{
   265  					{Repos: []string{"kubernetes/test-infra"}},
   266  				},
   267  			},
   268  		},
   269  	})
   270  	ta := tideAgent{
   271  		path:         s.URL,
   272  		updatePeriod: func() time.Duration { return time.Minute },
   273  	}
   274  	if err := ta.update(); err != nil {
   275  		t.Fatalf("Updating: %v", err)
   276  	}
   277  	if len(ta.pools) != 1 {
   278  		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(ta.pools), ta.pools)
   279  	}
   280  	if ta.pools[0].Org != "o" {
   281  		t.Errorf("Wrong org in pool. Got %s, expected o in %v", ta.pools[0].Org, ta.pools)
   282  	}
   283  	handler := handleTide(ca, &ta)
   284  	req, err := http.NewRequest(http.MethodGet, "/tide.js", nil)
   285  	if err != nil {
   286  		t.Fatalf("Error making request: %v", err)
   287  	}
   288  	rr := httptest.NewRecorder()
   289  	handler.ServeHTTP(rr, req)
   290  	if rr.Code != http.StatusOK {
   291  		t.Fatalf("Bad error code: %d", rr.Code)
   292  	}
   293  	resp := rr.Result()
   294  	defer resp.Body.Close()
   295  	body, err := ioutil.ReadAll(resp.Body)
   296  	if err != nil {
   297  		t.Fatalf("Error reading response body: %v", err)
   298  	}
   299  	res := tideData{}
   300  	if err := yaml.Unmarshal(body, &res); err != nil {
   301  		t.Fatalf("Error unmarshaling: %v", err)
   302  	}
   303  	if len(res.Pools) != 1 {
   304  		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Pools), res.Pools)
   305  	}
   306  	if res.Pools[0].Org != "o" {
   307  		t.Errorf("Wrong org in pool. Got %s, expected o in %v", res.Pools[0].Org, res.Pools)
   308  	}
   309  	if len(res.Queries) != 1 {
   310  		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Queries), res.Queries)
   311  	}
   312  	if expected := "is:pr state:open repo:\"kubernetes/test-infra\""; res.Queries[0] != expected {
   313  		t.Errorf("Wrong query. Got %s, expected %s", res.Queries[0], expected)
   314  	}
   315  }
   316  
   317  func TestHelp(t *testing.T) {
   318  	hitCount := 0
   319  	help := pluginhelp.Help{
   320  		AllRepos:            []string{"org/repo"},
   321  		RepoPlugins:         map[string][]string{"org": {"plugin"}},
   322  		RepoExternalPlugins: map[string][]string{"org/repo": {"external-plugin"}},
   323  		PluginHelp:          map[string]pluginhelp.PluginHelp{"plugin": {Description: "plugin"}},
   324  		ExternalPluginHelp:  map[string]pluginhelp.PluginHelp{"external-plugin": {Description: "external-plugin"}},
   325  	}
   326  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   327  		hitCount++
   328  		b, err := json.Marshal(help)
   329  		if err != nil {
   330  			t.Fatalf("Marshaling: %v", err)
   331  		}
   332  		fmt.Fprintf(w, string(b))
   333  	}))
   334  	ha := &helpAgent{
   335  		path: s.URL,
   336  	}
   337  	handler := handlePluginHelp(ha)
   338  	handleAndCheck := func() {
   339  		req, err := http.NewRequest(http.MethodGet, "/plugin-help.js", nil)
   340  		if err != nil {
   341  			t.Fatalf("Error making request: %v", err)
   342  		}
   343  		rr := httptest.NewRecorder()
   344  		handler.ServeHTTP(rr, req)
   345  		if rr.Code != http.StatusOK {
   346  			t.Fatalf("Bad error code: %d", rr.Code)
   347  		}
   348  		resp := rr.Result()
   349  		defer resp.Body.Close()
   350  		body, err := ioutil.ReadAll(resp.Body)
   351  		if err != nil {
   352  			t.Fatalf("Error reading response body: %v", err)
   353  		}
   354  		var res pluginhelp.Help
   355  		if err := yaml.Unmarshal(body, &res); err != nil {
   356  			t.Fatalf("Error unmarshaling: %v", err)
   357  		}
   358  		if !reflect.DeepEqual(help, res) {
   359  			t.Errorf("Invalid plugin help. Got %v, expected %v", res, help)
   360  		}
   361  		if hitCount != 1 {
   362  			t.Errorf("Expected fake hook endpoint to be hit once, but endpoint was hit %d times.", hitCount)
   363  		}
   364  	}
   365  	handleAndCheck()
   366  	handleAndCheck()
   367  }