github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/deck/pr_history_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 main
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  	"k8s.io/test-infra/prow/config"
    28  	"k8s.io/test-infra/prow/kube"
    29  )
    30  
    31  type fakeBucket struct {
    32  	name    string
    33  	objects map[string]string
    34  }
    35  
    36  func (bucket fakeBucket) getName() string {
    37  	return bucket.name
    38  }
    39  
    40  func (bucket fakeBucket) listSubDirs(prefix string) ([]string, error) {
    41  	dirs := sets.String{}
    42  	for k := range bucket.objects {
    43  		if !strings.HasPrefix(k, prefix) {
    44  			continue
    45  		}
    46  		suffix := strings.TrimPrefix(k, prefix)
    47  		dir := strings.Split(suffix, "/")[0]
    48  		dirs.Insert(dir)
    49  	}
    50  	return dirs.List(), nil
    51  }
    52  
    53  func (bucket fakeBucket) listAll(prefix string) ([]string, error) {
    54  	keys := []string{}
    55  	for k := range bucket.objects {
    56  		if !strings.HasPrefix(k, prefix) {
    57  			continue
    58  		}
    59  		keys = append(keys, k)
    60  	}
    61  	return keys, nil
    62  }
    63  
    64  func (bucket fakeBucket) readObject(key string) ([]byte, error) {
    65  	if obj, ok := bucket.objects[key]; ok {
    66  		return []byte(obj), nil
    67  	}
    68  	return []byte{}, fmt.Errorf("object %s not found", key)
    69  }
    70  
    71  func TestUpdateCommitData(t *testing.T) {
    72  	cases := []struct {
    73  		name      string
    74  		hash      string
    75  		buildTime time.Time
    76  		width     int
    77  		before    map[string]*commitData
    78  		after     map[string]*commitData
    79  	}{
    80  		{
    81  			name:      "new commit",
    82  			hash:      "d0c3cd182cffb3e722b14322fd1ca854a8bf62b0",
    83  			width:     1,
    84  			buildTime: time.Unix(1543534799, 0),
    85  			before:    make(map[string]*commitData),
    86  			after: map[string]*commitData{
    87  				"d0c3cd182cffb3e722b14322fd1ca854a8bf62b0": {
    88  					HashPrefix: "d0c3cd1",
    89  					MaxWidth:   1,
    90  					Link:       "https://github.com/kubernetes/test-infra/commit/d0c3cd182cffb3e722b14322fd1ca854a8bf62b0",
    91  					latest:     time.Unix(1543534799, 0),
    92  				},
    93  			},
    94  		},
    95  		{
    96  			name:      "update existing commit",
    97  			hash:      "d0c3cd182cffb3e722b14322fd1ca854a8bf62b0",
    98  			width:     3,
    99  			buildTime: time.Unix(320630400, 0),
   100  			before: map[string]*commitData{
   101  				"d0c3cd182cffb3e722b14322fd1ca854a8bf62b0": {
   102  					HashPrefix: "d0c3cd1",
   103  					MaxWidth:   5,
   104  					Link:       "https://github.com/kubernetes/test-infra/commit/d0c3cd182cffb3e722b14322fd1ca854a8bf62b0",
   105  					latest:     time.Unix(0, 0),
   106  				},
   107  			},
   108  			after: map[string]*commitData{
   109  				"d0c3cd182cffb3e722b14322fd1ca854a8bf62b0": {
   110  					HashPrefix: "d0c3cd1",
   111  					MaxWidth:   5,
   112  					Link:       "https://github.com/kubernetes/test-infra/commit/d0c3cd182cffb3e722b14322fd1ca854a8bf62b0",
   113  					latest:     time.Unix(320630400, 0),
   114  				},
   115  			},
   116  		},
   117  		{
   118  			name:   "unknown commit has no link",
   119  			hash:   "unknown",
   120  			width:  1,
   121  			before: make(map[string]*commitData),
   122  			after: map[string]*commitData{
   123  				"unknown": {
   124  					HashPrefix: "unknown",
   125  					MaxWidth:   1,
   126  					Link:       "",
   127  				},
   128  			},
   129  		},
   130  	}
   131  	org := "kubernetes"
   132  	repo := "test-infra"
   133  	for _, tc := range cases {
   134  		updateCommitData(tc.before, org, repo, tc.hash, tc.buildTime, tc.width)
   135  		for hash, expCommit := range tc.after {
   136  			if commit, ok := tc.before[hash]; ok {
   137  				if commit.HashPrefix != expCommit.HashPrefix {
   138  					t.Errorf("%s: expected commit hash prefix to be %s, got %s", tc.name, expCommit.HashPrefix, commit.HashPrefix)
   139  				}
   140  				if commit.Link != expCommit.Link {
   141  					t.Errorf("%s: expected commit link to be %s, got %s", tc.name, expCommit.Link, commit.Link)
   142  				}
   143  				if commit.MaxWidth != expCommit.MaxWidth {
   144  					t.Errorf("%s: expected commit width to be %d, got %d", tc.name, expCommit.MaxWidth, commit.MaxWidth)
   145  				}
   146  				if commit.latest != expCommit.latest {
   147  					t.Errorf("%s: expected commit time to be %v, got %v", tc.name, expCommit.latest, commit.latest)
   148  				}
   149  			} else {
   150  				t.Errorf("%s: expected commit %s not found", tc.name, hash)
   151  			}
   152  		}
   153  	}
   154  }
   155  
   156  func TestParsePullKey(t *testing.T) {
   157  	cases := []struct {
   158  		name   string
   159  		key    string
   160  		org    string
   161  		repo   string
   162  		pr     int
   163  		expErr bool
   164  	}{
   165  		{
   166  			name: "all good",
   167  			key:  "kubernetes/test-infra/10169",
   168  			org:  "kubernetes",
   169  			repo: "test-infra",
   170  			pr:   10169,
   171  		},
   172  		{
   173  			name:   "3rd field needs to be PR number",
   174  			key:    "kubernetes/test-infra/alpha",
   175  			expErr: true,
   176  		},
   177  		{
   178  			name:   "not enough parts",
   179  			key:    "kubernetes/10169",
   180  			expErr: true,
   181  		},
   182  	}
   183  	for _, tc := range cases {
   184  		org, repo, pr, err := parsePullKey(tc.key)
   185  		if (err != nil) != tc.expErr {
   186  			t.Errorf("%s: unexpected error %v", tc.name, err)
   187  		}
   188  		if org != tc.org || repo != tc.repo || pr != tc.pr {
   189  			t.Errorf("%s: expected %s, %s, %d; got %s, %s, %d", tc.name, tc.org, tc.repo, tc.pr, org, repo, pr)
   190  		}
   191  	}
   192  }
   193  
   194  var testBucket = fakeBucket{
   195  	name: "chum-bucket",
   196  	objects: map[string]string{
   197  		"pr-logs/pull/123/build-snowman/456/started.json": `{
   198  			"timestamp": 55555,
   199  			"pull": "master:d0c3cd182cffb3e722b14322fd1ca854a8bf62b0,69848:1244ee66517bbe603d899bbd24458ebc0e185fd9"
   200  		}`,
   201  		"pr-logs/pull/123/build-snowman/456/finished.json": `{
   202  			"timestamp": 66666,
   203  			"result":    "SUCCESS"
   204  		}`,
   205  		"pr-logs/pull/123/build-snowman/789/started.json": `{
   206  			"timestamp": 98765,
   207  			"pull": "master:d0c3cd182cffb3e722b14322fd1ca854a8bf62b0,69848:bbdebedaf24c03f9e2eeb88e8ea4bb10c9e1fbfc"
   208  		}`,
   209  		"pr-logs/pull/765/eat-bread/999/started.json": `{
   210  			"timestamp": 12345,
   211  			"pull": "not-master:21ebe05079a1aeb5f6dae23a2d8c106b4af8c363,12345:52252bcc81712c96940fca1d3c913dd76af3d2a2"
   212  		}`,
   213  	},
   214  }
   215  
   216  func TestListJobBuilds(t *testing.T) {
   217  	jobPrefixes := []string{"pr-logs/pull/123/build-snowman/", "pr-logs/pull/765/eat-bread/"}
   218  	expected := map[string]sets.String{
   219  		"build-snowman": {"456": {}, "789": {}},
   220  		"eat-bread":     {"999": {}},
   221  	}
   222  	jobs := listJobBuilds(testBucket, jobPrefixes)
   223  	if len(jobs) != len(expected) {
   224  		t.Errorf("expected %d jobs, got %d", len(expected), len(jobs))
   225  	}
   226  	for _, job := range jobs {
   227  		if expBuilds, ok := expected[job.name]; ok {
   228  			if len(job.buildPrefixes) != len(expBuilds) {
   229  				t.Errorf("expected %d builds for %q, found %d", len(expBuilds), job.name, len(job.buildPrefixes))
   230  			}
   231  			for _, build := range job.buildPrefixes {
   232  				if !expBuilds.Has(build) {
   233  					t.Errorf("found unexpected build for %q: %q", job.name, build)
   234  				}
   235  			}
   236  		} else {
   237  			t.Errorf("found unexpected job %q", job.name)
   238  		}
   239  	}
   240  }
   241  
   242  func TestGetPRBuildData(t *testing.T) {
   243  	jobs := []jobBuilds{
   244  		{
   245  			name: "build-snowman",
   246  			buildPrefixes: []string{
   247  				"pr-logs/pull/123/build-snowman/456",
   248  				"pr-logs/pull/123/build-snowman/789",
   249  			},
   250  		},
   251  		{
   252  			name: "eat-bread",
   253  			buildPrefixes: []string{
   254  				"pr-logs/pull/765/eat-bread/999",
   255  			},
   256  		},
   257  	}
   258  	expected := map[string]buildData{
   259  		"pr-logs/pull/123/build-snowman/456": {
   260  			prefix:       "pr-logs/pull/123/build-snowman/456",
   261  			jobName:      "build-snowman",
   262  			index:        0,
   263  			ID:           "456",
   264  			SpyglassLink: "/view/gcs/chum-bucket/pr-logs/pull/123/build-snowman/456",
   265  			Started:      time.Unix(55555, 0),
   266  			Duration:     time.Unix(66666, 0).Sub(time.Unix(55555, 0)),
   267  			Result:       "SUCCESS",
   268  			commitHash:   "1244ee66517bbe603d899bbd24458ebc0e185fd9",
   269  		},
   270  		"pr-logs/pull/123/build-snowman/789": {
   271  			prefix:       "pr-logs/pull/123/build-snowman/789",
   272  			jobName:      "build-snowman",
   273  			index:        1,
   274  			ID:           "789",
   275  			SpyglassLink: "/view/gcs/chum-bucket/pr-logs/pull/123/build-snowman/789",
   276  			Started:      time.Unix(98765, 0),
   277  			Result:       "Unknown",
   278  			commitHash:   "bbdebedaf24c03f9e2eeb88e8ea4bb10c9e1fbfc",
   279  		},
   280  		"pr-logs/pull/765/eat-bread/999": {
   281  			prefix:       "pr-logs/pull/765/eat-bread/999",
   282  			jobName:      "eat-bread",
   283  			index:        0,
   284  			ID:           "999",
   285  			SpyglassLink: "/view/gcs/chum-bucket/pr-logs/pull/765/eat-bread/999",
   286  			Started:      time.Unix(12345, 0),
   287  			Result:       "Unknown",
   288  			commitHash:   "52252bcc81712c96940fca1d3c913dd76af3d2a2",
   289  		},
   290  	}
   291  	builds := getPRBuildData(testBucket, jobs)
   292  	if len(builds) != len(expected) {
   293  		t.Errorf("expected %d builds, found %d", len(expected), len(builds))
   294  	}
   295  	for _, build := range builds {
   296  		if expBuild, ok := expected[build.prefix]; ok {
   297  			if !reflect.DeepEqual(build, expBuild) {
   298  				t.Errorf("build %s\n"+
   299  					"expected: %v\n"+
   300  					"got:      %v", build.prefix, expBuild, build)
   301  			}
   302  		} else {
   303  			t.Errorf("found unexpected build %s", build.prefix)
   304  		}
   305  	}
   306  }
   307  
   308  func TestGetGCSDirsForPR(t *testing.T) {
   309  	cases := []struct {
   310  		name     string
   311  		expected map[string][]string
   312  		config   *config.Config
   313  		org      string
   314  		repo     string
   315  		pr       int
   316  		expErr   bool
   317  	}{
   318  		{
   319  			name:   "no presubmits",
   320  			org:    "kubernetes",
   321  			repo:   "fizzbuzz",
   322  			pr:     123,
   323  			config: &config.Config{},
   324  			expErr: true,
   325  		},
   326  		{
   327  			name: "multiple buckets",
   328  			expected: map[string][]string{
   329  				"chum-bucket": {
   330  					"pr-logs/pull/prow/123/",
   331  				},
   332  				"krusty-krab": {
   333  					"pr-logs/pull/prow/123/",
   334  				},
   335  			},
   336  			org:  "kubernetes",
   337  			repo: "prow", // someday
   338  			pr:   123,
   339  			config: &config.Config{
   340  				ProwConfig: config.ProwConfig{
   341  					Plank: config.Plank{
   342  						DefaultDecorationConfig: &kube.DecorationConfig{
   343  							GCSConfiguration: &kube.GCSConfiguration{
   344  								Bucket:       "krusty-krab",
   345  								PathStrategy: "legacy",
   346  								DefaultOrg:   "kubernetes",
   347  								DefaultRepo:  "kubernetes",
   348  							},
   349  						},
   350  					},
   351  				},
   352  				JobConfig: config.JobConfig{
   353  					Presubmits: map[string][]config.Presubmit{
   354  						"kubernetes/prow": {
   355  							{
   356  								JobBase: config.JobBase{
   357  									Name: "fum-is-chum",
   358  									UtilityConfig: config.UtilityConfig{
   359  										DecorationConfig: &kube.DecorationConfig{
   360  											GCSConfiguration: &kube.GCSConfiguration{
   361  												Bucket:       "chum-bucket",
   362  												PathStrategy: "legacy",
   363  												DefaultOrg:   "kubernetes",
   364  												DefaultRepo:  "kubernetes",
   365  											},
   366  										},
   367  									},
   368  								},
   369  							},
   370  							{
   371  								JobBase: config.JobBase{
   372  									Name: "protect-formula",
   373  									// undecorated
   374  								},
   375  							},
   376  						},
   377  					},
   378  				},
   379  			},
   380  		},
   381  	}
   382  	for _, tc := range cases {
   383  		toSearch, err := getGCSDirsForPR(tc.config, tc.org, tc.repo, tc.pr)
   384  		fmt.Println(toSearch)
   385  		if (err != nil) != tc.expErr {
   386  			t.Errorf("%s: unexpected error %v", tc.name, err)
   387  		}
   388  		for bucket, expDirs := range tc.expected {
   389  			if dirs, ok := toSearch[bucket]; ok {
   390  				if len(dirs) != len(expDirs) {
   391  					t.Errorf("expected to find %d dirs in bucket %s, found %d", len(expDirs), bucket, len(dirs))
   392  				}
   393  				for _, expDir := range tc.expected[bucket] {
   394  					if !dirs.Has(expDir) {
   395  						t.Errorf("couldn't find expected dir %s in bucket %s", expDir, bucket)
   396  					}
   397  				}
   398  			} else {
   399  				t.Errorf("expected to find %d dirs in bucket %s, found none", len(expDirs), bucket)
   400  			}
   401  		}
   402  	}
   403  }