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 }