github.com/nektos/act@v0.2.83/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 t.Run(table.in, func(t *testing.T) { 159 assertObject := assert.New(t) 160 b, err := EvalBool(context.Background(), rc.ExprEval, table.in, exprparser.DefaultStatusCheckSuccess) 161 if table.wantErr { 162 assertObject.Error(err) 163 } 164 165 assertObject.Equal(table.out, b, fmt.Sprintf("Expected %s to be %v, was %v", table.in, table.out, b)) 166 }) 167 } 168 } 169 170 func updateTestIfWorkflow(t *testing.T, tables []struct { 171 in string 172 out bool 173 wantErr bool 174 }, rc *RunContext) { 175 var envs string 176 keys := make([]string, 0, len(rc.Env)) 177 for k := range rc.Env { 178 keys = append(keys, k) 179 } 180 sort.Strings(keys) 181 for _, k := range keys { 182 envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k]) 183 } 184 // editorconfig-checker-disable 185 workflow := fmt.Sprintf(` 186 name: "Test what expressions result in true and false on GitHub" 187 on: push 188 189 env: 190 %s 191 192 jobs: 193 test-ifs-and-buts: 194 runs-on: ubuntu-latest 195 steps: 196 `, envs) 197 // editorconfig-checker-enable 198 199 for i, table := range tables { 200 if table.wantErr || strings.HasPrefix(table.in, "github.actor") { 201 continue 202 } 203 expressionPattern := regexp.MustCompile(`\${{\s*(.+?)\s*}}`) 204 205 expr := expressionPattern.ReplaceAllStringFunc(table.in, func(match string) string { 206 return fmt.Sprintf("€{{ %s }}", expressionPattern.ReplaceAllString(match, "$1")) 207 }) 208 echo := fmt.Sprintf(`run: echo "%s should be false, but was evaluated to true;" exit 1;`, table.in) 209 name := fmt.Sprintf(`"❌ I should not run, expr: %s"`, expr) 210 if table.out { 211 echo = `run: echo OK` 212 name = fmt.Sprintf(`"✅ I should run, expr: %s"`, expr) 213 } 214 workflow += fmt.Sprintf("\n - name: %s\n id: step%d\n if: %s\n %s\n", name, i, table.in, echo) 215 if table.out { 216 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) 217 } 218 } 219 220 file, err := os.Create("../../.github/workflows/test-if.yml") 221 if err != nil { 222 t.Fatal(err) 223 } 224 225 _, err = file.WriteString(workflow) 226 if err != nil { 227 t.Fatal(err) 228 } 229 } 230 231 func TestRunContext_GetBindsAndMounts(t *testing.T) { 232 rctemplate := &RunContext{ 233 Name: "TestRCName", 234 Run: &model.Run{ 235 Workflow: &model.Workflow{ 236 Name: "TestWorkflowName", 237 }, 238 }, 239 Config: &Config{ 240 BindWorkdir: false, 241 }, 242 } 243 244 tests := []struct { 245 windowsPath bool 246 name string 247 rc *RunContext 248 wantbind string 249 wantmount string 250 }{ 251 {false, "/mnt/linux", rctemplate, "/mnt/linux", "/mnt/linux"}, 252 {false, "/mnt/path with spaces/linux", rctemplate, "/mnt/path with spaces/linux", "/mnt/path with spaces/linux"}, 253 {true, "C:\\Users\\TestPath\\MyTestPath", rctemplate, "/mnt/c/Users/TestPath/MyTestPath", "/mnt/c/Users/TestPath/MyTestPath"}, 254 {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"}, 255 {true, "/LinuxPathOnWindowsShouldFail", rctemplate, "", ""}, 256 } 257 258 isWindows := runtime.GOOS == "windows" 259 260 for _, testcase := range tests { 261 for _, bindWorkDir := range []bool{true, false} { 262 testBindSuffix := "" 263 if bindWorkDir { 264 testBindSuffix = "Bind" 265 } 266 267 // Only run windows path tests on windows and non-windows on non-windows 268 if (isWindows && testcase.windowsPath) || (!isWindows && !testcase.windowsPath) { 269 t.Run((testcase.name + testBindSuffix), func(t *testing.T) { 270 config := testcase.rc.Config 271 config.Workdir = testcase.name 272 config.BindWorkdir = bindWorkDir 273 gotbind, gotmount := rctemplate.GetBindsAndMounts() 274 275 // Name binds/mounts are either/or 276 if config.BindWorkdir { 277 fullBind := testcase.name + ":" + testcase.wantbind 278 if runtime.GOOS == "darwin" { 279 fullBind += ":delegated" 280 } 281 assert.Contains(t, gotbind, fullBind) 282 } else { 283 mountkey := testcase.rc.jobContainerName() 284 assert.EqualValues(t, testcase.wantmount, gotmount[mountkey]) 285 } 286 }) 287 } 288 } 289 } 290 291 t.Run("ContainerVolumeMountTest", func(t *testing.T) { 292 tests := []struct { 293 name string 294 volumes []string 295 wantbind string 296 wantmount map[string]string 297 }{ 298 {"BindAnonymousVolume", []string{"/volume"}, "/volume", map[string]string{}}, 299 {"BindHostFile", []string{"/path/to/file/on/host:/volume"}, "/path/to/file/on/host:/volume", map[string]string{}}, 300 {"MountExistingVolume", []string{"volume-id:/volume"}, "", map[string]string{"volume-id": "/volume"}}, 301 } 302 303 for _, testcase := range tests { 304 t.Run(testcase.name, func(t *testing.T) { 305 job := &model.Job{} 306 err := job.RawContainer.Encode(map[string][]string{ 307 "volumes": testcase.volumes, 308 }) 309 assert.NoError(t, err) 310 311 rc := &RunContext{ 312 Name: "TestRCName", 313 Run: &model.Run{ 314 Workflow: &model.Workflow{ 315 Name: "TestWorkflowName", 316 }, 317 }, 318 Config: &Config{ 319 BindWorkdir: false, 320 }, 321 } 322 rc.Run.JobID = "job1" 323 rc.Run.Workflow.Jobs = map[string]*model.Job{"job1": job} 324 325 gotbind, gotmount := rc.GetBindsAndMounts() 326 327 if len(testcase.wantbind) > 0 { 328 assert.Contains(t, gotbind, testcase.wantbind) 329 } 330 331 for k, v := range testcase.wantmount { 332 assert.Contains(t, gotmount, k) 333 assert.Equal(t, gotmount[k], v) 334 } 335 }) 336 } 337 }) 338 } 339 340 func TestGetGitHubContext(t *testing.T) { 341 log.SetLevel(log.DebugLevel) 342 343 cwd, err := os.Getwd() 344 assert.Nil(t, err) 345 346 rc := &RunContext{ 347 Config: &Config{ 348 EventName: "push", 349 Workdir: cwd, 350 }, 351 Run: &model.Run{ 352 Workflow: &model.Workflow{ 353 Name: "GitHubContextTest", 354 }, 355 }, 356 Name: "GitHubContextTest", 357 CurrentStep: "step", 358 Matrix: map[string]interface{}{}, 359 Env: map[string]string{}, 360 ExtraPath: []string{}, 361 StepResults: map[string]*model.StepResult{}, 362 OutputMappings: map[MappableOutput]MappableOutput{}, 363 } 364 rc.Run.JobID = "job1" 365 366 ghc := rc.getGithubContext(context.Background()) 367 368 log.Debugf("%v", ghc) 369 370 actor := "nektos/act" 371 if a := os.Getenv("ACT_ACTOR"); a != "" { 372 actor = a 373 } 374 375 repo := "nektos/act" 376 if r := os.Getenv("ACT_REPOSITORY"); r != "" { 377 repo = r 378 } 379 380 owner := "nektos" 381 if o := os.Getenv("ACT_OWNER"); o != "" { 382 owner = o 383 } 384 385 assert.Equal(t, "1", ghc.RunID) 386 assert.Equal(t, "1", ghc.RunNumber) 387 assert.Equal(t, "0", ghc.RetentionDays) 388 assert.Equal(t, actor, ghc.Actor) 389 assert.Equal(t, repo, ghc.Repository) 390 assert.Equal(t, owner, ghc.RepositoryOwner) 391 assert.Equal(t, "/dev/null", ghc.RunnerPerflog) 392 assert.Equal(t, rc.Config.Secrets["GITHUB_TOKEN"], ghc.Token) 393 assert.Equal(t, "job1", ghc.Job) 394 } 395 396 func TestGetGithubContextRef(t *testing.T) { 397 table := []struct { 398 event string 399 json string 400 ref string 401 }{ 402 {event: "push", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 403 {event: "create", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 404 {event: "workflow_dispatch", json: `{"ref":"0000000000000000000000000000000000000000"}`, ref: "0000000000000000000000000000000000000000"}, 405 {event: "delete", json: `{"repository":{"default_branch": "main"}}`, ref: "refs/heads/main"}, 406 {event: "pull_request", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 407 {event: "pull_request_review", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 408 {event: "pull_request_review_comment", json: `{"number":123}`, ref: "refs/pull/123/merge"}, 409 {event: "pull_request_target", json: `{"pull_request":{"base":{"ref": "main"}}}`, ref: "refs/heads/main"}, 410 {event: "deployment", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, 411 {event: "deployment_status", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, 412 {event: "release", json: `{"release": {"tag_name": "tag-name"}}`, ref: "refs/tags/tag-name"}, 413 } 414 415 for _, data := range table { 416 t.Run(data.event, func(t *testing.T) { 417 rc := &RunContext{ 418 EventJSON: data.json, 419 Config: &Config{ 420 EventName: data.event, 421 Workdir: "", 422 }, 423 Run: &model.Run{ 424 Workflow: &model.Workflow{ 425 Name: "GitHubContextTest", 426 }, 427 }, 428 } 429 430 ghc := rc.getGithubContext(context.Background()) 431 432 assert.Equal(t, data.ref, ghc.Ref) 433 }) 434 } 435 } 436 437 func createIfTestRunContext(jobs map[string]*model.Job) *RunContext { 438 rc := &RunContext{ 439 Config: &Config{ 440 Workdir: ".", 441 Platforms: map[string]string{ 442 "ubuntu-latest": "ubuntu-latest", 443 }, 444 }, 445 Env: map[string]string{}, 446 Run: &model.Run{ 447 JobID: "job1", 448 Workflow: &model.Workflow{ 449 Name: "test-workflow", 450 Jobs: jobs, 451 }, 452 }, 453 } 454 rc.ExprEval = rc.NewExpressionEvaluator(context.Background()) 455 456 return rc 457 } 458 459 func createJob(t *testing.T, input string, result string) *model.Job { 460 var job *model.Job 461 err := yaml.Unmarshal([]byte(input), &job) 462 assert.NoError(t, err) 463 job.Result = result 464 465 return job 466 } 467 468 func TestRunContextRunsOnPlatformNames(t *testing.T) { 469 log.SetLevel(log.DebugLevel) 470 assertObject := assert.New(t) 471 472 rc := createIfTestRunContext(map[string]*model.Job{ 473 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 474 }) 475 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 476 477 rc = createIfTestRunContext(map[string]*model.Job{ 478 "job1": createJob(t, `runs-on: ${{ 'ubuntu-latest' }}`, ""), 479 }) 480 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 481 482 rc = createIfTestRunContext(map[string]*model.Job{ 483 "job1": createJob(t, `runs-on: [self-hosted, my-runner]`, ""), 484 }) 485 assertObject.Equal([]string{"self-hosted", "my-runner"}, rc.runsOnPlatformNames(context.Background())) 486 487 rc = createIfTestRunContext(map[string]*model.Job{ 488 "job1": createJob(t, `runs-on: [self-hosted, "${{ 'my-runner' }}"]`, ""), 489 }) 490 assertObject.Equal([]string{"self-hosted", "my-runner"}, rc.runsOnPlatformNames(context.Background())) 491 492 rc = createIfTestRunContext(map[string]*model.Job{ 493 "job1": createJob(t, `runs-on: ${{ fromJSON('["ubuntu-latest"]') }}`, ""), 494 }) 495 assertObject.Equal([]string{"ubuntu-latest"}, rc.runsOnPlatformNames(context.Background())) 496 497 // test missing / invalid runs-on 498 rc = createIfTestRunContext(map[string]*model.Job{ 499 "job1": createJob(t, `name: something`, ""), 500 }) 501 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 502 503 rc = createIfTestRunContext(map[string]*model.Job{ 504 "job1": createJob(t, `runs-on: 505 mapping: value`, ""), 506 }) 507 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 508 509 rc = createIfTestRunContext(map[string]*model.Job{ 510 "job1": createJob(t, `runs-on: ${{ invalid expression }}`, ""), 511 }) 512 assertObject.Equal([]string{}, rc.runsOnPlatformNames(context.Background())) 513 } 514 515 func TestRunContextIsEnabled(t *testing.T) { 516 log.SetLevel(log.DebugLevel) 517 assertObject := assert.New(t) 518 519 // success() 520 rc := createIfTestRunContext(map[string]*model.Job{ 521 "job1": createJob(t, `runs-on: ubuntu-latest 522 if: success()`, ""), 523 }) 524 assertObject.True(rc.isEnabled(context.Background())) 525 526 rc = createIfTestRunContext(map[string]*model.Job{ 527 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 528 "job2": createJob(t, `runs-on: ubuntu-latest 529 needs: [job1] 530 if: success()`, ""), 531 }) 532 rc.Run.JobID = "job2" 533 assertObject.False(rc.isEnabled(context.Background())) 534 535 rc = createIfTestRunContext(map[string]*model.Job{ 536 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 537 "job2": createJob(t, `runs-on: ubuntu-latest 538 needs: [job1] 539 if: success()`, ""), 540 }) 541 rc.Run.JobID = "job2" 542 assertObject.True(rc.isEnabled(context.Background())) 543 544 rc = createIfTestRunContext(map[string]*model.Job{ 545 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 546 "job2": createJob(t, `runs-on: ubuntu-latest 547 if: success()`, ""), 548 }) 549 rc.Run.JobID = "job2" 550 assertObject.True(rc.isEnabled(context.Background())) 551 552 // failure() 553 rc = createIfTestRunContext(map[string]*model.Job{ 554 "job1": createJob(t, `runs-on: ubuntu-latest 555 if: failure()`, ""), 556 }) 557 assertObject.False(rc.isEnabled(context.Background())) 558 559 rc = createIfTestRunContext(map[string]*model.Job{ 560 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 561 "job2": createJob(t, `runs-on: ubuntu-latest 562 needs: [job1] 563 if: failure()`, ""), 564 }) 565 rc.Run.JobID = "job2" 566 assertObject.True(rc.isEnabled(context.Background())) 567 568 rc = createIfTestRunContext(map[string]*model.Job{ 569 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 570 "job2": createJob(t, `runs-on: ubuntu-latest 571 needs: [job1] 572 if: failure()`, ""), 573 }) 574 rc.Run.JobID = "job2" 575 assertObject.False(rc.isEnabled(context.Background())) 576 577 rc = createIfTestRunContext(map[string]*model.Job{ 578 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 579 "job2": createJob(t, `runs-on: ubuntu-latest 580 if: failure()`, ""), 581 }) 582 rc.Run.JobID = "job2" 583 assertObject.False(rc.isEnabled(context.Background())) 584 585 // always() 586 rc = createIfTestRunContext(map[string]*model.Job{ 587 "job1": createJob(t, `runs-on: ubuntu-latest 588 if: always()`, ""), 589 }) 590 assertObject.True(rc.isEnabled(context.Background())) 591 592 rc = createIfTestRunContext(map[string]*model.Job{ 593 "job1": createJob(t, `runs-on: ubuntu-latest`, "failure"), 594 "job2": createJob(t, `runs-on: ubuntu-latest 595 needs: [job1] 596 if: always()`, ""), 597 }) 598 rc.Run.JobID = "job2" 599 assertObject.True(rc.isEnabled(context.Background())) 600 601 rc = createIfTestRunContext(map[string]*model.Job{ 602 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 603 "job2": createJob(t, `runs-on: ubuntu-latest 604 needs: [job1] 605 if: always()`, ""), 606 }) 607 rc.Run.JobID = "job2" 608 assertObject.True(rc.isEnabled(context.Background())) 609 610 rc = createIfTestRunContext(map[string]*model.Job{ 611 "job1": createJob(t, `runs-on: ubuntu-latest`, "success"), 612 "job2": createJob(t, `runs-on: ubuntu-latest 613 if: always()`, ""), 614 }) 615 rc.Run.JobID = "job2" 616 assertObject.True(rc.isEnabled(context.Background())) 617 618 rc = createIfTestRunContext(map[string]*model.Job{ 619 "job1": createJob(t, `uses: ./.github/workflows/reusable.yml`, ""), 620 }) 621 assertObject.True(rc.isEnabled(context.Background())) 622 623 rc = createIfTestRunContext(map[string]*model.Job{ 624 "job1": createJob(t, `uses: ./.github/workflows/reusable.yml 625 if: false`, ""), 626 }) 627 assertObject.False(rc.isEnabled(context.Background())) 628 } 629 630 func TestRunContextGetEnv(t *testing.T) { 631 tests := []struct { 632 description string 633 rc *RunContext 634 targetEnv string 635 want string 636 }{ 637 { 638 description: "Env from Config should overwrite", 639 rc: &RunContext{ 640 Config: &Config{ 641 Env: map[string]string{"OVERWRITTEN": "true"}, 642 }, 643 Run: &model.Run{ 644 Workflow: &model.Workflow{ 645 Jobs: map[string]*model.Job{"test": {Name: "test"}}, 646 Env: map[string]string{"OVERWRITTEN": "false"}, 647 }, 648 JobID: "test", 649 }, 650 }, 651 targetEnv: "OVERWRITTEN", 652 want: "true", 653 }, 654 { 655 description: "No overwrite occurs", 656 rc: &RunContext{ 657 Config: &Config{ 658 Env: map[string]string{"SOME_OTHER_VAR": "true"}, 659 }, 660 Run: &model.Run{ 661 Workflow: &model.Workflow{ 662 Jobs: map[string]*model.Job{"test": {Name: "test"}}, 663 Env: map[string]string{"OVERWRITTEN": "false"}, 664 }, 665 JobID: "test", 666 }, 667 }, 668 targetEnv: "OVERWRITTEN", 669 want: "false", 670 }, 671 } 672 673 for _, test := range tests { 674 t.Run(test.description, func(t *testing.T) { 675 envMap := test.rc.GetEnv() 676 assert.EqualValues(t, test.want, envMap[test.targetEnv]) 677 }) 678 } 679 } 680 681 func TestSetRuntimeVariables(t *testing.T) { 682 rc := &RunContext{ 683 Config: &Config{ 684 ArtifactServerAddr: "myhost", 685 ArtifactServerPort: "8000", 686 }, 687 } 688 v := "http://myhost:8000/" 689 env := map[string]string{} 690 setActionRuntimeVars(rc, env) 691 692 assert.Equal(t, v, env["ACTIONS_RESULTS_URL"]) 693 assert.Equal(t, v, env["ACTIONS_RUNTIME_URL"]) 694 runtimeToken := env["ACTIONS_RUNTIME_TOKEN"] 695 assert.NotEmpty(t, v, runtimeToken) 696 697 tkn, _, err := jwt.NewParser().ParseUnverified(runtimeToken, jwt.MapClaims{}) 698 assert.NotNil(t, tkn) 699 assert.Nil(t, err) 700 } 701 702 func TestSetRuntimeVariablesWithRunID(t *testing.T) { 703 rc := &RunContext{ 704 Config: &Config{ 705 ArtifactServerAddr: "myhost", 706 ArtifactServerPort: "8000", 707 Env: map[string]string{ 708 "GITHUB_RUN_ID": "45", 709 }, 710 }, 711 } 712 v := "http://myhost:8000/" 713 env := map[string]string{} 714 setActionRuntimeVars(rc, env) 715 716 assert.Equal(t, v, env["ACTIONS_RESULTS_URL"]) 717 assert.Equal(t, v, env["ACTIONS_RUNTIME_URL"]) 718 runtimeToken := env["ACTIONS_RUNTIME_TOKEN"] 719 assert.NotEmpty(t, v, runtimeToken) 720 721 claims := jwt.MapClaims{} 722 tkn, _, err := jwt.NewParser().ParseUnverified(runtimeToken, &claims) 723 assert.NotNil(t, tkn) 724 assert.Nil(t, err) 725 scp, ok := claims["scp"] 726 assert.True(t, ok, "scp claim exists") 727 assert.Equal(t, "Actions.Results:45:45", scp, "contains expected scp claim") 728 }