go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/led/job/edit_test.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package job 16 17 import ( 18 "testing" 19 20 . "github.com/smartystreets/goconvey/convey" 21 . "go.chromium.org/luci/common/testing/assertions" 22 23 "go.chromium.org/luci/buildbucket" 24 bbpb "go.chromium.org/luci/buildbucket/proto" 25 swarmingpb "go.chromium.org/luci/swarming/proto/api_v2" 26 ) 27 28 func TestClearCurrentIsolated(t *testing.T) { 29 t.Parallel() 30 31 runCases(t, `ClearCurrentIsolated`, []testCase{ 32 { 33 name: "basic with rbe-cas input", 34 fn: func(jd *Definition) { 35 if jd.GetSwarming() != nil { 36 jd.GetSwarming().CasUserPayload = &swarmingpb.CASReference{ 37 CasInstance: "instance", 38 Digest: &swarmingpb.Digest{ 39 Hash: "hash", 40 SizeBytes: 1, 41 }, 42 } 43 } 44 for _, slc := range jd.GetSwarming().GetTask().GetTaskSlices() { 45 if slc.Properties == nil { 46 slc.Properties = &swarmingpb.TaskProperties{} 47 } 48 slc.Properties.CasInputRoot = &swarmingpb.CASReference{ 49 CasInstance: "instance", 50 Digest: &swarmingpb.Digest{ 51 Hash: "hash", 52 SizeBytes: 1, 53 }, 54 } 55 } 56 SoEdit(jd, func(je Editor) { 57 je.ClearCurrentIsolated() 58 }) 59 60 for _, slc := range jd.GetSwarming().GetTask().GetTaskSlices() { 61 So(slc.Properties.CasInputRoot, ShouldBeNil) 62 } 63 64 iso, err := jd.Info().CurrentIsolated() 65 So(err, ShouldBeNil) 66 So(iso, ShouldBeNil) 67 }, 68 }, 69 }) 70 } 71 72 func TestEnv(t *testing.T) { 73 t.Parallel() 74 75 runCases(t, `Env`, []testCase{ 76 { 77 name: "empty", 78 fn: func(jd *Definition) { 79 SoEdit(jd, func(je Editor) { 80 je.Env(nil) 81 }) 82 So(must(jd.Info().Env()), ShouldBeEmpty) 83 }, 84 }, 85 86 { 87 name: "new", 88 skipSWEmpty: true, 89 fn: func(jd *Definition) { 90 SoEdit(jd, func(je Editor) { 91 je.Env(map[string]string{ 92 "KEY": "VALUE", 93 "DEL": "", // noop 94 }) 95 }) 96 So(must(jd.Info().Env()), ShouldResemble, map[string]string{ 97 "KEY": "VALUE", 98 }) 99 }, 100 }, 101 102 { 103 name: "add", 104 skipSWEmpty: true, 105 fn: func(jd *Definition) { 106 SoEdit(jd, func(je Editor) { 107 je.Env(map[string]string{ 108 "KEY": "VALUE", 109 }) 110 }) 111 SoEdit(jd, func(je Editor) { 112 je.Env(map[string]string{ 113 "OTHER": "NEW_VAL", 114 "DEL": "", // noop 115 }) 116 }) 117 So(must(jd.Info().Env()), ShouldResemble, map[string]string{ 118 "KEY": "VALUE", 119 "OTHER": "NEW_VAL", 120 }) 121 }, 122 }, 123 124 { 125 name: "del", 126 skipSWEmpty: true, 127 fn: func(jd *Definition) { 128 SoEdit(jd, func(je Editor) { 129 je.Env(map[string]string{ 130 "KEY": "VALUE", 131 }) 132 }) 133 SoEdit(jd, func(je Editor) { 134 je.Env(map[string]string{ 135 "OTHER": "NEW_VAL", 136 "KEY": "", // delete 137 }) 138 }) 139 So(must(jd.Info().Env()), ShouldResemble, map[string]string{ 140 "OTHER": "NEW_VAL", 141 }) 142 }, 143 }, 144 }) 145 } 146 147 func TestPriority(t *testing.T) { 148 t.Parallel() 149 150 runCases(t, `Priority`, []testCase{ 151 { 152 name: "negative", 153 fn: func(jd *Definition) { 154 So(jd.Edit(func(je Editor) { 155 je.Priority(-1) 156 }), ShouldErrLike, "negative Priority") 157 }, 158 }, 159 160 { 161 name: "set", 162 fn: func(jd *Definition) { 163 SoEdit(jd, func(je Editor) { 164 je.Priority(100) 165 }) 166 So(jd.Info().Priority(), ShouldEqual, 100) 167 }, 168 }, 169 170 { 171 name: "reset", 172 fn: func(jd *Definition) { 173 SoEdit(jd, func(je Editor) { 174 je.Priority(100) 175 }) 176 SoEdit(jd, func(je Editor) { 177 je.Priority(200) 178 }) 179 So(jd.Info().Priority(), ShouldEqual, 200) 180 }, 181 }, 182 }) 183 } 184 185 func TestSwarmingHostname(t *testing.T) { 186 t.Parallel() 187 188 runCases(t, `SwarmingHostname`, []testCase{ 189 { 190 name: "empty", 191 fn: func(jd *Definition) { 192 So(jd.Edit(func(je Editor) { 193 je.SwarmingHostname("") 194 }), ShouldErrLike, "empty SwarmingHostname") 195 }, 196 }, 197 198 { 199 name: "set", 200 fn: func(jd *Definition) { 201 SoEdit(jd, func(je Editor) { 202 je.SwarmingHostname("example.com") 203 }) 204 So(jd.Info().SwarmingHostname(), ShouldEqual, "example.com") 205 }, 206 }, 207 208 { 209 name: "reset", 210 fn: func(jd *Definition) { 211 SoEdit(jd, func(je Editor) { 212 je.SwarmingHostname("example.com") 213 }) 214 SoEdit(jd, func(je Editor) { 215 je.SwarmingHostname("other.example.com") 216 }) 217 So(jd.Info().SwarmingHostname(), ShouldEqual, "other.example.com") 218 }, 219 }, 220 }) 221 } 222 223 func TestTaskName(t *testing.T) { 224 t.Parallel() 225 226 runCases(t, `TaskName`, []testCase{ 227 { 228 name: "set", 229 fn: func(jd *Definition) { 230 if jd.GetBuildbucket() == nil && jd.GetSwarming().GetTask() == nil { 231 So(jd.Info().TaskName(), ShouldResemble, "") 232 } else { 233 So(jd.Info().TaskName(), ShouldResemble, "default-task-name") 234 } 235 236 SoEdit(jd, func(je Editor) { 237 je.TaskName("") 238 }) 239 So(jd.Info().TaskName(), ShouldResemble, "") 240 241 SoEdit(jd, func(je Editor) { 242 je.TaskName("something") 243 }) 244 So(jd.Info().TaskName(), ShouldResemble, "something") 245 }, 246 }, 247 }) 248 } 249 250 func TestPrefixPathEnv(t *testing.T) { 251 t.Parallel() 252 253 runCases(t, `PrefixPathEnv`, []testCase{ 254 { 255 name: "empty", 256 fn: func(jd *Definition) { 257 SoEdit(jd, func(je Editor) { 258 je.PrefixPathEnv(nil) 259 }) 260 So(must(jd.Info().PrefixPathEnv()), ShouldBeEmpty) 261 }, 262 }, 263 264 { 265 name: "add", 266 skipSWEmpty: true, 267 fn: func(jd *Definition) { 268 SoEdit(jd, func(je Editor) { 269 je.PrefixPathEnv([]string{"some/path", "other/path"}) 270 }) 271 So(must(jd.Info().PrefixPathEnv()), ShouldResemble, []string{ 272 "some/path", "other/path", 273 }) 274 }, 275 }, 276 277 { 278 name: "remove", 279 skipSWEmpty: true, 280 fn: func(jd *Definition) { 281 SoEdit(jd, func(je Editor) { 282 je.PrefixPathEnv([]string{"some/path", "other/path", "third"}) 283 }) 284 SoEdit(jd, func(je Editor) { 285 je.PrefixPathEnv([]string{"!other/path"}) 286 }) 287 So(must(jd.Info().PrefixPathEnv()), ShouldResemble, []string{ 288 "some/path", "third", 289 }) 290 }, 291 }, 292 }) 293 } 294 295 func TestTags(t *testing.T) { 296 t.Parallel() 297 298 runCases(t, `Tags`, []testCase{ 299 { 300 name: "empty", 301 fn: func(jd *Definition) { 302 SoEdit(jd, func(je Editor) { 303 je.Tags(nil) 304 }) 305 So(jd.Info().Tags(), ShouldBeEmpty) 306 }, 307 }, 308 309 { 310 name: "add", 311 fn: func(jd *Definition) { 312 SoEdit(jd, func(je Editor) { 313 je.Tags([]string{"other:value", "key:value"}) 314 }) 315 So(jd.Info().Tags(), ShouldResemble, []string{ 316 "key:value", "other:value", 317 }) 318 }, 319 }, 320 }) 321 } 322 323 func TestExperimental(t *testing.T) { 324 t.Parallel() 325 326 runCases(t, `Experimental`, []testCase{ 327 { 328 name: "full", 329 skipSW: true, 330 fn: func(jd *Definition) { 331 So(jd.HighLevelInfo().Experimental(), ShouldBeFalse) 332 SoHLEdit(jd, func(je HighLevelEditor) { 333 je.Experimental(true) 334 }) 335 So(jd.HighLevelInfo().Experimental(), ShouldBeTrue) 336 So(jd.HighLevelInfo().Experiments(), ShouldResemble, []string{buildbucket.ExperimentNonProduction}) 337 338 SoHLEdit(jd, func(je HighLevelEditor) { 339 je.Experimental(false) 340 }) 341 So(jd.HighLevelInfo().Experimental(), ShouldBeFalse) 342 So(jd.HighLevelInfo().Experiments(), ShouldHaveLength, 0) 343 }, 344 }, 345 }) 346 } 347 348 func TestExperiments(t *testing.T) { 349 t.Parallel() 350 351 runCases(t, `Experiments`, []testCase{ 352 { 353 name: "full", 354 skipSW: true, 355 fn: func(jd *Definition) { 356 So(jd.HighLevelInfo().Experiments(), ShouldHaveLength, 0) 357 358 SoHLEdit(jd, func(je HighLevelEditor) { 359 je.Experiments(map[string]bool{ 360 "exp1": true, 361 "exp2": true, 362 "delMissingOK": false, 363 }) 364 }) 365 er := jd.GetBuildbucket().BbagentArgs.Build.Infra.Buildbucket.ExperimentReasons 366 So(jd.HighLevelInfo().Experiments(), ShouldResemble, []string{"exp1", "exp2"}) 367 So(er, ShouldResemble, map[string]bbpb.BuildInfra_Buildbucket_ExperimentReason{ 368 "exp1": bbpb.BuildInfra_Buildbucket_EXPERIMENT_REASON_REQUESTED, 369 "exp2": bbpb.BuildInfra_Buildbucket_EXPERIMENT_REASON_REQUESTED, 370 }) 371 SoHLEdit(jd, func(je HighLevelEditor) { 372 je.Experiments(map[string]bool{ 373 "exp1": false, 374 }) 375 }) 376 So(jd.HighLevelInfo().Experiments(), ShouldResemble, []string{"exp2"}) 377 So(er, ShouldResemble, map[string]bbpb.BuildInfra_Buildbucket_ExperimentReason{ 378 "exp2": bbpb.BuildInfra_Buildbucket_EXPERIMENT_REASON_REQUESTED, 379 }) 380 }, 381 }, 382 }) 383 } 384 385 func TestProperties(t *testing.T) { 386 t.Parallel() 387 388 runCases(t, `Properties`, []testCase{ 389 { 390 name: "nil", 391 skipSW: true, 392 fn: func(jd *Definition) { 393 SoHLEdit(jd, func(je HighLevelEditor) { 394 je.Properties(nil, false) 395 }) 396 So(must(jd.HighLevelInfo().Properties()), ShouldBeEmpty) 397 }, 398 }, 399 400 { 401 name: "add", 402 skipSW: true, 403 fn: func(jd *Definition) { 404 SoHLEdit(jd, func(je HighLevelEditor) { 405 je.Properties(map[string]string{ 406 "key": `"value"`, 407 "other": `{"something": [1, 2, "subvalue"]}`, 408 "delete": "", // noop 409 }, false) 410 }) 411 So(must(jd.HighLevelInfo().Properties()), ShouldResemble, map[string]string{ 412 "key": `"value"`, 413 "other": `{"something":[1,2,"subvalue"]}`, 414 }) 415 }, 416 }, 417 418 { 419 name: "add (auto)", 420 skipSW: true, 421 fn: func(jd *Definition) { 422 SoHLEdit(jd, func(je HighLevelEditor) { 423 je.Properties(map[string]string{ 424 "json": `{"thingy": "kerplop"}`, 425 "literal": `{I am a banana}`, 426 "delete": "", // noop 427 }, true) 428 }) 429 So(must(jd.HighLevelInfo().Properties()), ShouldResemble, map[string]string{ 430 "json": `{"thingy":"kerplop"}`, 431 "literal": `"{I am a banana}"`, // string now 432 }) 433 }, 434 }, 435 436 { 437 name: "delete", 438 skipSW: true, 439 fn: func(jd *Definition) { 440 SoHLEdit(jd, func(je HighLevelEditor) { 441 je.Properties(map[string]string{ 442 "key": `"value"`, 443 }, false) 444 }) 445 SoHLEdit(jd, func(je HighLevelEditor) { 446 je.Properties(map[string]string{ 447 "key": "", 448 }, false) 449 }) 450 So(must(jd.HighLevelInfo().Properties()), ShouldBeEmpty) 451 }, 452 }, 453 }) 454 } 455 456 func TestGerritChange(t *testing.T) { 457 t.Parallel() 458 459 mkChange := func(project string) *bbpb.GerritChange { 460 return &bbpb.GerritChange{ 461 Host: "host.example.com", 462 Project: project, 463 Change: 1, 464 Patchset: 1, 465 } 466 } 467 468 runCases(t, `GerritChange`, []testCase{ 469 { 470 name: "nil", 471 skipSW: true, 472 fn: func(jd *Definition) { 473 SoHLEdit(jd, func(je HighLevelEditor) { 474 je.AddGerritChange(nil) 475 je.RemoveGerritChange(nil) 476 }) 477 So(jd.HighLevelInfo().GerritChanges(), ShouldBeEmpty) 478 }, 479 }, 480 481 { 482 name: "new", 483 skipSW: true, 484 fn: func(jd *Definition) { 485 SoHLEdit(jd, func(je HighLevelEditor) { 486 je.AddGerritChange(mkChange("project")) 487 }) 488 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 489 mkChange("project"), 490 }) 491 }, 492 }, 493 494 { 495 name: "clear", 496 skipSW: true, 497 fn: func(jd *Definition) { 498 SoHLEdit(jd, func(je HighLevelEditor) { 499 je.AddGerritChange(mkChange("project")) 500 }) 501 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 502 mkChange("project"), 503 }) 504 SoHLEdit(jd, func(je HighLevelEditor) { 505 je.ClearGerritChanges() 506 }) 507 So(jd.HighLevelInfo().GerritChanges(), ShouldBeEmpty) 508 }, 509 }, 510 511 { 512 name: "dupe", 513 skipSW: true, 514 fn: func(jd *Definition) { 515 SoHLEdit(jd, func(je HighLevelEditor) { 516 je.AddGerritChange(mkChange("project")) 517 je.AddGerritChange(mkChange("project")) 518 }) 519 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 520 mkChange("project"), 521 }) 522 }, 523 }, 524 525 { 526 name: "remove", 527 skipSW: true, 528 fn: func(jd *Definition) { 529 bb := jd.GetBuildbucket() 530 bb.EnsureBasics() 531 bb.BbagentArgs.Build.Input.GerritChanges = []*bbpb.GerritChange{ 532 mkChange("a"), 533 mkChange("b"), 534 mkChange("c"), 535 } 536 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 537 mkChange("a"), 538 mkChange("b"), 539 mkChange("c"), 540 }) 541 542 SoHLEdit(jd, func(je HighLevelEditor) { 543 je.RemoveGerritChange(mkChange("b")) 544 }) 545 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 546 mkChange("a"), 547 mkChange("c"), 548 }) 549 }, 550 }, 551 552 { 553 name: "remove (noop)", 554 skipSW: true, 555 fn: func(jd *Definition) { 556 SoHLEdit(jd, func(je HighLevelEditor) { 557 je.AddGerritChange(mkChange("a")) 558 559 je.RemoveGerritChange(mkChange("b")) 560 }) 561 So(jd.HighLevelInfo().GerritChanges(), ShouldResembleProto, []*bbpb.GerritChange{ 562 mkChange("a"), 563 }) 564 }, 565 }, 566 }) 567 } 568 569 func TestGitilesCommit(t *testing.T) { 570 t.Parallel() 571 572 runCases(t, `GitilesChange`, []testCase{ 573 { 574 name: "nil", 575 skipSW: true, 576 fn: func(jd *Definition) { 577 SoHLEdit(jd, func(je HighLevelEditor) { 578 je.GitilesCommit(nil) 579 }) 580 So(jd.HighLevelInfo().GitilesCommit(), ShouldBeNil) 581 }, 582 }, 583 584 { 585 name: "add", 586 skipSW: true, 587 fn: func(jd *Definition) { 588 SoHLEdit(jd, func(je HighLevelEditor) { 589 je.GitilesCommit(&bbpb.GitilesCommit{Id: "deadbeef"}) 590 }) 591 So(jd.HighLevelInfo().GitilesCommit(), ShouldResembleProto, &bbpb.GitilesCommit{ 592 Id: "deadbeef", 593 }) 594 }, 595 }, 596 597 { 598 name: "del", 599 skipSW: true, 600 fn: func(jd *Definition) { 601 SoHLEdit(jd, func(je HighLevelEditor) { 602 je.GitilesCommit(&bbpb.GitilesCommit{Id: "deadbeef"}) 603 }) 604 SoHLEdit(jd, func(je HighLevelEditor) { 605 je.GitilesCommit(nil) 606 }) 607 So(jd.HighLevelInfo().GitilesCommit(), ShouldBeNil) 608 }, 609 }, 610 }) 611 } 612 613 func TestTaskPayload(t *testing.T) { 614 t.Parallel() 615 616 runCases(t, `TaskPayload`, []testCase{ 617 { 618 name: "empty", 619 skipSW: true, 620 fn: func(jd *Definition) { 621 SoHLEdit(jd, func(je HighLevelEditor) { 622 je.TaskPayloadSource("", "") 623 je.TaskPayloadPath("") 624 je.TaskPayloadCmd(nil) 625 }) 626 hli := jd.HighLevelInfo() 627 pkg, vers := hli.TaskPayloadSource() 628 So(pkg, ShouldEqual, "") 629 So(vers, ShouldEqual, "") 630 So(hli.TaskPayloadPath(), ShouldEqual, "") 631 So(hli.TaskPayloadCmd(), ShouldResemble, []string{"luciexe"}) 632 }, 633 }, 634 635 { 636 name: "isolate", 637 skipSW: true, 638 fn: func(jd *Definition) { 639 SoHLEdit(jd, func(je HighLevelEditor) { 640 je.TaskPayloadSource("", "") 641 je.TaskPayloadPath("some/path") 642 }) 643 hli := jd.HighLevelInfo() 644 pkg, vers := hli.TaskPayloadSource() 645 So(pkg, ShouldEqual, "") 646 So(vers, ShouldEqual, "") 647 So(hli.TaskPayloadPath(), ShouldEqual, "some/path") 648 }, 649 }, 650 651 { 652 name: "cipd", 653 skipSW: true, 654 fn: func(jd *Definition) { 655 SoHLEdit(jd, func(je HighLevelEditor) { 656 je.TaskPayloadSource("pkgname", "latest") 657 je.TaskPayloadPath("some/path") 658 }) 659 hli := jd.HighLevelInfo() 660 pkg, vers := hli.TaskPayloadSource() 661 So(pkg, ShouldEqual, "pkgname") 662 So(vers, ShouldEqual, "latest") 663 So(hli.TaskPayloadPath(), ShouldEqual, "some/path") 664 }, 665 }, 666 667 { 668 name: "cipd latest", 669 skipSW: true, 670 fn: func(jd *Definition) { 671 SoHLEdit(jd, func(je HighLevelEditor) { 672 je.TaskPayloadSource("pkgname", "") 673 je.TaskPayloadPath("some/path") 674 }) 675 hli := jd.HighLevelInfo() 676 pkg, vers := hli.TaskPayloadSource() 677 So(pkg, ShouldEqual, "pkgname") 678 So(vers, ShouldEqual, "latest") 679 So(hli.TaskPayloadPath(), ShouldEqual, "some/path") 680 }, 681 }, 682 }) 683 }