mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 05:18:22 +00:00
Split status checks into plan and apply.
Split single top-level status check into one plan and one apply check. These checks will also now track the whole pull request status rather than just the last action.
This commit is contained in:
@@ -166,7 +166,7 @@ func getAtlantisStatus(t *E2ETester, branchName string) (string, error) {
|
||||
}
|
||||
|
||||
for _, status := range combinedStatus.Statuses {
|
||||
if status.GetContext() == "Atlantis" {
|
||||
if status.GetContext() == "plan/atlantis" {
|
||||
return status.GetState(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,18 +91,26 @@ func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo
|
||||
if !c.validateCtxAndComment(ctx) {
|
||||
return
|
||||
}
|
||||
if err := c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.PlanCommand); err != nil {
|
||||
|
||||
if err := c.CommitStatusUpdater.UpdateCombined(ctx.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.PlanCommand); err != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", err)
|
||||
}
|
||||
|
||||
projectCmds, err := c.ProjectCommandBuilder.BuildAutoplanCommands(ctx)
|
||||
if err != nil {
|
||||
if statusErr := c.CommitStatusUpdater.UpdateCombined(ctx.BaseRepo, ctx.Pull, models.FailedCommitStatus, models.PlanCommand); statusErr != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", statusErr)
|
||||
}
|
||||
|
||||
c.updatePull(ctx, AutoplanCommand{}, CommandResult{Error: err})
|
||||
return
|
||||
}
|
||||
if len(projectCmds) == 0 {
|
||||
log.Info("determined there was no project to run plan in")
|
||||
if err := c.CommitStatusUpdater.Update(baseRepo, pull, models.SuccessCommitStatus, models.PlanCommand); err != nil {
|
||||
// If there were no projects modified, we set a successful commit status
|
||||
// with 0/0 projects planned successfully because we've already set an
|
||||
// in-progress status and we don't want that to be "in progress" forever.
|
||||
if err := c.CommitStatusUpdater.UpdateCombinedCount(baseRepo, pull, models.SuccessCommitStatus, models.PlanCommand, 0, 0); err != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", err)
|
||||
}
|
||||
return
|
||||
@@ -115,10 +123,12 @@ func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo
|
||||
result.PlansDeleted = true
|
||||
}
|
||||
c.updatePull(ctx, AutoplanCommand{}, result)
|
||||
_, err = c.updateDB(ctx, ctx.Pull, result.ProjectResults)
|
||||
pullStatus, err := c.updateDB(ctx, ctx.Pull, result.ProjectResults)
|
||||
if err != nil {
|
||||
c.Logger.Err("writing results: %s", err)
|
||||
}
|
||||
|
||||
c.updateCommitStatus(ctx, models.PlanCommand, pullStatus)
|
||||
}
|
||||
|
||||
// RunCommentCommand executes the command.
|
||||
@@ -184,7 +194,7 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
|
||||
ctx.Log.Info("pull request mergeable status: %t", ctx.PullMergeable)
|
||||
}
|
||||
|
||||
if err = c.CommitStatusUpdater.Update(baseRepo, pull, models.PendingCommitStatus, cmd.CommandName()); err != nil {
|
||||
if err = c.CommitStatusUpdater.UpdateCombined(baseRepo, pull, models.PendingCommitStatus, cmd.CommandName()); err != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", err)
|
||||
}
|
||||
|
||||
@@ -199,6 +209,9 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if statusErr := c.CommitStatusUpdater.UpdateCombined(ctx.BaseRepo, ctx.Pull, models.FailedCommitStatus, cmd.CommandName()); statusErr != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", statusErr)
|
||||
}
|
||||
c.updatePull(ctx, cmd, CommandResult{Error: err})
|
||||
return
|
||||
}
|
||||
@@ -220,11 +233,45 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
|
||||
return
|
||||
}
|
||||
|
||||
c.updateCommitStatus(ctx, cmd.Name, pullStatus)
|
||||
|
||||
if cmd.Name == models.ApplyCommand && c.automergeEnabled(ctx, projectCmds) {
|
||||
c.automerge(ctx, pullStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DefaultCommandRunner) updateCommitStatus(ctx *CommandContext, cmd models.CommandName, pullStatus models.PullStatus) {
|
||||
var numSuccess int
|
||||
var status models.CommitStatus
|
||||
|
||||
if cmd == models.PlanCommand {
|
||||
// We consider anything that isn't a plan error as a plan success.
|
||||
// For example, if there is an apply error, that means that at least a
|
||||
// plan was generated successfully.
|
||||
numSuccess = len(pullStatus.Projects) - pullStatus.StatusCount(models.ErroredPlanStatus)
|
||||
status = models.SuccessCommitStatus
|
||||
if numSuccess != len(pullStatus.Projects) {
|
||||
status = models.FailedCommitStatus
|
||||
}
|
||||
} else {
|
||||
numSuccess = pullStatus.StatusCount(models.AppliedPlanStatus)
|
||||
|
||||
numErrored := pullStatus.StatusCount(models.ErroredApplyStatus)
|
||||
status = models.SuccessCommitStatus
|
||||
if numErrored > 0 {
|
||||
status = models.FailedCommitStatus
|
||||
} else if numSuccess < len(pullStatus.Projects) {
|
||||
// If there are plans that haven't been applied yet, we'll use a pending
|
||||
// status.
|
||||
status = models.PendingCommitStatus
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.CommitStatusUpdater.UpdateCombinedCount(ctx.BaseRepo, ctx.Pull, status, cmd, numSuccess, len(pullStatus.Projects)); err != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.PullStatus) {
|
||||
// We only automerge if all projects have been successfully applied.
|
||||
for _, p := range pullStatus.Projects {
|
||||
@@ -328,10 +375,6 @@ func (c *DefaultCommandRunner) updatePull(ctx *CommandContext, command PullComma
|
||||
ctx.Log.Warn(res.Failure)
|
||||
}
|
||||
|
||||
// Update the pull request's status icon and comment back.
|
||||
if err := c.CommitStatusUpdater.UpdateProjectResult(ctx, command.CommandName(), res); err != nil {
|
||||
ctx.Log.Warn("unable to update commit status: %s", err)
|
||||
}
|
||||
comment := c.MarkdownRenderer.Render(res, command.CommandName(), ctx.Log.History.String(), command.IsVerbose(), ctx.BaseRepo.VCSHost.Type)
|
||||
if err := c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, comment); err != nil {
|
||||
ctx.Log.Err("unable to comment: %s", err)
|
||||
|
||||
145
server/events/command_runner_internal_test.go
Normal file
145
server/events/command_runner_internal_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpdateCommitStatus(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
cmd models.CommandName
|
||||
pullStatus models.PullStatus
|
||||
expStatus models.CommitStatus
|
||||
expNumSuccess int
|
||||
expNumTotal int
|
||||
}{
|
||||
"single plan success": {
|
||||
cmd: models.PlanCommand,
|
||||
pullStatus: models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
expStatus: models.SuccessCommitStatus,
|
||||
expNumSuccess: 1,
|
||||
expNumTotal: 1,
|
||||
},
|
||||
"one plan error, other errors": {
|
||||
cmd: models.PlanCommand,
|
||||
pullStatus: models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.ErroredPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.ErroredApplyStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
expStatus: models.FailedCommitStatus,
|
||||
expNumSuccess: 3,
|
||||
expNumTotal: 4,
|
||||
},
|
||||
"apply, one pending": {
|
||||
cmd: models.ApplyCommand,
|
||||
pullStatus: models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
expStatus: models.PendingCommitStatus,
|
||||
expNumSuccess: 1,
|
||||
expNumTotal: 2,
|
||||
},
|
||||
"apply, all successful": {
|
||||
cmd: models.ApplyCommand,
|
||||
pullStatus: models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
expStatus: models.SuccessCommitStatus,
|
||||
expNumSuccess: 2,
|
||||
expNumTotal: 2,
|
||||
},
|
||||
"apply, one errored, one pending": {
|
||||
cmd: models.ApplyCommand,
|
||||
pullStatus: models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.ErroredApplyStatus,
|
||||
},
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
expStatus: models.FailedCommitStatus,
|
||||
expNumSuccess: 1,
|
||||
expNumTotal: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
csu := &MockCSU{}
|
||||
cr := &DefaultCommandRunner{
|
||||
CommitStatusUpdater: csu,
|
||||
}
|
||||
cr.updateCommitStatus(&CommandContext{}, c.cmd, c.pullStatus)
|
||||
Equals(t, models.Repo{}, csu.CalledRepo)
|
||||
Equals(t, models.PullRequest{}, csu.CalledPull)
|
||||
Equals(t, c.expStatus, csu.CalledStatus)
|
||||
Equals(t, c.cmd, csu.CalledCommand)
|
||||
Equals(t, c.expNumSuccess, csu.CalledNumSuccess)
|
||||
Equals(t, c.expNumTotal, csu.CalledNumTotal)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type MockCSU struct {
|
||||
CalledRepo models.Repo
|
||||
CalledPull models.PullRequest
|
||||
CalledStatus models.CommitStatus
|
||||
CalledCommand models.CommandName
|
||||
CalledNumSuccess int
|
||||
CalledNumTotal int
|
||||
}
|
||||
|
||||
func (m *MockCSU) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error {
|
||||
m.CalledRepo = repo
|
||||
m.CalledPull = pull
|
||||
m.CalledStatus = status
|
||||
m.CalledCommand = command
|
||||
m.CalledNumSuccess = numSuccess
|
||||
m.CalledNumTotal = numTotal
|
||||
return nil
|
||||
}
|
||||
func (m *MockCSU) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCSU) UpdateProject(ctx models.ProjectCommandContext, cmdName models.CommandName, status models.CommitStatus, url string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
var projectCommandBuilder *mocks.MockProjectCommandBuilder
|
||||
var projectCommandRunner *mocks.MockProjectCommandRunner
|
||||
var eventParsing *mocks.MockEventParsing
|
||||
var ghStatus *mocks.MockCommitStatusUpdater
|
||||
var githubGetter *mocks.MockGithubPullGetter
|
||||
var gitlabGetter *mocks.MockGitlabMergeRequestGetter
|
||||
var ch events.DefaultCommandRunner
|
||||
@@ -48,7 +47,6 @@ func setup(t *testing.T) *vcsmocks.MockClient {
|
||||
RegisterMockTestingT(t)
|
||||
projectCommandBuilder = mocks.NewMockProjectCommandBuilder()
|
||||
eventParsing = mocks.NewMockEventParsing()
|
||||
ghStatus = mocks.NewMockCommitStatusUpdater()
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
githubGetter = mocks.NewMockGithubPullGetter()
|
||||
gitlabGetter = mocks.NewMockGitlabMergeRequestGetter()
|
||||
@@ -62,7 +60,7 @@ func setup(t *testing.T) *vcsmocks.MockClient {
|
||||
ThenReturn(pullLogger)
|
||||
ch = events.DefaultCommandRunner{
|
||||
VCSClient: vcsClient,
|
||||
CommitStatusUpdater: ghStatus,
|
||||
CommitStatusUpdater: &events.DefaultCommitStatusUpdater{vcsClient},
|
||||
EventParser: eventParsing,
|
||||
MarkdownRenderer: &events.MarkdownRenderer{},
|
||||
GithubPullGetter: githubGetter,
|
||||
|
||||
@@ -26,12 +26,12 @@ import (
|
||||
// CommitStatusUpdater updates the status of a commit with the VCS host. We set
|
||||
// the status to signify whether the plan/apply succeeds.
|
||||
type CommitStatusUpdater interface {
|
||||
// Update updates the status of the head commit of pull.
|
||||
Update(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error
|
||||
// UpdateProjectResult updates the status of the head commit given the
|
||||
// state of response.
|
||||
// todo: rename this so it doesn't conflict with UpdateProject
|
||||
UpdateProjectResult(ctx *CommandContext, commandName models.CommandName, res CommandResult) error
|
||||
// UpdateCombined updates the combined status of the head commit of pull.
|
||||
// A combined status represents all the projects modified in the pull.
|
||||
UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error
|
||||
// UpdateCombinedCount updates the combined status to reflect the
|
||||
// numSuccess out of numTotal.
|
||||
UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error
|
||||
// UpdateProject sets the commit status for the project represented by
|
||||
// ctx.
|
||||
UpdateProject(ctx models.ProjectCommandContext, cmdName models.CommandName, status models.CommitStatus, url string) error
|
||||
@@ -42,25 +42,28 @@ type DefaultCommitStatusUpdater struct {
|
||||
Client vcs.Client
|
||||
}
|
||||
|
||||
// Update updates the commit status.
|
||||
func (d *DefaultCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
|
||||
description := fmt.Sprintf("%s %s", strings.Title(command.String()), strings.Title(status.String()))
|
||||
return d.Client.UpdateStatus(repo, pull, status, "Atlantis", description, "")
|
||||
func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
|
||||
src := fmt.Sprintf("%s/atlantis", command.String())
|
||||
var descripWords string
|
||||
switch status {
|
||||
case models.PendingCommitStatus:
|
||||
descripWords = "in progress..."
|
||||
case models.FailedCommitStatus:
|
||||
descripWords = "failed."
|
||||
case models.SuccessCommitStatus:
|
||||
descripWords = "succeeded."
|
||||
}
|
||||
descrip := fmt.Sprintf("%s %s", strings.Title(command.String()), descripWords)
|
||||
return d.Client.UpdateStatus(repo, pull, status, src, descrip, "")
|
||||
}
|
||||
|
||||
// UpdateProjectResult updates the commit status based on the status of res.
|
||||
func (d *DefaultCommitStatusUpdater) UpdateProjectResult(ctx *CommandContext, commandName models.CommandName, res CommandResult) error {
|
||||
var status models.CommitStatus
|
||||
if res.Error != nil || res.Failure != "" {
|
||||
status = models.FailedCommitStatus
|
||||
} else {
|
||||
var statuses []models.CommitStatus
|
||||
for _, p := range res.ProjectResults {
|
||||
statuses = append(statuses, p.CommitStatus())
|
||||
}
|
||||
status = d.worstStatus(statuses)
|
||||
func (d *DefaultCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error {
|
||||
src := fmt.Sprintf("%s/atlantis", command.String())
|
||||
cmdVerb := "planned"
|
||||
if command == models.ApplyCommand {
|
||||
cmdVerb = "applied"
|
||||
}
|
||||
return d.Update(ctx.BaseRepo, ctx.Pull, status, commandName)
|
||||
return d.Client.UpdateStatus(repo, pull, status, src, fmt.Sprintf("%d/%d projects %s successfully.", numSuccess, numTotal, cmdVerb), "")
|
||||
}
|
||||
|
||||
func (d *DefaultCommitStatusUpdater) UpdateProject(ctx models.ProjectCommandContext, cmdName models.CommandName, status models.CommitStatus, url string) error {
|
||||
@@ -81,12 +84,3 @@ func (d *DefaultCommitStatusUpdater) UpdateProject(ctx models.ProjectCommandCont
|
||||
descrip := fmt.Sprintf("%s %s", strings.Title(cmdName.String()), descripWords)
|
||||
return d.Client.UpdateStatus(ctx.BaseRepo, ctx.Pull, status, src, descrip, url)
|
||||
}
|
||||
|
||||
func (d *DefaultCommitStatusUpdater) worstStatus(ss []models.CommitStatus) models.CommitStatus {
|
||||
for _, s := range ss {
|
||||
if s == models.FailedCommitStatus {
|
||||
return models.FailedCommitStatus
|
||||
}
|
||||
}
|
||||
return models.SuccessCommitStatus
|
||||
}
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/petergtz/pegomock"
|
||||
@@ -27,113 +25,120 @@ import (
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
var repoModel = models.Repo{}
|
||||
var pullModel = models.PullRequest{}
|
||||
var status = models.SuccessCommitStatus
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockClient()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.Update(repoModel, pullModel, status, models.PlanCommand)
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, status, "Atlantis", "Plan Success", "")
|
||||
}
|
||||
|
||||
func TestUpdateProjectResult_Error(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
}
|
||||
client := mocks.NewMockClient()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, models.PlanCommand, events.CommandResult{Error: errors.New("err")})
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, models.FailedCommitStatus, "Atlantis", "Plan Failed", "")
|
||||
}
|
||||
|
||||
func TestUpdateProjectResult_Failure(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
}
|
||||
client := mocks.NewMockClient()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, models.PlanCommand, events.CommandResult{Failure: "failure"})
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, models.FailedCommitStatus, "Atlantis", "Plan Failed", "")
|
||||
}
|
||||
|
||||
func TestUpdateProjectResult(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
}
|
||||
|
||||
func TestUpdateCombined(t *testing.T) {
|
||||
cases := []struct {
|
||||
Statuses []string
|
||||
Expected models.CommitStatus
|
||||
status models.CommitStatus
|
||||
command models.CommandName
|
||||
expDescrip string
|
||||
}{
|
||||
{
|
||||
[]string{"success", "failure", "error"},
|
||||
models.FailedCommitStatus,
|
||||
status: models.PendingCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
expDescrip: "Plan in progress...",
|
||||
},
|
||||
{
|
||||
[]string{"failure", "error", "success"},
|
||||
models.FailedCommitStatus,
|
||||
status: models.FailedCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
expDescrip: "Plan failed.",
|
||||
},
|
||||
{
|
||||
[]string{"success", "failure"},
|
||||
models.FailedCommitStatus,
|
||||
status: models.SuccessCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
expDescrip: "Plan succeeded.",
|
||||
},
|
||||
{
|
||||
[]string{"success", "error"},
|
||||
models.FailedCommitStatus,
|
||||
status: models.PendingCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
expDescrip: "Apply in progress...",
|
||||
},
|
||||
{
|
||||
[]string{"failure", "error"},
|
||||
models.FailedCommitStatus,
|
||||
status: models.FailedCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
expDescrip: "Apply failed.",
|
||||
},
|
||||
{
|
||||
[]string{"success"},
|
||||
models.SuccessCommitStatus,
|
||||
},
|
||||
{
|
||||
[]string{"success", "success"},
|
||||
models.SuccessCommitStatus,
|
||||
status: models.SuccessCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
expDescrip: "Apply succeeded.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(strings.Join(c.Statuses, "-"), func(t *testing.T) {
|
||||
var results []models.ProjectResult
|
||||
for _, statusStr := range c.Statuses {
|
||||
var result models.ProjectResult
|
||||
switch statusStr {
|
||||
case "failure":
|
||||
result = models.ProjectResult{
|
||||
Failure: "failure",
|
||||
}
|
||||
case "error":
|
||||
result = models.ProjectResult{
|
||||
Error: errors.New("err"),
|
||||
}
|
||||
default:
|
||||
result = models.ProjectResult{}
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
resp := events.CommandResult{ProjectResults: results}
|
||||
|
||||
t.Run(c.expDescrip, func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockClient()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, models.PlanCommand, resp)
|
||||
err := s.UpdateCombined(models.Repo{}, models.PullRequest{}, c.status, c.command)
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.Expected, "Atlantis", "Plan "+strings.Title(c.Expected.String()), "")
|
||||
|
||||
expSrc := fmt.Sprintf("%s/atlantis", c.command)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, expSrc, c.expDescrip, "")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCombinedCount(t *testing.T) {
|
||||
cases := []struct {
|
||||
status models.CommitStatus
|
||||
command models.CommandName
|
||||
numSuccess int
|
||||
numTotal int
|
||||
expDescrip string
|
||||
}{
|
||||
{
|
||||
status: models.PendingCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
numSuccess: 0,
|
||||
numTotal: 2,
|
||||
expDescrip: "0/2 projects planned successfully.",
|
||||
},
|
||||
{
|
||||
status: models.FailedCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
numSuccess: 1,
|
||||
numTotal: 2,
|
||||
expDescrip: "1/2 projects planned successfully.",
|
||||
},
|
||||
{
|
||||
status: models.SuccessCommitStatus,
|
||||
command: models.PlanCommand,
|
||||
numSuccess: 2,
|
||||
numTotal: 2,
|
||||
expDescrip: "2/2 projects planned successfully.",
|
||||
},
|
||||
{
|
||||
status: models.FailedCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
numSuccess: 0,
|
||||
numTotal: 2,
|
||||
expDescrip: "0/2 projects applied successfully.",
|
||||
},
|
||||
{
|
||||
status: models.PendingCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
numSuccess: 1,
|
||||
numTotal: 2,
|
||||
expDescrip: "1/2 projects applied successfully.",
|
||||
},
|
||||
{
|
||||
status: models.SuccessCommitStatus,
|
||||
command: models.ApplyCommand,
|
||||
numSuccess: 2,
|
||||
numTotal: 2,
|
||||
expDescrip: "2/2 projects applied successfully.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.expDescrip, func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockClient()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateCombinedCount(models.Repo{}, models.PullRequest{}, c.status, c.command, c.numSuccess, c.numTotal)
|
||||
Ok(t, err)
|
||||
|
||||
expSrc := fmt.Sprintf("%s/atlantis", c.command)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, expSrc, c.expDescrip, "")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -177,13 +182,13 @@ func TestDefaultCommitStatusUpdater_UpdateProjectSrc(t *testing.T) {
|
||||
models.PendingCommitStatus,
|
||||
"url")
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, models.PendingCommitStatus, c.expSrc, "Plan in progress...", "url")
|
||||
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, models.PendingCommitStatus, c.expSrc, "Plan in progress...", "url")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test that it uses the right words in the description.
|
||||
func TestDefaultCommitStatusUpdater_UpdateProjectStatus(t *testing.T) {
|
||||
func TestDefaultCommitStatusUpdater_UpdateProject(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
cases := []struct {
|
||||
status models.CommitStatus
|
||||
@@ -234,7 +239,7 @@ func TestDefaultCommitStatusUpdater_UpdateProjectStatus(t *testing.T) {
|
||||
c.status,
|
||||
"url")
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.status, fmt.Sprintf("%s/atlantis: ./default", c.cmd.String()), c.expDescrip, "url")
|
||||
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, fmt.Sprintf("%s/atlantis: ./default", c.cmd.String()), c.expDescrip, "url")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,6 +381,7 @@ func TestPullStatus_UpdateGet(t *testing.T) {
|
||||
pull,
|
||||
[]models.ProjectResult{
|
||||
{
|
||||
Command: models.PlanCommand,
|
||||
RepoRelDir: ".",
|
||||
Workspace: "default",
|
||||
Failure: "failure",
|
||||
@@ -600,11 +601,20 @@ func TestPullStatus_UpdateMerge(t *testing.T) {
|
||||
pull,
|
||||
[]models.ProjectResult{
|
||||
{
|
||||
Command: models.PlanCommand,
|
||||
RepoRelDir: "mergeme",
|
||||
Workspace: "default",
|
||||
Failure: "failure",
|
||||
},
|
||||
{
|
||||
Command: models.PlanCommand,
|
||||
RepoRelDir: "projectname",
|
||||
Workspace: "default",
|
||||
ProjectName: "projectname",
|
||||
Failure: "failure",
|
||||
},
|
||||
{
|
||||
Command: models.PlanCommand,
|
||||
RepoRelDir: "staythesame",
|
||||
Workspace: "default",
|
||||
PlanSuccess: &models.PlanSuccess{
|
||||
@@ -620,11 +630,20 @@ func TestPullStatus_UpdateMerge(t *testing.T) {
|
||||
updateStatus, err := b.UpdatePullWithResults(pull,
|
||||
[]models.ProjectResult{
|
||||
{
|
||||
Command: models.ApplyCommand,
|
||||
RepoRelDir: "mergeme",
|
||||
Workspace: "default",
|
||||
ApplySuccess: "applied!",
|
||||
},
|
||||
{
|
||||
Command: models.ApplyCommand,
|
||||
RepoRelDir: "projectname",
|
||||
Workspace: "default",
|
||||
ProjectName: "projectname",
|
||||
Error: errors.New("apply error"),
|
||||
},
|
||||
{
|
||||
Command: models.ApplyCommand,
|
||||
RepoRelDir: "newresult",
|
||||
Workspace: "default",
|
||||
ApplySuccess: "success!",
|
||||
@@ -645,6 +664,12 @@ func TestPullStatus_UpdateMerge(t *testing.T) {
|
||||
Workspace: "default",
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
{
|
||||
RepoRelDir: "projectname",
|
||||
Workspace: "default",
|
||||
ProjectName: "projectname",
|
||||
Status: models.ErroredApplyStatus,
|
||||
},
|
||||
{
|
||||
RepoRelDir: "staythesame",
|
||||
Workspace: "default",
|
||||
|
||||
@@ -5,7 +5,6 @@ package mocks
|
||||
|
||||
import (
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
"reflect"
|
||||
"time"
|
||||
@@ -19,12 +18,12 @@ func NewMockCommitStatusUpdater() *MockCommitStatusUpdater {
|
||||
return &MockCommitStatusUpdater{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
|
||||
func (mock *MockCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockCommitStatusUpdater().")
|
||||
}
|
||||
params := []pegomock.Param{repo, pull, status, command}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Update", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateCombined", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
@@ -34,12 +33,12 @@ func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRe
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, commandName models.CommandName, res events.CommandResult) error {
|
||||
func (mock *MockCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockCommitStatusUpdater().")
|
||||
}
|
||||
params := []pegomock.Param{ctx, commandName, res}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateProjectResult", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
params := []pegomock.Param{repo, pull, status, command, numSuccess, numTotal}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateCombinedCount", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
@@ -101,23 +100,23 @@ type VerifierCommitStatusUpdater struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) *CommitStatusUpdater_Update_OngoingVerification {
|
||||
func (verifier *VerifierCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) *CommitStatusUpdater_UpdateCombined_OngoingVerification {
|
||||
params := []pegomock.Param{repo, pull, status, command}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Update", params, verifier.timeout)
|
||||
return &CommitStatusUpdater_Update_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateCombined", params, verifier.timeout)
|
||||
return &CommitStatusUpdater_UpdateCombined_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type CommitStatusUpdater_Update_OngoingVerification struct {
|
||||
type CommitStatusUpdater_UpdateCombined_OngoingVerification struct {
|
||||
mock *MockCommitStatusUpdater
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, models.CommitStatus, models.CommandName) {
|
||||
func (c *CommitStatusUpdater_UpdateCombined_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, models.CommitStatus, models.CommandName) {
|
||||
repo, pull, status, command := c.GetAllCapturedArguments()
|
||||
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], command[len(command)-1]
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []models.CommitStatus, _param3 []models.CommandName) {
|
||||
func (c *CommitStatusUpdater_UpdateCombined_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []models.CommitStatus, _param3 []models.CommandName) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
@@ -140,36 +139,48 @@ func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, commandName models.CommandName, res events.CommandResult) *CommitStatusUpdater_UpdateProjectResult_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, commandName, res}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateProjectResult", params, verifier.timeout)
|
||||
return &CommitStatusUpdater_UpdateProjectResult_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
func (verifier *VerifierCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) *CommitStatusUpdater_UpdateCombinedCount_OngoingVerification {
|
||||
params := []pegomock.Param{repo, pull, status, command, numSuccess, numTotal}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateCombinedCount", params, verifier.timeout)
|
||||
return &CommitStatusUpdater_UpdateCombinedCount_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type CommitStatusUpdater_UpdateProjectResult_OngoingVerification struct {
|
||||
type CommitStatusUpdater_UpdateCombinedCount_OngoingVerification struct {
|
||||
mock *MockCommitStatusUpdater
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetCapturedArguments() (*events.CommandContext, models.CommandName, events.CommandResult) {
|
||||
ctx, commandName, res := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], commandName[len(commandName)-1], res[len(res)-1]
|
||||
func (c *CommitStatusUpdater_UpdateCombinedCount_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, models.CommitStatus, models.CommandName, int, int) {
|
||||
repo, pull, status, command, numSuccess, numTotal := c.GetAllCapturedArguments()
|
||||
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], command[len(command)-1], numSuccess[len(numSuccess)-1], numTotal[len(numTotal)-1]
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []models.CommandName, _param2 []events.CommandResult) {
|
||||
func (c *CommitStatusUpdater_UpdateCombinedCount_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []models.CommitStatus, _param3 []models.CommandName, _param4 []int, _param5 []int) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
_param0[u] = param.(models.Repo)
|
||||
}
|
||||
_param1 = make([]models.CommandName, len(params[1]))
|
||||
_param1 = make([]models.PullRequest, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(models.CommandName)
|
||||
_param1[u] = param.(models.PullRequest)
|
||||
}
|
||||
_param2 = make([]events.CommandResult, len(params[2]))
|
||||
_param2 = make([]models.CommitStatus, len(params[2]))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(events.CommandResult)
|
||||
_param2[u] = param.(models.CommitStatus)
|
||||
}
|
||||
_param3 = make([]models.CommandName, len(params[3]))
|
||||
for u, param := range params[3] {
|
||||
_param3[u] = param.(models.CommandName)
|
||||
}
|
||||
_param4 = make([]int, len(params[4]))
|
||||
for u, param := range params[4] {
|
||||
_param4[u] = param.(int)
|
||||
}
|
||||
_param5 = make([]int, len(params[5]))
|
||||
for u, param := range params[5] {
|
||||
_param5[u] = param.(int)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -405,6 +405,17 @@ type PullStatus struct {
|
||||
Pull PullRequest
|
||||
}
|
||||
|
||||
// StatusCount returns the number of projects that have status.
|
||||
func (p PullStatus) StatusCount(status ProjectPlanStatus) int {
|
||||
c := 0
|
||||
for _, pr := range p.Projects {
|
||||
if pr.Status == status {
|
||||
c++
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ProjectStatus is the status of a specific project.
|
||||
type ProjectStatus struct {
|
||||
Workspace string
|
||||
|
||||
@@ -294,3 +294,83 @@ func TestProjectResult_IsSuccessful(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectResult_PlanStatus(t *testing.T) {
|
||||
cases := []struct {
|
||||
p models.ProjectResult
|
||||
expStatus models.ProjectPlanStatus
|
||||
}{
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.PlanCommand,
|
||||
Error: errors.New("err"),
|
||||
},
|
||||
expStatus: models.ErroredPlanStatus,
|
||||
},
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.PlanCommand,
|
||||
Failure: "failure",
|
||||
},
|
||||
expStatus: models.ErroredPlanStatus,
|
||||
},
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.PlanCommand,
|
||||
PlanSuccess: &models.PlanSuccess{},
|
||||
},
|
||||
expStatus: models.PlannedPlanStatus,
|
||||
},
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.ApplyCommand,
|
||||
Error: errors.New("err"),
|
||||
},
|
||||
expStatus: models.ErroredApplyStatus,
|
||||
},
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.ApplyCommand,
|
||||
Failure: "failure",
|
||||
},
|
||||
expStatus: models.ErroredApplyStatus,
|
||||
},
|
||||
{
|
||||
p: models.ProjectResult{
|
||||
Command: models.ApplyCommand,
|
||||
ApplySuccess: "success",
|
||||
},
|
||||
expStatus: models.AppliedPlanStatus,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.expStatus.String(), func(t *testing.T) {
|
||||
Equals(t, c.expStatus, c.p.PlanStatus())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullStatus_StatusCount(t *testing.T) {
|
||||
ps := models.PullStatus{
|
||||
Projects: []models.ProjectStatus{
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.PlannedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.AppliedPlanStatus,
|
||||
},
|
||||
{
|
||||
Status: models.ErroredApplyStatus,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Equals(t, 2, ps.StatusCount(models.PlannedPlanStatus))
|
||||
Equals(t, 1, ps.StatusCount(models.AppliedPlanStatus))
|
||||
Equals(t, 1, ps.StatusCount(models.ErroredApplyStatus))
|
||||
Equals(t, 0, ps.StatusCount(models.ErroredPlanStatus))
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClient, *moc
|
||||
|
||||
// Mocks.
|
||||
e2eVCSClient := vcsmocks.NewMockClient()
|
||||
e2eStatusUpdater := mocks.NewMockCommitStatusUpdater()
|
||||
e2eStatusUpdater := &events.DefaultCommitStatusUpdater{Client: e2eVCSClient}
|
||||
e2eGithubGetter := mocks.NewMockGithubPullGetter()
|
||||
e2eGitlabGetter := mocks.NewMockGitlabMergeRequestGetter()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user