github.com/nektos/act@v0.2.63/pkg/runner/run_context_test.go (about) 1 package runner 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "regexp" 8 "runtime" 9 "sort" 10 "strings" 11 "testing" 12 13 "github.com/golang-jwt/jwt/v5" 14 "github.com/nektos/act/pkg/exprparser" 15 "github.com/nektos/act/pkg/model" 16 17 log "github.com/sirupsen/logrus" 18 assert "github.com/stretchr/testify/assert" 19 yaml "gopkg.in/yaml.v3" 20 ) 21 22 func TestRunContext_EvalBool(t *testing.T) { 23 var yml yaml.Node 24 err := yml.Encode(map[string][]interface{}{ 25 "os": {"Linux", "Windows"}, 26 "foo": {"bar", "baz"}, 27 }) 28 assert.NoError(t, err) 29 30 rc := &RunContext{ 31 Config: &Config{ 32 Workdir: ".", 33 }, 34 Env: map[string]string{ 35 "SOMETHING_TRUE": "true", 36 "SOMETHING_FALSE": "false", 37 "SOME_TEXT": "text", 38 }, 39 Run: &model.Run{ 40 JobID: "job1", 41 Workflow: &model.Workflow{ 42 Name: "test-workflow", 43 Jobs: map[string]*model.Job{ 44 "job1": { 45 Strategy: &model.Strategy{ 46 RawMatrix: yml, 47 }, 48 }, 49 }, 50 }, 51 }, 52 Matrix: map[string]interface{}{ 53 "os": "Linux", 54 "foo": "bar", 55 }, 56 StepResults: map[string]*model.StepResult{ 57 "id1": { 58 Conclusion: model.StepStatusSuccess, 59 Outcome: model.StepStatusFailure, 60 Outputs: map[string]string{ 61 "foo": "bar", 62 }, 63 }, 64 }, 65 } 66 rc.ExprEval = rc.NewExpressionEvaluator(context.Background()) 67 68 tables := []struct { 69 in string 70 out bool 71 wantErr bool 72 }{ 73 // The basic ones 74 {in: "failure()", out: false}, 75 {in: "success()", out: true}, 76 {in: "cancelled()", out: false}, 77 {in: "always()", out: true}, 78 // TODO: move to sc.NewExpressionEvaluator(), because "steps" context is not available here 79 // {in: "steps.id1.conclusion == 'success'", out: true}, 80 // {in: "steps.id1.conclusion != 'success'", out: false}, 81 // {in: "steps.id1.outcome == 'failure'", out: true}, 82 // {in: "steps.id1.outcome != 'failure'", out: false}, 83 {in: "true", out: true}, 84 {in: "false", out: false}, 85 // TODO: This does not throw an error, because the evaluator does not know if the expression is inside ${{ }} or not 86 // {in: "!true", wantErr: true}, 87 // {in: "!false", wantErr: true}, 88 {in: "1 != 0", out: true}, 89 {in: "1 != 1", out: false}, 90 {in: "${{ 1 != 0 }}", out: true}, 91 {in: "${{ 1 != 1 }}", out: false}, 92 {in: "1 == 0", out: false}, 93 {in: "1 == 1", out: true}, 94 {in: "1 > 2", out: false}, 95 {in: "1 < 2", out: true}, 96 // And or 97 {in: "true && false", out: false}, 98 {in: "true && 1 < 2", out: true}, 99 {in: "false || 1 < 2", out: true}, 100 {in: "false || false", out: false}, 101 // None boolable 102 {in: "env.UNKNOWN == 'true'", out: false}, 103 {in: "env.UNKNOWN", out: false}, 104 // Inline expressions 105 {in: "env.SOME_TEXT", out: true}, 106 {in: "env.SOME_TEXT == 'text'", out: true}, 107 {in: "env.SOMETHING_TRUE == 'true'", out: true}, 108 {in: "env.SOMETHING_FALSE == 'true'", out: false}, 109 {in: "env.SOMETHING_TRUE", out: true}, 110 {in: "env.SOMETHING_FALSE", out: true}, 111 // TODO: This does not throw an error, because the evaluator does not know if the expression is inside ${{ }} or not 112 // {in: "!env.SOMETHING_TRUE", wantErr: true}, 113 // {in: "!env.SOMETHING_FALSE", wantErr: true}, 114 {in: "${{ !env.SOMETHING_TRUE }}", out: false}, 115 {in: "${{ !env.SOMETHING_FALSE }}", out: false}, 116 {in: "${{ ! env.SOMETHING_TRUE }}", out: false}, 117 {in: "${{ ! env.SOMETHING_FALSE }}", out: false}, 118 {in: "${{ env.SOMETHING_TRUE }}", out: true}, 119 {in: "${{ env.SOMETHING_FALSE }}", out: true}, 120 {in: "${{ !env.SOMETHING_TRUE }}", out: false}, 121 {in: "${{ !env.SOMETHING_FALSE }}", out: false}, 122 {in: "${{ !env.SOMETHING_TRUE && true }}", out: false}, 123 {in: "${{ !env.SOMETHING_FALSE && true }}", out: false}, 124 {in: "${{ !env.SOMETHING_TRUE || true }}", out: true}, 125 {in: "${{ !env.SOMETHING_FALSE || false }}", out: false}, 126 {in: "${{ env.SOMETHING_TRUE && true }}", out: true}, 127 {in: "${{ env.SOMETHING_FALSE || true }}", out: true}, 128 {in: "${{ env.SOMETHING_FALSE || false }}", out: true}, 129 // TODO: This does not throw an error, because the evaluator does not know if the expression is inside ${{ }} or not 130 // {in: "!env.SOMETHING_TRUE || true", wantErr: true}, 131 {in: "${{ env.SOMETHING_TRUE == 'true'}}", out: true}, 132 {in: "${{ env.SOMETHING_FALSE == 'true'}}", out: false}, 133 {in: "${{ env.SOMETHING_FALSE == 'false'}}", out: true}, 134 {in: "${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }}", out: true}, 135 136 // All together now 137 {in: "false || env.SOMETHING_TRUE == 'true'", out: true}, 138 {in: "true || env.SOMETHING_FALSE == 'true'", out: true}, 139 {in: "true && env.SOMETHING_TRUE == 'true'", out: true}, 140 {in: "false && env.SOMETHING_TRUE == 'true'", out: false}, 141 {in: "env.SOMETHING_FALSE == 'true' && env.SOMETHING_TRUE == 'true'", out: false}, 142 {in: "env.SOMETHING_FALSE == 'true' && true", out: false}, 143 {in: "${{ env.SOMETHING_FALSE == 'true' }} && true", out: true}, 144 {in: "true && ${{ env.SOMETHING_FALSE == 'true' }}", out: true}, 145 // Check github context 146 {in: "github.actor == 'nektos/act'", out: true}, 147 {in: "github.actor == 'unknown'", out: false}, 148 {in: "github.job == 'job1'", out: true}, 149 // The special ACT flag 150 {in: "${{ env.ACT }}", out: true}, 151 {in: "${{ !env.ACT }}", out: false}, 152 // Invalid expressions should be reported 153 {in: "INVALID_EXPRESSION", wantErr: true}, 154 } 155 156 updateTestIfWorkflow(t, tables, rc) 157 for _, table := range tables { 158 table := table 159 t.Run(table.in, func(t *testing.T) { 160 assertObject := assert.New(t) 161 b, err := EvalBool(context.Background(), rc.ExprEval, table.in, exprparser.DefaultStatusCheckSuccess) 162 if table.wantErr { 163 assertObject.Error(err) 164 } 165 166 assertObject.Equal(table.out, b, fmt.Sprintf("Expected %s to be %v, was %v", table.in, table.out, b)) 167 }) 168 } 169 } 170 171 func updateTestIfWorkflow(t *testing.T, tables []struct { 172 in string 173 out bool 174 wantErr bool 175 }, rc *RunContext) { 176 var envs string 177 keys := make([]string, 0, len(rc.Env)) 178 for k := range rc.Env { 179 keys = append(keys, k) 180 } 181 sort.Strings(keys) 182 for _, k := range keys { 183 envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k]) 184 } 185 // editorconfig-checker-disable 186 workflow := fmt.Sprintf(` 187 name: "Test what expressions result in true and false on GitHub" 188 on: push 189 190 env: 191 %s 192 193 jobs: 194 test-ifs-and-buts: 195 runs-on: ubuntu-latest 196 steps: 197 `, envs) 198 // editorconfig-checker-enable 199 200 for i, table := range tables { 201 if table.wantErr || strings.HasPrefix(table.in, "github.actor") { 202 continue 203 } 204 expressionPattern := regexp.MustCompile(`\${{\s*(.+?)\s*}}`) 205 206 expr := expressionPattern.ReplaceAllStringFunc(table.in, func(match string) string { 207 return fmt.Sprintf("€{{ %s }}", expressionPattern.ReplaceAllString(match, "$1")) 208 }) 209 echo := fmt.Sprintf(`run: echo "%s should be false, but was evaluated to true;" exit 1;`, table.in) 210 name := fmt.Sprintf(`"❌ I should not run, expr: %s"`, expr) 211 if table.out { 212 echo = `run: echo OK` 213 name = fmt.Sprintf(`"✅ I should run, expr: %s"`, expr) 214 } 215 workflow += fmt.Sprintf("\n - name: %s\n id: step%d\n if: %s\n %s\n", name, i, table.in, echo) 216 if table.out { 217 workflow += fmt.Sprintf("\n - name: \"Double checking expr: %s\"\n if: steps.step%d.conclusion == 'skipped'\n run: echo \"%s should have been true, but wasn't\"\n", expr, i, table.in) 218 } 219 } 220 221 file, err := os.Create("../../.github/workflows/test-if.yml") 222 if err != nil { 223 t.Fatal(err) 224 } 225 226 _, err = file.WriteString(workflow) 227 if err != nil { 228 t.Fatal(err) 229 } 230 } 231 232 func TestRunContext_GetBindsAndMounts(t *testing.T) { 233 rctemplate := &RunContext{ 234 Name: "TestRCName", 235 Run: &model.Run{ 236 Workflow: &model.Workflow{ 237 Name: "TestWorkflowName", 238 }, 239 }, 240 Config: &Config{ 241 BindWorkdir: false, 242 }, 243 } 244 245 tests := []struct { 246 windowsPath bool 247 name string 248 rc *RunContext 249 wantbind string 250 wantmount string 251 }{ 252 {false, "/mnt/linux", rctemplate, "/mnt/linux", "/mnt/linux"}, 253 {false, "/mnt/path with spaces/linux", rctemplate, "/mnt/path with spaces/linux", "/mnt/path with spaces/linux"}, 254 {true, "C:\\Users\\TestPath\\MyTestPath", rctemplate, "/mnt/c/Users/TestPath/MyTestPath", "/mnt/c/Users/TestPath/MyTestPath"}, 255 {true, "C:\\Users\\Test Path with Spaces\\MyTestPath", rctemplate, "/mnt/c/Users/Test Path with Spaces/MyTestPath", "/mnt/c/Users/Test Path with Spaces/MyTestPath"}, 256 {true, "/LinuxPathOnWindowsShouldFail", rctemplate, "", ""}, 257 } 258 259 isWindows := runtime.GOOS == "windows" 260 261 for _, testcase := range tests { 262 // pin for scopelint 263 testcase := testcase 264 for _, bindWorkDir := range []bool{true, false} { 265 // pin for scopelint 266 bindWorkDir := bindWorkDir 267 testBindSuffix := "" 268 if bindWorkDir { 269 testBindSuffix = "Bind" 270 } 271 272 // Only run windows path tests on windows and non-windows on non-windows 273 if (isWindows && testcase.windowsPath) || (!isWindows && !testcase.windowsPath) { 274 t.Run((testcase.name + testBindSuffix), func(t *testing.T) { 275 config := testcase.rc.Config 276 config.Workdir = testcase.name 277 config.BindWorkdir = bindWorkDir 278 gotbind, gotmount := rctemplate.GetBindsAndMounts() 279 280 // Name binds/mounts are either/or 281 if config.BindWorkdir { 282 fullBind := testcase.name + ":" + testcase.wantbind 283 if runtime.GOOS == "darwin" { 284 fullBind += ":delegated" 285 } 286 assert.Contains(t, gotbind, fullBind) 287 } else { 288 mountkey := testcase.rc.jobContainerName() 289 assert.EqualValues(t, testcase.wantmount, gotmount[mountkey]) 290 } 291 }) 292 } 293 } 294 } 295 296 t.Run("ContainerVolumeMountTest", func(t *testing.T) { 297 tests := []struct { 298 name string 299 volumes []string 300 wantbind string 301 wantmount map[string]string 302 }{ 303 {"BindAnonymousVolume", []string{"/volume"}, "/volume", map[string]string{}}, 304 {"BindHostFile", []string{"/path/to/file/on/host:/volume"}, "/path/to/file/on/host:/volume", map[string]string{}}, 305 {"MountExistingVolume", []string{"volume-id:/volume"}, "", map[string]string{"volume-id": "/volume"}}, 306 } 307 308 for _, testcase := range tests { 309 t.Run(testcase.name, func(t *testing.T) { 310 job := &model.Job{} 311 err := job.RawContainer.Encode(map[string][]string{ 312 "volumes": testcase.volumes, 313 }) 314 assert.NoError(t, err) 315 316 rc := &RunContext{ 317 Name: "TestRCName", 318 Run: &model.Run{ 319 Workflow: &model.Workflow{ 320 Name: "TestWorkflowName", 321 }, 322 }, 323 Config: &Config{ 324 BindWorkdir: false, 325 }, 326 } 327 rc.Run.JobID = "job1" 328 rc.Run.Workflow.Jobs = map[string]*model.Job{"job1": job} 329 330 gotbind, gotmount := rc.GetBindsAndMounts() 331 332 if len(testcase.wantbind) > 0 { 333 assert.Contains(t, gotbind, testcase.wantbind) 334 } 335 336 for k, v := range testcase.wantmount { 337 assert.Contains(t, gotmount, k) 338 assert.Equal(t, gotmount[k], v) 339 } 340 }) 341 } 342 }) 343 } 344 345 func TestGetGitHubContext(t *testing.T) { 346 log.SetLevel(log.DebugLevel) 347 348 cwd, err := os.Getwd() 349 assert.Nil(t, err) 350 351 rc := &RunContext{ 352 Config: &Config{ 353 EventName: "push", 354 Workdir: cwd, 355 }, 356 Run: &model.Run{ 357 Workflow: &model.Workflow{ 358 Name: "GitHubContextTest", 359 }, 360 }, 361 Name: "GitHubContextTest", 362 CurrentStep: "step", 363 Matrix: map[string]interface{}{}, 364 Env: map[string]string{}, 365 ExtraPath: []string{}, 366 StepResults: map[string]*model.StepResult{}, 367 OutputMappings: map[MappableOutput]MappableOutput{}, 368 } 369 rc.Run.JobID = "job1" 370 371 ghc := rc.getGithubContext(context.Background()) 372 373 log.Debugf("%v", ghc) 374 375 actor := "nektos/act" 376 if a := os.Getenv("ACT_ACTOR"); a != "" { 377 actor = a 378 } 379 380 repo := "nektos/act" 381 if r := os.Getenv("ACT_REPOSITORY"); r != "" { 382 repo = r 383 } 384 385 owner := "nektos" 386 if o := os.Getenv("ACT_OWNER"); o != "" { 387 owner = o 388 } 389 390 assert.Equal(t, ghc.RunID, "1") 391 assert.Equal(t, ghc.RunNumber, "1") 392 assert.Equal(t, ghc.RetentionDays, "0") 393 assert.Equal(t, ghc.Actor, actor) 394 assert.Equal(t, ghc.Repository, repo) 395 assert.Equal(t, ghc.RepositoryOwner, owner) 396 assert.Equal(t, ghc.RunnerPerflog, "/dev/null") 397 assert.Equal(t, ghc.Token, rc.Config.Secrets["GITHUB_TOKEN"]) 398 assert.Equal(t, ghc.Job, "job1") 399 } 400 401 func TestGetGithubContextRef(t *testing.T) { 402 table := []struct { 403 event string 404 json string 405 ref string 406 }{ 407 {event: "push", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 408 {event: "create", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 409 {event: "workflow_dispatch", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 410 {event: "delete", json: `{"repository":{"default_branch": "main"}}`, ref: "refs/heads/main"}, 411 {event: "pull_request", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 412 {event: "pull_request_review", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 413 {event: "pull_request_review_comment", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 414 {event: "pull_request_target", json: `{"pull_request":{"base":{"ref": "main"}}}`, ref: "refs/heads/main"}, 415 {event: "deployment", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, 416 {event: "deployment_status", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, 417 {event: "release", json: `{"release": {"tag_name": "tag-name"}}`, ref: "refs/tags/tag-name"}, 418 } 419 420 for _, data := range table { 421 data := data 422 t.Run(data.event, func(t *testing.T) { 423 rc := &RunContext{ 424 EventJSON: data.json, 425 Config: &Config{ 426 EventName: data.event, 427 Workdir: "", 428 }, 429 Run: &model.Run{ 430 Workflow: &model.Workflow{ 431 Name: "GitHubContextTest", 432 }, 433 }, 434 } 435 436 ghc := rc.getGithubContext(context.Background()) 437 438 assert.Equal(t, data.ref, ghc.Ref) 439 }) 440 } 441 } 442 443 func createIfTestRunContext(jobs map[string]*model.Job) *RunContext { 444 rc := &RunContext{ 445 Config: &Config{ 446 Workdir: ".", 447 Platforms: map[string]string{ 448 "ubuntu-latest": "ubuntu-latest", 449 }, 450 }, 451 Env: map[string]string{}, 452 Run: &model.Run{ 453 JobID: "job1", 454 Workflow: &model.Workflow{ 455 Name: "test-workflow", 456 Jobs: jobs, 457 }, 458 }, 459 } 460 rc.ExprEval = rc.NewExpressionEvaluator(context.Background()) 461 462 return rc 463 } 464 465 func createJob(t *testing.T, input string, result string) *model.Job { 466 var job *model.Job 467 err := yaml.Unmarshal([]byte(input), &job) 468 assert.NoError(t, err) 469 job.Result = result 470 471 return job 472 } 473 474 func TestRunContextRunsOnPlatformNames(t *testing.T) { 475 log.SetLevel(log.DebugLevel) 476 assertObject := assert.New(t) 477 478 rc := createIfTestRunContext(map[string]*model.Job{ 479 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 480 }) 481 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 482 483 rc = createIfTestRunContext(map[string]*model.Job{ 484 "job1": createJob(t, `runs-on: ${{ 'ubuntu-latest' }}`, ""), 485 }) 486 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 487 488 rc = createIfTestRunContext(map[string]*model.Job{ 489 "job1": createJob(t, `runs-on: [self-hosted, my-runner]`, ""), 490 }) 491 assertObject.Equal([]string{"self-hosted", "my-runner"}, rc.runsOnPlatformNames(context.Background())) 492 493 rc = createIfTestRunContext(map[string]*model.Job{ 494 "job1": createJob(t, `runs-on: [self-hosted, "${{ 'my-runner' }}"]`, ""), 495 }) 496 assertObject.Equal([]string{"self-hosted", "my-runner"}, rc.runsOnPlatformNames(context.Background())) 497 498 rc = createIfTestRunContext(map[string]*model.Job{ 499 "job1": createJob(t, `runs-on: ${{ fromJSON('["ubuntu-latest"]') }}`, ""), 500 }) 501 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 502 503 // test missing / invalid runs-on 504 rc = createIfTestRunContext(map[string]*model.Job{ 505 "job1": createJob(t, `name: something`, ""), 506 }) 507 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 508 509 rc = createIfTestRunContext(map[string]*model.Job{ 510 "job1": createJob(t, `runs-on: 511 mapping: value`, ""), 512 }) 513 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 514 515 rc = createIfTestRunContext(map[string]*model.Job{ 516 "job1": createJob(t, `runs-on: ${{ invalid expression }}`, ""), 517 }) 518 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 519 } 520 521 func TestRunContextIsEnabled(t *testing.T) { 522 log.SetLevel(log.DebugLevel) 523 assertObject := assert.New(t) 524 525 // success() 526 rc := createIfTestRunContext(map[string]*model.Job{ 527 "job1": createJob(t, `runs-on: ubuntu-latest 528 if: success()`, ""), 529 }) 530 assertObject.True(rc.isEnabled(context.Background())) 531 532 rc = createIfTestRunContext(map[string]*model.Job{ 533 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 534 "job2": createJob(t, `runs-on: ubuntu-latest 535 needs: [job1] 536 if: success()`, ""), 537 }) 538 rc.Run.JobID = "job2" 539 assertObject.False(rc.isEnabled(context.Background())) 540 541 rc = createIfTestRunContext(map[string]*model.Job{ 542 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 543 "job2": createJob(t, `runs-on: ubuntu-latest 544 needs: [job1] 545 if: success()`, ""), 546 }) 547 rc.Run.JobID = "job2" 548 assertObject.True(rc.isEnabled(context.Background())) 549 550 rc = createIfTestRunContext(map[string]*model.Job{ 551 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 552 "job2": createJob(t, `runs-on: ubuntu-latest 553 if: success()`, ""), 554 }) 555 rc.Run.JobID = "job2" 556 assertObject.True(rc.isEnabled(context.Background())) 557 558 // failure() 559 rc = createIfTestRunContext(map[string]*model.Job{ 560 "job1": createJob(t, `runs-on: ubuntu-latest 561 if: failure()`, ""), 562 }) 563 assertObject.False(rc.isEnabled(context.Background())) 564 565 rc = createIfTestRunContext(map[string]*model.Job{ 566 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 567 "job2": createJob(t, `runs-on: ubuntu-latest 568 needs: [job1] 569 if: failure()`, ""), 570 }) 571 rc.Run.JobID = "job2" 572 assertObject.True(rc.isEnabled(context.Background())) 573 574 rc = createIfTestRunContext(map[string]*model.Job{ 575 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 576 "job2": createJob(t, `runs-on: ubuntu-latest 577 needs: [job1] 578 if: failure()`, ""), 579 }) 580 rc.Run.JobID = "job2" 581 assertObject.False(rc.isEnabled(context.Background())) 582 583 rc = createIfTestRunContext(map[string]*model.Job{ 584 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 585 "job2": createJob(t, `runs-on: ubuntu-latest 586 if: failure()`, ""), 587 }) 588 rc.Run.JobID = "job2" 589 assertObject.False(rc.isEnabled(context.Background())) 590 591 // always() 592 rc = createIfTestRunContext(map[string]*model.Job{ 593 "job1": createJob(t, `runs-on: ubuntu-latest 594 if: always()`, ""), 595 }) 596 assertObject.True(rc.isEnabled(context.Background())) 597 598 rc = createIfTestRunContext(map[string]*model.Job{ 599 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 600 "job2": createJob(t, `runs-on: ubuntu-latest 601 needs: [job1] 602 if: always()`, ""), 603 }) 604 rc.Run.JobID = "job2" 605 assertObject.True(rc.isEnabled(context.Background())) 606 607 rc = createIfTestRunContext(map[string]*model.Job{ 608 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 609 "job2": createJob(t, `runs-on: ubuntu-latest 610 needs: [job1] 611 if: always()`, ""), 612 }) 613 rc.Run.JobID = "job2" 614 assertObject.True(rc.isEnabled(context.Background())) 615 616 rc = createIfTestRunContext(map[string]*model.Job{ 617 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 618 "job2": createJob(t, `runs-on: ubuntu-latest 619 if: always()`, ""), 620 }) 621 rc.Run.JobID = "job2" 622 assertObject.True(rc.isEnabled(context.Background())) 623 624 rc = createIfTestRunContext(map[string]*model.Job{ 625 "job1": createJob(t, `uses: ./.github/workflows/reusable.yml`, ""), 626 }) 627 assertObject.True(rc.isEnabled(context.Background())) 628 629 rc = createIfTestRunContext(map[string]*model.Job{ 630 "job1": createJob(t, `uses: ./.github/workflows/reusable.yml 631 if: false`, ""), 632 }) 633 assertObject.False(rc.isEnabled(context.Background())) 634 } 635 636 func TestRunContextGetEnv(t *testing.T) { 637 tests := []struct { 638 description string 639 rc *RunContext 640 targetEnv string 641 want string 642 }{ 643 { 644 description: "Env from Config should overwrite", 645 rc: &RunContext{ 646 Config: &Config{ 647 Env: map[string]string{"OVERWRITTEN": "true"}, 648 }, 649 Run: &model.Run{ 650 Workflow: &model.Workflow{ 651 Jobs: map[string]*model.Job{"test": {Name: "test"}}, 652 Env: map[string]string{"OVERWRITTEN": "false"}, 653 }, 654 JobID: "test", 655 }, 656 }, 657 targetEnv: "OVERWRITTEN", 658 want: "true", 659 }, 660 { 661 description: "No overwrite occurs", 662 rc: &RunContext{ 663 Config: &Config{ 664 Env: map[string]string{"SOME_OTHER_VAR": "true"}, 665 }, 666 Run: &model.Run{ 667 Workflow: &model.Workflow{ 668 Jobs: map[string]*model.Job{"test": {Name: "test"}}, 669 Env: map[string]string{"OVERWRITTEN": "false"}, 670 }, 671 JobID: "test", 672 }, 673 }, 674 targetEnv: "OVERWRITTEN", 675 want: "false", 676 }, 677 } 678 679 for _, test := range tests { 680 t.Run(test.description, func(t *testing.T) { 681 envMap := test.rc.GetEnv() 682 assert.EqualValues(t, test.want, envMap[test.targetEnv]) 683 }) 684 } 685 } 686 687 func TestSetRuntimeVariables(t *testing.T) { 688 rc := &RunContext{ 689 Config: &Config{ 690 ArtifactServerAddr: "myhost", 691 ArtifactServerPort: "8000", 692 }, 693 } 694 v := "http://myhost:8000/" 695 env := map[string]string{} 696 setActionRuntimeVars(rc, env) 697 698 assert.Equal(t, v, env["ACTIONS_RESULTS_URL"]) 699 assert.Equal(t, v, env["ACTIONS_RUNTIME_URL"]) 700 runtimeToken := env["ACTIONS_RUNTIME_TOKEN"] 701 assert.NotEmpty(t, v, runtimeToken) 702 703 tkn, _, err := jwt.NewParser().ParseUnverified(runtimeToken, jwt.MapClaims{}) 704 assert.NotNil(t, tkn) 705 assert.Nil(t, err) 706 } 707 708 func TestSetRuntimeVariablesWithRunID(t *testing.T) { 709 rc := &RunContext{ 710 Config: &Config{ 711 ArtifactServerAddr: "myhost", 712 ArtifactServerPort: "8000", 713 Env: map[string]string{ 714 "GITHUB_RUN_ID": "45", 715 }, 716 }, 717 } 718 v := "http://myhost:8000/" 719 env := map[string]string{} 720 setActionRuntimeVars(rc, env) 721 722 assert.Equal(t, v, env["ACTIONS_RESULTS_URL"]) 723 assert.Equal(t, v, env["ACTIONS_RUNTIME_URL"]) 724 runtimeToken := env["ACTIONS_RUNTIME_TOKEN"] 725 assert.NotEmpty(t, v, runtimeToken) 726 727 claims := jwt.MapClaims{} 728 tkn, _, err := jwt.NewParser().ParseUnverified(runtimeToken, &claims) 729 assert.NotNil(t, tkn) 730 assert.Nil(t, err) 731 scp, ok := claims["scp"] 732 assert.True(t, ok, "scp claim exists") 733 assert.Equal(t, "Actions.Results:45:45", scp, "contains expected scp claim") 734 }