package events_test import ( "errors" "testing" "github.com/google/go-github/v50/github" . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/core/db" "github.com/runatlantis/atlantis/server/core/locking" "github.com/runatlantis/atlantis/server/events" "github.com/runatlantis/atlantis/server/events/command" "github.com/runatlantis/atlantis/server/events/mocks/matchers" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/models/testdata" "github.com/runatlantis/atlantis/server/logging" "github.com/runatlantis/atlantis/server/metrics" . "github.com/runatlantis/atlantis/testing" ) func TestApplyCommandRunner_IsLocked(t *testing.T) { RegisterMockTestingT(t) cases := []struct { Description string ApplyLocked bool ApplyLockError error ExpComment string }{ { Description: "When global apply lock is present IsDisabled returns true", ApplyLocked: true, ApplyLockError: nil, ExpComment: "**Error:** Running `atlantis apply` is disabled.", }, { Description: "When no global apply lock is present and DisableApply flag is false IsDisabled returns false", ApplyLocked: false, ApplyLockError: nil, ExpComment: "Ran Apply for 0 projects:", }, { Description: "If ApplyLockChecker returns an error IsDisabled return value of DisableApply flag", ApplyLockError: errors.New("error"), ApplyLocked: false, ExpComment: "Ran Apply for 0 projects:", }, } for _, c := range cases { t.Run(c.Description, func(t *testing.T) { vcsClient := setup(t) scopeNull, _, _ := metrics.NewLoggingScope(logger, "atlantis") pull := &github.PullRequest{ State: github.String("open"), } modelPull := models.PullRequest{BaseRepo: testdata.GithubRepo, State: models.OpenPullState, Num: testdata.Pull.Num} When(githubGetter.GetPullRequest(testdata.GithubRepo, testdata.Pull.Num)).ThenReturn(pull, nil) When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, modelPull.BaseRepo, testdata.GithubRepo, nil) ctx := &command.Context{ User: testdata.User, Log: logging.NewNoopLogger(t), Scope: scopeNull, Pull: modelPull, HeadRepo: testdata.GithubRepo, Trigger: command.CommentTrigger, } When(applyLockChecker.CheckApplyLock()).ThenReturn(locking.ApplyCommandLock{Locked: c.ApplyLocked}, c.ApplyLockError) applyCommandRunner.Run(ctx, &events.CommentCommand{Name: command.Apply}) vcsClient.VerifyWasCalledOnce().CreateComment(testdata.GithubRepo, modelPull.Num, c.ExpComment, "apply") }) } } func TestApplyCommandRunner_IsSilenced(t *testing.T) { RegisterMockTestingT(t) cases := []struct { Description string Matched bool Targeted bool VCSStatusSilence bool PrevApplyStored bool // stores a 1/1 passing apply in the backend ExpVCSStatusSet bool ExpVCSStatusTotal int ExpVCSStatusSucc int ExpSilenced bool }{ { Description: "When applying, don't comment but set the 0/0 VCS status", ExpVCSStatusSet: true, ExpSilenced: true, }, { Description: "When applying with any previous apply's, don't comment but set the 0/0 VCS status", PrevApplyStored: true, ExpVCSStatusSet: true, ExpSilenced: true, }, { Description: "When applying with unmatched target, don't comment but set the 0/0 VCS status", Targeted: true, ExpVCSStatusSet: true, ExpSilenced: true, }, { Description: "When applying with unmatched target and any previous apply's, don't comment and maintain VCS status", Targeted: true, PrevApplyStored: true, ExpVCSStatusSet: true, ExpSilenced: true, ExpVCSStatusSucc: 1, ExpVCSStatusTotal: 1, }, { Description: "When applying with silenced VCS status, don't do anything", VCSStatusSilence: true, ExpVCSStatusSet: false, ExpSilenced: true, }, { Description: "When applying with matching projects, comment as usual", Matched: true, ExpVCSStatusSet: true, ExpSilenced: false, ExpVCSStatusSucc: 1, ExpVCSStatusTotal: 1, }, } for _, c := range cases { t.Run(c.Description, func(t *testing.T) { // create an empty DB tmp := t.TempDir() db, err := db.New(tmp) Ok(t, err) vcsClient := setup(t, func(tc *TestConfig) { tc.SilenceNoProjects = true tc.silenceVCSStatusNoProjects = c.VCSStatusSilence tc.backend = db }) scopeNull, _, _ := metrics.NewLoggingScope(logger, "atlantis") modelPull := models.PullRequest{BaseRepo: testdata.GithubRepo, State: models.OpenPullState, Num: testdata.Pull.Num} cmd := &events.CommentCommand{Name: command.Apply} if c.Targeted { cmd.RepoRelDir = "mydir" } ctx := &command.Context{ User: testdata.User, Log: logging.NewNoopLogger(t), Scope: scopeNull, Pull: modelPull, HeadRepo: testdata.GithubRepo, Trigger: command.CommentTrigger, } if c.PrevApplyStored { _, err = db.UpdatePullWithResults(modelPull, []command.ProjectResult{ { Command: command.Apply, RepoRelDir: "prevdir", Workspace: "default", }, }) Ok(t, err) } When(projectCommandBuilder.BuildApplyCommands(ctx, cmd)).Then(func(args []Param) ReturnValues { if c.Matched { return ReturnValues{[]command.ProjectContext{{ CommandName: command.Apply, ProjectPlanStatus: models.PlannedPlanStatus, }}, nil} } return ReturnValues{[]command.ProjectContext{}, nil} }) applyCommandRunner.Run(ctx, cmd) timesComment := 1 if c.ExpSilenced { timesComment = 0 } vcsClient.VerifyWasCalled(Times(timesComment)).CreateComment(AnyRepo(), AnyInt(), AnyString(), AnyString()) if c.ExpVCSStatusSet { commitUpdater.VerifyWasCalledOnce().UpdateCombinedCount( matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest(), matchers.EqModelsCommitStatus(models.SuccessCommitStatus), matchers.EqCommandName(command.Apply), EqInt(c.ExpVCSStatusSucc), EqInt(c.ExpVCSStatusTotal), ) } else { commitUpdater.VerifyWasCalled(Never()).UpdateCombinedCount( matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest(), matchers.AnyModelsCommitStatus(), matchers.EqCommandName(command.Apply), AnyInt(), AnyInt(), ) } }) } }