mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 00:58:17 +00:00
Test project command runner.
This commit is contained in:
20
server/events/mocks/matchers/webhooks_applyresult.go
Normal file
20
server/events/mocks/matchers/webhooks_applyresult.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
webhooks "github.com/runatlantis/atlantis/server/events/webhooks"
|
||||
)
|
||||
|
||||
func AnyWebhooksApplyResult() webhooks.ApplyResult {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(webhooks.ApplyResult))(nil)).Elem()))
|
||||
var nullValue webhooks.ApplyResult
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqWebhooksApplyResult(value webhooks.ApplyResult) webhooks.ApplyResult {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue webhooks.ApplyResult
|
||||
return nullValue
|
||||
}
|
||||
88
server/events/mocks/mock_step_runner.go
Normal file
88
server/events/mocks/mock_step_runner.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Automatically generated by pegomock. DO NOT EDIT!
|
||||
// Source: github.com/runatlantis/atlantis/server/events (interfaces: StepRunner)
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
type MockStepRunner struct {
|
||||
fail func(message string, callerSkip ...int)
|
||||
}
|
||||
|
||||
func NewMockStepRunner() *MockStepRunner {
|
||||
return &MockStepRunner{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
params := []pegomock.Param{ctx, extraArgs, path}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Run", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 string
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(string)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockStepRunner) VerifyWasCalledOnce() *VerifierStepRunner {
|
||||
return &VerifierStepRunner{mock, pegomock.Times(1), nil}
|
||||
}
|
||||
|
||||
func (mock *MockStepRunner) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierStepRunner {
|
||||
return &VerifierStepRunner{mock, invocationCountMatcher, nil}
|
||||
}
|
||||
|
||||
func (mock *MockStepRunner) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierStepRunner {
|
||||
return &VerifierStepRunner{mock, invocationCountMatcher, inOrderContext}
|
||||
}
|
||||
|
||||
type VerifierStepRunner struct {
|
||||
mock *MockStepRunner
|
||||
invocationCountMatcher pegomock.Matcher
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) *StepRunner_Run_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, extraArgs, path}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params)
|
||||
return &StepRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type StepRunner_Run_OngoingVerification struct {
|
||||
mock *MockStepRunner
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *StepRunner_Run_OngoingVerification) GetCapturedArguments() (models.ProjectCommandContext, []string, string) {
|
||||
ctx, extraArgs, path := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], extraArgs[len(extraArgs)-1], path[len(path)-1]
|
||||
}
|
||||
|
||||
func (c *StepRunner_Run_OngoingVerification) GetAllCapturedArguments() (_param0 []models.ProjectCommandContext, _param1 [][]string, _param2 []string) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.ProjectCommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(models.ProjectCommandContext)
|
||||
}
|
||||
_param1 = make([][]string, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.([]string)
|
||||
}
|
||||
_param2 = make([]string, len(params[2]))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(string)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
81
server/events/mocks/mock_webhooks_sender.go
Normal file
81
server/events/mocks/mock_webhooks_sender.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Automatically generated by pegomock. DO NOT EDIT!
|
||||
// Source: github.com/runatlantis/atlantis/server/events (interfaces: WebhooksSender)
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
webhooks "github.com/runatlantis/atlantis/server/events/webhooks"
|
||||
logging "github.com/runatlantis/atlantis/server/logging"
|
||||
)
|
||||
|
||||
type MockWebhooksSender struct {
|
||||
fail func(message string, callerSkip ...int)
|
||||
}
|
||||
|
||||
func NewMockWebhooksSender() *MockWebhooksSender {
|
||||
return &MockWebhooksSender{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockWebhooksSender) Send(log *logging.SimpleLogger, res webhooks.ApplyResult) error {
|
||||
params := []pegomock.Param{log, res}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Send", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(error)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockWebhooksSender) VerifyWasCalledOnce() *VerifierWebhooksSender {
|
||||
return &VerifierWebhooksSender{mock, pegomock.Times(1), nil}
|
||||
}
|
||||
|
||||
func (mock *MockWebhooksSender) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierWebhooksSender {
|
||||
return &VerifierWebhooksSender{mock, invocationCountMatcher, nil}
|
||||
}
|
||||
|
||||
func (mock *MockWebhooksSender) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierWebhooksSender {
|
||||
return &VerifierWebhooksSender{mock, invocationCountMatcher, inOrderContext}
|
||||
}
|
||||
|
||||
type VerifierWebhooksSender struct {
|
||||
mock *MockWebhooksSender
|
||||
invocationCountMatcher pegomock.Matcher
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierWebhooksSender) Send(log *logging.SimpleLogger, res webhooks.ApplyResult) *WebhooksSender_Send_OngoingVerification {
|
||||
params := []pegomock.Param{log, res}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Send", params)
|
||||
return &WebhooksSender_Send_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type WebhooksSender_Send_OngoingVerification struct {
|
||||
mock *MockWebhooksSender
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *WebhooksSender_Send_OngoingVerification) GetCapturedArguments() (*logging.SimpleLogger, webhooks.ApplyResult) {
|
||||
log, res := c.GetAllCapturedArguments()
|
||||
return log[len(log)-1], res[len(res)-1]
|
||||
}
|
||||
|
||||
func (c *WebhooksSender_Send_OngoingVerification) GetAllCapturedArguments() (_param0 []*logging.SimpleLogger, _param1 []webhooks.ApplyResult) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*logging.SimpleLogger, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*logging.SimpleLogger)
|
||||
}
|
||||
_param1 = make([]webhooks.ApplyResult, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(webhooks.ApplyResult)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -238,7 +238,6 @@ type ProjectCommandContext struct {
|
||||
|
||||
// CommentArgs are the extra arguments appended to comment,
|
||||
// ex. atlantis plan -- -target=resource
|
||||
CommentArgs []string
|
||||
Workspace string
|
||||
RequireApprovalOverride bool
|
||||
CommentArgs []string
|
||||
Workspace string
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ type DefaultProjectCommandBuilder struct {
|
||||
VCSClient vcs.ClientProxy
|
||||
WorkingDir WorkingDir
|
||||
WorkingDirLocker WorkingDirLocker
|
||||
RequireApproval bool
|
||||
AllowRepoConfig bool
|
||||
AllowRepoConfigFlag string
|
||||
}
|
||||
@@ -180,17 +179,16 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(ctx *CommandContex
|
||||
}
|
||||
|
||||
return models.ProjectCommandContext{
|
||||
BaseRepo: ctx.BaseRepo,
|
||||
HeadRepo: ctx.HeadRepo,
|
||||
Pull: ctx.Pull,
|
||||
User: ctx.User,
|
||||
Log: ctx.Log,
|
||||
CommentArgs: cmd.Flags,
|
||||
Workspace: workspace,
|
||||
RepoRelDir: dir,
|
||||
ProjectConfig: projCfg,
|
||||
GlobalConfig: globalCfg,
|
||||
RequireApprovalOverride: p.RequireApproval,
|
||||
BaseRepo: ctx.BaseRepo,
|
||||
HeadRepo: ctx.HeadRepo,
|
||||
Pull: ctx.Pull,
|
||||
User: ctx.User,
|
||||
Log: ctx.Log,
|
||||
CommentArgs: cmd.Flags,
|
||||
Workspace: workspace,
|
||||
RepoRelDir: dir,
|
||||
ProjectConfig: projCfg,
|
||||
GlobalConfig: globalCfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ projects:
|
||||
When(vcsClient.GetModifiedFiles(baseRepo, pull)).ThenReturn([]string{"main.tf"}, nil)
|
||||
|
||||
builder := &events.DefaultProjectCommandBuilder{
|
||||
WorkingDirLocker: events.NewDefaultAtlantisWorkingDirLocker(),
|
||||
WorkingDirLocker: events.NewDefaultWorkingDirLocker(),
|
||||
WorkingDir: workingDir,
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
VCSClient: vcsClient,
|
||||
@@ -216,7 +216,6 @@ projects:
|
||||
Equals(t, models.User{}, actCtx.User)
|
||||
Equals(t, logger, actCtx.Log)
|
||||
Equals(t, 0, len(actCtx.CommentArgs))
|
||||
Equals(t, false, actCtx.RequireApprovalOverride)
|
||||
|
||||
Equals(t, expCtx.projectConfig, actCtx.ProjectConfig)
|
||||
Equals(t, expCtx.dir, actCtx.RepoRelDir)
|
||||
@@ -412,7 +411,7 @@ projects:
|
||||
When(vcsClient.GetModifiedFiles(baseRepo, pull)).ThenReturn([]string{"main.tf"}, nil)
|
||||
|
||||
builder := &events.DefaultProjectCommandBuilder{
|
||||
WorkingDirLocker: events.NewDefaultAtlantisWorkingDirLocker(),
|
||||
WorkingDirLocker: events.NewDefaultWorkingDirLocker(),
|
||||
WorkingDir: workingDir,
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
VCSClient: vcsClient,
|
||||
@@ -446,7 +445,6 @@ projects:
|
||||
Equals(t, pull, actCtx.Pull)
|
||||
Equals(t, models.User{}, actCtx.User)
|
||||
Equals(t, logger, actCtx.Log)
|
||||
Equals(t, false, actCtx.RequireApprovalOverride)
|
||||
|
||||
Equals(t, c.ExpProjectConfig, actCtx.ProjectConfig)
|
||||
Equals(t, c.ExpDir, actCtx.RepoRelDir)
|
||||
|
||||
@@ -34,8 +34,16 @@ type LockURLGenerator interface {
|
||||
GenerateLockURL(lockID string) string
|
||||
}
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_step_runner.go StepRunner
|
||||
|
||||
type StepRunner interface {
|
||||
Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error)
|
||||
}
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_webhooks_sender.go WebhooksSender
|
||||
|
||||
type WebhooksSender interface {
|
||||
Send(log *logging.SimpleLogger, result webhooks.ApplyResult) error
|
||||
Send(log *logging.SimpleLogger, res webhooks.ApplyResult) error
|
||||
}
|
||||
|
||||
// PlanSuccess is the result of a successful plan.
|
||||
@@ -52,16 +60,17 @@ type ProjectCommandRunner interface {
|
||||
}
|
||||
|
||||
type DefaultProjectCommandRunner struct {
|
||||
Locker ProjectLocker
|
||||
LockURLGenerator LockURLGenerator
|
||||
InitStepRunner runtime.InitStepRunner
|
||||
PlanStepRunner runtime.PlanStepRunner
|
||||
ApplyStepRunner runtime.ApplyStepRunner
|
||||
RunStepRunner runtime.RunStepRunner
|
||||
PullApprovedChecker runtime.PullApprovedChecker
|
||||
WorkingDir WorkingDir
|
||||
Webhooks WebhooksSender
|
||||
WorkingDirLocker WorkingDirLocker
|
||||
Locker ProjectLocker
|
||||
LockURLGenerator LockURLGenerator
|
||||
InitStepRunner StepRunner
|
||||
PlanStepRunner StepRunner
|
||||
ApplyStepRunner StepRunner
|
||||
RunStepRunner StepRunner
|
||||
PullApprovedChecker runtime.PullApprovedChecker
|
||||
WorkingDir WorkingDir
|
||||
Webhooks WebhooksSender
|
||||
WorkingDirLocker WorkingDirLocker
|
||||
RequireApprovalOverride bool
|
||||
}
|
||||
|
||||
func (p *DefaultProjectCommandRunner) Plan(ctx models.ProjectCommandContext) ProjectCommandResult {
|
||||
@@ -160,7 +169,7 @@ func (p *DefaultProjectCommandRunner) Apply(ctx models.ProjectCommandContext) Pr
|
||||
if ctx.ProjectConfig != nil {
|
||||
applyRequirements = ctx.ProjectConfig.ApplyRequirements
|
||||
}
|
||||
if ctx.RequireApprovalOverride {
|
||||
if p.RequireApprovalOverride {
|
||||
applyRequirements = []string{raw.ApprovedApplyRequirement}
|
||||
}
|
||||
for _, req := range applyRequirements {
|
||||
|
||||
@@ -13,277 +13,402 @@
|
||||
//
|
||||
package events_test
|
||||
|
||||
//
|
||||
//import (
|
||||
// "errors"
|
||||
// "testing"
|
||||
//
|
||||
// "github.com/mohae/deepcopy"
|
||||
// . "github.com/petergtz/pegomock"
|
||||
// "github.com/runatlantis/atlantis/server/events"
|
||||
// "github.com/runatlantis/atlantis/server/events/locking"
|
||||
// lmocks "github.com/runatlantis/atlantis/server/events/locking/mocks"
|
||||
// "github.com/runatlantis/atlantis/server/events/mocks"
|
||||
// "github.com/runatlantis/atlantis/server/events/models"
|
||||
// rmocks "github.com/runatlantis/atlantis/server/events/run/mocks"
|
||||
// tmocks "github.com/runatlantis/atlantis/server/events/terraform/mocks"
|
||||
// vcsmocks "github.com/runatlantis/atlantis/server/events/vcs/mocks"
|
||||
// "github.com/runatlantis/atlantis/server/events/vcs/mocks/matchers"
|
||||
// "github.com/runatlantis/atlantis/server/logging"
|
||||
// . "github.com/runatlantis/atlantis/testing"
|
||||
//)
|
||||
//
|
||||
//var planCtx = events.CommandContext{
|
||||
// Command: &events.Command{
|
||||
// Name: events.Plan,
|
||||
// Workspace: "workspace",
|
||||
// RepoRelDir: "",
|
||||
// },
|
||||
// Log: logging.NewNoopLogger(),
|
||||
// BaseRepo: models.Repo{},
|
||||
// HeadRepo: models.Repo{},
|
||||
// Pull: models.PullRequest{},
|
||||
// User: models.User{
|
||||
// Username: "anubhavmishra",
|
||||
// },
|
||||
//}
|
||||
//
|
||||
//func TestExecute_ModifiedFilesErr(t *testing.T) {
|
||||
// t.Log("If GetModifiedFiles returns an error we return an error")
|
||||
// p, _, _ := setupPlanExecutorTest(t)
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(nil, errors.New("err"))
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// Assert(t, r.Error != nil, "exp .Error to be set")
|
||||
// Equals(t, "getting modified files: err", r.Error.Error())
|
||||
//}
|
||||
//
|
||||
//func TestExecute_NoModifiedProjects(t *testing.T) {
|
||||
// t.Log("If there are no modified projects we return a failure")
|
||||
// p, _, _ := setupPlanExecutorTest(t)
|
||||
// // We don't need to actually mock VCSClient.GetModifiedFiles because by
|
||||
// // default it will return an empty slice which is what we want for this test.
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// Equals(t, "No Terraform files were modified.", r.Failure)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_CloneErr(t *testing.T) {
|
||||
// t.Log("If WorkingDir.Clone returns an error we return an error")
|
||||
// p, _, _ := setupPlanExecutorTest(t)
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "workspace")).ThenReturn("", errors.New("err"))
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// Assert(t, r.Error != nil, "exp .Error to be set")
|
||||
// Equals(t, "err", r.Error.Error())
|
||||
//}
|
||||
//
|
||||
//func TestExecute_DirectoryAndWorkspaceSet(t *testing.T) {
|
||||
// t.Log("Test that we run plan in the right directory and workspace if they're set")
|
||||
// p, runner, _ := setupPlanExecutorTest(t)
|
||||
// ctx := deepcopy.Copy(planCtx).(events.CommandContext)
|
||||
// ctx.Log = logging.NewNoopLogger()
|
||||
// ctx.Command.RepoRelDir = "dir1/dir2"
|
||||
// ctx.Command.Workspace = "workspace-flag"
|
||||
//
|
||||
// When(p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, "workspace-flag")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
// When(p.ProjectPreExecute.Execute(&ctx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "dir1/dir2"})).
|
||||
// ThenReturn(events.PreExecuteResult{
|
||||
// LockResponse: locking.TryLockResponse{
|
||||
// LockKey: "key",
|
||||
// },
|
||||
// })
|
||||
// r := p.Execute(&ctx)
|
||||
//
|
||||
// runner.VerifyWasCalledOnce().RunCommandWithVersion(
|
||||
// ctx.Log,
|
||||
// "/tmp/clone-repo/dir1/dir2",
|
||||
// []string{"plan", "-refresh", "-no-color", "-out", "/tmp/clone-repo/dir1/dir2/workspace-flag.tfplan", "-var", "atlantis_user=anubhavmishra"},
|
||||
// nil,
|
||||
// "workspace-flag",
|
||||
// )
|
||||
// Assert(t, len(r.ProjectResults) == 1, "exp one project result")
|
||||
// result := r.ProjectResults[0]
|
||||
// Assert(t, result.PlanSuccess != nil, "exp plan success to not be nil")
|
||||
// Equals(t, "", result.PlanSuccess.TerraformOutput)
|
||||
// Equals(t, "lockurl-key", result.PlanSuccess.LockURL)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_AddedArgs(t *testing.T) {
|
||||
// t.Log("Test that we include extra-args added to the comment in the plan command")
|
||||
// p, runner, _ := setupPlanExecutorTest(t)
|
||||
// ctx := deepcopy.Copy(planCtx).(events.CommandContext)
|
||||
// ctx.Log = logging.NewNoopLogger()
|
||||
// ctx.Command.CommentArgs = []string{"\"-target=resource\"", "\"-var\"", "\"a=b\"", "\";\"", "\"echo\"", "\"hi\""}
|
||||
//
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, "workspace")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
// When(p.ProjectPreExecute.Execute(&ctx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "."})).
|
||||
// ThenReturn(events.PreExecuteResult{
|
||||
// LockResponse: locking.TryLockResponse{
|
||||
// LockKey: "key",
|
||||
// },
|
||||
// })
|
||||
// r := p.Execute(&ctx)
|
||||
//
|
||||
// runner.VerifyWasCalledOnce().RunCommandWithVersion(
|
||||
// ctx.Log,
|
||||
// "/tmp/clone-repo",
|
||||
// []string{
|
||||
// "plan",
|
||||
// "-refresh",
|
||||
// "-no-color",
|
||||
// "-out",
|
||||
// "/tmp/clone-repo/workspace.tfplan",
|
||||
// "-var",
|
||||
// "atlantis_user=anubhavmishra",
|
||||
// // NOTE: extra args should be quoted to prevent an attacker from
|
||||
// // appending malicious commands.
|
||||
// "\"-target=resource\"",
|
||||
// "\"-var\"",
|
||||
// "\"a=b\"",
|
||||
// "\";\"",
|
||||
// "\"echo\"",
|
||||
// "\"hi\"",
|
||||
// },
|
||||
// nil,
|
||||
// "workspace",
|
||||
// )
|
||||
// Assert(t, len(r.ProjectResults) == 1, "exp one project result")
|
||||
// result := r.ProjectResults[0]
|
||||
// Assert(t, result.PlanSuccess != nil, "exp plan success to not be nil")
|
||||
// Equals(t, "", result.PlanSuccess.TerraformOutput)
|
||||
// Equals(t, "lockurl-key", result.PlanSuccess.LockURL)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_Success(t *testing.T) {
|
||||
// t.Log("If there are no errors, the plan should be returned")
|
||||
// p, runner, _ := setupPlanExecutorTest(t)
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "workspace")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
// When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "."})).
|
||||
// ThenReturn(events.PreExecuteResult{
|
||||
// LockResponse: locking.TryLockResponse{
|
||||
// LockKey: "key",
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// runner.VerifyWasCalledOnce().RunCommandWithVersion(
|
||||
// planCtx.Log,
|
||||
// "/tmp/clone-repo",
|
||||
// []string{"plan", "-refresh", "-no-color", "-out", "/tmp/clone-repo/workspace.tfplan", "-var", "atlantis_user=anubhavmishra"},
|
||||
// nil,
|
||||
// "workspace",
|
||||
// )
|
||||
// Assert(t, len(r.ProjectResults) == 1, "exp one project result")
|
||||
// result := r.ProjectResults[0]
|
||||
// Assert(t, result.PlanSuccess != nil, "exp plan success to not be nil")
|
||||
// Equals(t, "", result.PlanSuccess.TerraformOutput)
|
||||
// Equals(t, "lockurl-key", result.PlanSuccess.LockURL)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_PreExecuteResult(t *testing.T) {
|
||||
// t.Log("If DefaultProjectPreExecutor.Execute returns a ProjectResult we should return it")
|
||||
// p, _, _ := setupPlanExecutorTest(t)
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "workspace")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
// projectResult := events.ProjectResult{
|
||||
// Failure: "failure",
|
||||
// }
|
||||
// When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "."})).
|
||||
// ThenReturn(events.PreExecuteResult{ProjectResult: projectResult})
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// Assert(t, len(r.ProjectResults) == 1, "exp one project result")
|
||||
// result := r.ProjectResults[0]
|
||||
// Equals(t, "failure", result.Failure)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_MultiProjectFailure(t *testing.T) {
|
||||
// t.Log("If is an error planning in one project it should be returned. It shouldn't affect another project though.")
|
||||
// p, runner, locker := setupPlanExecutorTest(t)
|
||||
// // Two projects have been modified so we should run plan in two paths.
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"path1/file.tf", "path2/file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "workspace")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
//
|
||||
// // Both projects will succeed in the PreExecute stage.
|
||||
// When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "path1"})).
|
||||
// ThenReturn(events.PreExecuteResult{LockResponse: locking.TryLockResponse{LockKey: "key1"}})
|
||||
// When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "path2"})).
|
||||
// ThenReturn(events.PreExecuteResult{LockResponse: locking.TryLockResponse{LockKey: "key2"}})
|
||||
//
|
||||
// // The first project will fail when running plan
|
||||
// When(runner.RunCommandWithVersion(
|
||||
// planCtx.Log,
|
||||
// "/tmp/clone-repo/path1",
|
||||
// []string{"plan", "-refresh", "-no-color", "-out", "/tmp/clone-repo/path1/workspace.tfplan", "-var", "atlantis_user=anubhavmishra"},
|
||||
// nil,
|
||||
// "workspace",
|
||||
// )).ThenReturn("", errors.New("path1 err"))
|
||||
// // The second will succeed. We don't need to stub it because by default it
|
||||
// // will return a nil error.
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// // We expect Unlock to be called for the failed project.
|
||||
// locker.VerifyWasCalledOnce().Unlock("key1")
|
||||
//
|
||||
// // So at the end we expect the first project to return an error and the second to be successful.
|
||||
// Assert(t, len(r.ProjectResults) == 2, "exp two project results")
|
||||
// result1 := r.ProjectResults[0]
|
||||
// Assert(t, result1.Error != nil, "exp err to not be nil")
|
||||
// Equals(t, "path1 err\n", result1.Error.Error())
|
||||
//
|
||||
// result2 := r.ProjectResults[1]
|
||||
// Assert(t, result2.PlanSuccess != nil, "exp plan success to not be nil")
|
||||
// Equals(t, "", result2.PlanSuccess.TerraformOutput)
|
||||
// Equals(t, "lockurl-key2", result2.PlanSuccess.LockURL)
|
||||
//}
|
||||
//
|
||||
//func TestExecute_PostPlanCommands(t *testing.T) {
|
||||
// t.Log("Should execute post-plan commands and return if there is an error")
|
||||
// p, _, _ := setupPlanExecutorTest(t)
|
||||
// When(p.VCSClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"file.tf"}, nil)
|
||||
// When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "workspace")).
|
||||
// ThenReturn("/tmp/clone-repo", nil)
|
||||
// When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", RepoRelDir: "."})).
|
||||
// ThenReturn(events.PreExecuteResult{
|
||||
// ProjectConfig: events.ProjectConfig{PostPlan: []string{"post-plan"}},
|
||||
// })
|
||||
// When(p.Run.Execute(planCtx.Log, []string{"post-plan"}, "/tmp/clone-repo", "workspace", nil, "post_plan")).
|
||||
// ThenReturn("", errors.New("err"))
|
||||
//
|
||||
// r := p.Execute(&planCtx)
|
||||
//
|
||||
// Assert(t, len(r.ProjectResults) == 1, "exp one project result")
|
||||
// result := r.ProjectResults[0]
|
||||
// Assert(t, result.Error != nil, "exp plan error to not be nil")
|
||||
// Equals(t, "running post plan commands: err", result.Error.Error())
|
||||
//}
|
||||
//
|
||||
//func setupPlanExecutorTest(t *testing.T) (*events.PlanExecutor, *tmocks.MockClient, *lmocks.MockLocker) {
|
||||
// RegisterMockTestingT(t)
|
||||
// vcsProxy := vcsmocks.NewMockClientProxy()
|
||||
// w := mocks.NewMockWorkingDir()
|
||||
// ppe := mocks.NewMockProjectPreExecutor()
|
||||
// runner := tmocks.NewMockClient()
|
||||
// locker := lmocks.NewMockLocker()
|
||||
// run := rmocks.NewMockRunner()
|
||||
// p := events.PlanExecutor{
|
||||
// VCSClient: vcsProxy,
|
||||
// ProjectFinder: &events.DefaultProjectFinder{},
|
||||
// Workspace: w,
|
||||
// ProjectPreExecute: ppe,
|
||||
// Terraform: runner,
|
||||
// Locker: locker,
|
||||
// Run: run,
|
||||
// }
|
||||
// return &p, runner, locker
|
||||
//}
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/petergtz/pegomock"
|
||||
"github.com/runatlantis/atlantis/server/events"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks/matchers"
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
mocks2 "github.com/runatlantis/atlantis/server/events/runtime/mocks"
|
||||
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
func TestDefaultProjectCommandRunner_Plan(t *testing.T) {
|
||||
cases := []struct {
|
||||
description string
|
||||
projCfg *valid.Project
|
||||
globalCfg *valid.Config
|
||||
expSteps []string
|
||||
expOut string
|
||||
}{
|
||||
{
|
||||
description: "use defaults",
|
||||
projCfg: nil,
|
||||
globalCfg: nil,
|
||||
expSteps: []string{"init", "plan"},
|
||||
expOut: "init\nplan",
|
||||
},
|
||||
{
|
||||
description: "no workflow, use defaults",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"init", "plan"},
|
||||
expOut: "init\nplan",
|
||||
},
|
||||
{
|
||||
description: "workflow without plan stage set",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
Workflow: String("myworkflow"),
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
"myworkflow": {
|
||||
Apply: &valid.Stage{
|
||||
Steps: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"init", "plan"},
|
||||
expOut: "init\nplan",
|
||||
},
|
||||
{
|
||||
description: "workflow with custom plan stage",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
Workflow: String("myworkflow"),
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
"myworkflow": {
|
||||
Plan: &valid.Stage{
|
||||
Steps: []valid.Step{
|
||||
{
|
||||
StepName: "run",
|
||||
},
|
||||
{
|
||||
StepName: "apply",
|
||||
},
|
||||
{
|
||||
StepName: "plan",
|
||||
},
|
||||
{
|
||||
StepName: "init",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"run", "apply", "plan", "init"},
|
||||
expOut: "run\napply\nplan\ninit",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(strings.Join(c.expSteps, ","), func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
mockInit := mocks.NewMockStepRunner()
|
||||
mockPlan := mocks.NewMockStepRunner()
|
||||
mockApply := mocks.NewMockStepRunner()
|
||||
mockRun := mocks.NewMockStepRunner()
|
||||
mockWorkingDir := mocks.NewMockWorkingDir()
|
||||
mockLocker := mocks.NewMockProjectLocker()
|
||||
|
||||
runner := events.DefaultProjectCommandRunner{
|
||||
Locker: mockLocker,
|
||||
LockURLGenerator: mockURLGenerator{},
|
||||
InitStepRunner: mockInit,
|
||||
PlanStepRunner: mockPlan,
|
||||
ApplyStepRunner: mockApply,
|
||||
RunStepRunner: mockRun,
|
||||
PullApprovedChecker: nil,
|
||||
WorkingDir: mockWorkingDir,
|
||||
Webhooks: nil,
|
||||
WorkingDirLocker: events.NewDefaultWorkingDirLocker(),
|
||||
}
|
||||
|
||||
repoDir := "/tmp/mydir"
|
||||
When(mockWorkingDir.Clone(
|
||||
matchers.AnyPtrToLoggingSimpleLogger(),
|
||||
matchers.AnyModelsRepo(),
|
||||
matchers.AnyModelsRepo(),
|
||||
matchers.AnyModelsPullRequest(),
|
||||
AnyString(),
|
||||
)).ThenReturn(repoDir, nil)
|
||||
When(mockLocker.TryLock(
|
||||
matchers.AnyPtrToLoggingSimpleLogger(),
|
||||
matchers.AnyModelsPullRequest(),
|
||||
matchers.AnyModelsUser(),
|
||||
AnyString(),
|
||||
matchers.AnyModelsProject(),
|
||||
)).ThenReturn(&events.TryLockResponse{
|
||||
LockAcquired: true,
|
||||
LockKey: "lock-key",
|
||||
}, nil)
|
||||
|
||||
ctx := models.ProjectCommandContext{
|
||||
Log: logging.NewNoopLogger(),
|
||||
ProjectConfig: c.projCfg,
|
||||
Workspace: "default",
|
||||
GlobalConfig: c.globalCfg,
|
||||
RepoRelDir: ".",
|
||||
}
|
||||
When(mockInit.Run(ctx, nil, repoDir)).ThenReturn("init", nil)
|
||||
When(mockPlan.Run(ctx, nil, repoDir)).ThenReturn("plan", nil)
|
||||
When(mockApply.Run(ctx, nil, repoDir)).ThenReturn("apply", nil)
|
||||
When(mockRun.Run(ctx, nil, repoDir)).ThenReturn("run", nil)
|
||||
|
||||
res := runner.Plan(ctx)
|
||||
|
||||
Assert(t, res.PlanSuccess != nil, "exp plan success")
|
||||
Equals(t, "https://lock-key", res.PlanSuccess.LockURL)
|
||||
Equals(t, c.expOut, res.PlanSuccess.TerraformOutput)
|
||||
|
||||
for _, step := range c.expSteps {
|
||||
switch step {
|
||||
case "init":
|
||||
mockInit.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "plan":
|
||||
mockPlan.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "apply":
|
||||
mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "run":
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandRunner_ApplyNotCloned(t *testing.T) {
|
||||
mockWorkingDir := mocks.NewMockWorkingDir()
|
||||
runner := &events.DefaultProjectCommandRunner{
|
||||
WorkingDir: mockWorkingDir,
|
||||
}
|
||||
ctx := models.ProjectCommandContext{}
|
||||
When(mockWorkingDir.GetWorkingDir(ctx.BaseRepo, ctx.Pull, ctx.Workspace)).ThenReturn("", os.ErrNotExist)
|
||||
|
||||
res := runner.Apply(ctx)
|
||||
ErrEquals(t, "project has not been cloned–did you run plan?", res.Error)
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandRunner_ApplyNotApproved(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
mockWorkingDir := mocks.NewMockWorkingDir()
|
||||
mockApproved := mocks2.NewMockPullApprovedChecker()
|
||||
runner := &events.DefaultProjectCommandRunner{
|
||||
WorkingDir: mockWorkingDir,
|
||||
PullApprovedChecker: mockApproved,
|
||||
WorkingDirLocker: events.NewDefaultWorkingDirLocker(),
|
||||
RequireApprovalOverride: true,
|
||||
}
|
||||
ctx := models.ProjectCommandContext{}
|
||||
When(mockWorkingDir.GetWorkingDir(ctx.BaseRepo, ctx.Pull, ctx.Workspace)).ThenReturn("/tmp/mydir", nil)
|
||||
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(false, nil)
|
||||
|
||||
res := runner.Apply(ctx)
|
||||
Equals(t, "Pull request must be approved before running apply.", res.Failure)
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandRunner_Apply(t *testing.T) {
|
||||
cases := []struct {
|
||||
description string
|
||||
projCfg *valid.Project
|
||||
globalCfg *valid.Config
|
||||
expSteps []string
|
||||
expOut string
|
||||
}{
|
||||
{
|
||||
description: "use defaults",
|
||||
projCfg: nil,
|
||||
globalCfg: nil,
|
||||
expSteps: []string{"apply"},
|
||||
expOut: "apply",
|
||||
},
|
||||
{
|
||||
description: "no workflow, use defaults",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"apply"},
|
||||
expOut: "apply",
|
||||
},
|
||||
{
|
||||
description: "no workflow, approval required, use defaults",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
ApplyRequirements: []string{"approved"},
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
ApplyRequirements: []string{"approved"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"approved", "apply"},
|
||||
expOut: "apply",
|
||||
},
|
||||
{
|
||||
description: "workflow without apply stage set",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
Workflow: String("myworkflow"),
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
"myworkflow": {
|
||||
Plan: &valid.Stage{
|
||||
Steps: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"apply"},
|
||||
expOut: "apply",
|
||||
},
|
||||
{
|
||||
description: "workflow with custom apply stage",
|
||||
projCfg: &valid.Project{
|
||||
Dir: ".",
|
||||
Workflow: String("myworkflow"),
|
||||
},
|
||||
globalCfg: &valid.Config{
|
||||
Version: 2,
|
||||
Projects: []valid.Project{
|
||||
{
|
||||
Dir: ".",
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
"myworkflow": {
|
||||
Apply: &valid.Stage{
|
||||
Steps: []valid.Step{
|
||||
{
|
||||
StepName: "run",
|
||||
},
|
||||
{
|
||||
StepName: "apply",
|
||||
},
|
||||
{
|
||||
StepName: "plan",
|
||||
},
|
||||
{
|
||||
StepName: "init",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSteps: []string{"run", "apply", "plan", "init"},
|
||||
expOut: "run\napply\nplan\ninit",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(strings.Join(c.expSteps, ","), func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
mockInit := mocks.NewMockStepRunner()
|
||||
mockPlan := mocks.NewMockStepRunner()
|
||||
mockApply := mocks.NewMockStepRunner()
|
||||
mockRun := mocks.NewMockStepRunner()
|
||||
mockApproved := mocks2.NewMockPullApprovedChecker()
|
||||
mockWorkingDir := mocks.NewMockWorkingDir()
|
||||
mockLocker := mocks.NewMockProjectLocker()
|
||||
mockSender := mocks.NewMockWebhooksSender()
|
||||
|
||||
runner := events.DefaultProjectCommandRunner{
|
||||
Locker: mockLocker,
|
||||
LockURLGenerator: mockURLGenerator{},
|
||||
InitStepRunner: mockInit,
|
||||
PlanStepRunner: mockPlan,
|
||||
ApplyStepRunner: mockApply,
|
||||
RunStepRunner: mockRun,
|
||||
PullApprovedChecker: mockApproved,
|
||||
WorkingDir: mockWorkingDir,
|
||||
Webhooks: mockSender,
|
||||
WorkingDirLocker: events.NewDefaultWorkingDirLocker(),
|
||||
}
|
||||
|
||||
repoDir := "/tmp/mydir"
|
||||
When(mockWorkingDir.GetWorkingDir(
|
||||
matchers.AnyModelsRepo(),
|
||||
matchers.AnyModelsPullRequest(),
|
||||
AnyString(),
|
||||
)).ThenReturn(repoDir, nil)
|
||||
|
||||
ctx := models.ProjectCommandContext{
|
||||
Log: logging.NewNoopLogger(),
|
||||
ProjectConfig: c.projCfg,
|
||||
Workspace: "default",
|
||||
GlobalConfig: c.globalCfg,
|
||||
RepoRelDir: ".",
|
||||
}
|
||||
When(mockInit.Run(ctx, nil, repoDir)).ThenReturn("init", nil)
|
||||
When(mockPlan.Run(ctx, nil, repoDir)).ThenReturn("plan", nil)
|
||||
When(mockApply.Run(ctx, nil, repoDir)).ThenReturn("apply", nil)
|
||||
When(mockRun.Run(ctx, nil, repoDir)).ThenReturn("run", nil)
|
||||
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(true, nil)
|
||||
|
||||
res := runner.Apply(ctx)
|
||||
Equals(t, c.expOut, res.ApplySuccess)
|
||||
|
||||
for _, step := range c.expSteps {
|
||||
switch step {
|
||||
case "approved":
|
||||
mockApproved.VerifyWasCalledOnce().PullIsApproved(ctx.BaseRepo, ctx.Pull)
|
||||
case "init":
|
||||
mockInit.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "plan":
|
||||
mockPlan.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "apply":
|
||||
mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
case "run":
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, nil, repoDir)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockURLGenerator struct{}
|
||||
|
||||
func (m mockURLGenerator) GenerateLockURL(lockID string) string {
|
||||
return "https://" + lockID
|
||||
}
|
||||
|
||||
20
server/events/runtime/mocks/matchers/models_pullrequest.go
Normal file
20
server/events/runtime/mocks/matchers/models_pullrequest.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
func AnyModelsPullRequest() models.PullRequest {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.PullRequest))(nil)).Elem()))
|
||||
var nullValue models.PullRequest
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqModelsPullRequest(value models.PullRequest) models.PullRequest {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue models.PullRequest
|
||||
return nullValue
|
||||
}
|
||||
20
server/events/runtime/mocks/matchers/models_repo.go
Normal file
20
server/events/runtime/mocks/matchers/models_repo.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
func AnyModelsRepo() models.Repo {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.Repo))(nil)).Elem()))
|
||||
var nullValue models.Repo
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqModelsRepo(value models.Repo) models.Repo {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue models.Repo
|
||||
return nullValue
|
||||
}
|
||||
84
server/events/runtime/mocks/mock_pull_approved_checker.go
Normal file
84
server/events/runtime/mocks/mock_pull_approved_checker.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// Automatically generated by pegomock. DO NOT EDIT!
|
||||
// Source: github.com/runatlantis/atlantis/server/events/runtime (interfaces: PullApprovedChecker)
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
type MockPullApprovedChecker struct {
|
||||
fail func(message string, callerSkip ...int)
|
||||
}
|
||||
|
||||
func NewMockPullApprovedChecker() *MockPullApprovedChecker {
|
||||
return &MockPullApprovedChecker{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockPullApprovedChecker) PullIsApproved(baseRepo models.Repo, pull models.PullRequest) (bool, error) {
|
||||
params := []pegomock.Param{baseRepo, pull}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsApproved", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 bool
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(bool)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockPullApprovedChecker) VerifyWasCalledOnce() *VerifierPullApprovedChecker {
|
||||
return &VerifierPullApprovedChecker{mock, pegomock.Times(1), nil}
|
||||
}
|
||||
|
||||
func (mock *MockPullApprovedChecker) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierPullApprovedChecker {
|
||||
return &VerifierPullApprovedChecker{mock, invocationCountMatcher, nil}
|
||||
}
|
||||
|
||||
func (mock *MockPullApprovedChecker) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierPullApprovedChecker {
|
||||
return &VerifierPullApprovedChecker{mock, invocationCountMatcher, inOrderContext}
|
||||
}
|
||||
|
||||
type VerifierPullApprovedChecker struct {
|
||||
mock *MockPullApprovedChecker
|
||||
invocationCountMatcher pegomock.Matcher
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierPullApprovedChecker) PullIsApproved(baseRepo models.Repo, pull models.PullRequest) *PullApprovedChecker_PullIsApproved_OngoingVerification {
|
||||
params := []pegomock.Param{baseRepo, pull}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsApproved", params)
|
||||
return &PullApprovedChecker_PullIsApproved_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type PullApprovedChecker_PullIsApproved_OngoingVerification struct {
|
||||
mock *MockPullApprovedChecker
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *PullApprovedChecker_PullIsApproved_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) {
|
||||
baseRepo, pull := c.GetAllCapturedArguments()
|
||||
return baseRepo[len(baseRepo)-1], pull[len(pull)-1]
|
||||
}
|
||||
|
||||
func (c *PullApprovedChecker_PullIsApproved_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(models.Repo)
|
||||
}
|
||||
_param1 = make([]models.PullRequest, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(models.PullRequest)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_pull_approved_checker.go PullApprovedChecker
|
||||
|
||||
type PullApprovedChecker interface {
|
||||
PullIsApproved(baseRepo models.Repo, pull models.PullRequest) (bool, error)
|
||||
}
|
||||
|
||||
@@ -36,20 +36,20 @@ type WorkingDirLocker interface {
|
||||
Unlock(repoFullName, workspace string, pullNum int)
|
||||
}
|
||||
|
||||
// DefaultAtlantisWorkingDirLocker implements WorkingDirLocker.
|
||||
type DefaultAtlantisWorkingDirLocker struct {
|
||||
// DefaultWorkingDirLocker implements WorkingDirLocker.
|
||||
type DefaultWorkingDirLocker struct {
|
||||
mutex sync.Mutex
|
||||
locks map[string]interface{}
|
||||
}
|
||||
|
||||
// NewDefaultAtlantisWorkingDirLocker is a constructor.
|
||||
func NewDefaultAtlantisWorkingDirLocker() *DefaultAtlantisWorkingDirLocker {
|
||||
return &DefaultAtlantisWorkingDirLocker{
|
||||
// NewDefaultWorkingDirLocker is a constructor.
|
||||
func NewDefaultWorkingDirLocker() *DefaultWorkingDirLocker {
|
||||
return &DefaultWorkingDirLocker{
|
||||
locks: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultAtlantisWorkingDirLocker) TryLock(repoFullName string, workspace string, pullNum int) (func(), error) {
|
||||
func (d *DefaultWorkingDirLocker) TryLock(repoFullName string, workspace string, pullNum int) (func(), error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
@@ -67,12 +67,12 @@ func (d *DefaultAtlantisWorkingDirLocker) TryLock(repoFullName string, workspace
|
||||
}
|
||||
|
||||
// Unlock unlocks the repo, pull and workspace.
|
||||
func (d *DefaultAtlantisWorkingDirLocker) Unlock(repoFullName, workspace string, pullNum int) {
|
||||
func (d *DefaultWorkingDirLocker) Unlock(repoFullName, workspace string, pullNum int) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
delete(d.locks, d.key(repoFullName, workspace, pullNum))
|
||||
}
|
||||
|
||||
func (d *DefaultAtlantisWorkingDirLocker) key(repo string, workspace string, pull int) string {
|
||||
func (d *DefaultWorkingDirLocker) key(repo string, workspace string, pull int) string {
|
||||
return fmt.Sprintf("%s/%s/%d", repo, workspace, pull)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ var repo = "repo/owner"
|
||||
var workspace = "default"
|
||||
|
||||
func TestTryLock(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
// The first lock should succeed.
|
||||
unlockFn, err := locker.TryLock(repo, workspace, 1)
|
||||
@@ -43,7 +43,7 @@ func TestTryLock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTryLockDifferentWorkspaces(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
t.Log("a lock for the same repo and pull but different workspace should succeed")
|
||||
_, err := locker.TryLock(repo, workspace, 1)
|
||||
@@ -59,7 +59,7 @@ func TestTryLockDifferentWorkspaces(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTryLockDifferentRepo(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
t.Log("a lock for a different repo but the same workspace and pull should succeed")
|
||||
_, err := locker.TryLock(repo, workspace, 1)
|
||||
@@ -76,7 +76,7 @@ func TestTryLockDifferentRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTryLockDifferentPulls(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
t.Log("a lock for a different pull but the same repo and workspace should succeed")
|
||||
_, err := locker.TryLock(repo, workspace, 1)
|
||||
@@ -93,7 +93,7 @@ func TestTryLockDifferentPulls(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnlock(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
t.Log("unlocking should work")
|
||||
unlockFn, err := locker.TryLock(repo, workspace, 1)
|
||||
@@ -104,7 +104,7 @@ func TestUnlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnlockDifferentWorkspaces(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
t.Log("unlocking should work for different workspaces")
|
||||
unlockFn1, err1 := locker.TryLock(repo, workspace, 1)
|
||||
Ok(t, err1)
|
||||
@@ -120,7 +120,7 @@ func TestUnlockDifferentWorkspaces(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnlockDifferentRepos(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
t.Log("unlocking should work for different repos")
|
||||
unlockFn1, err1 := locker.TryLock(repo, workspace, 1)
|
||||
Ok(t, err1)
|
||||
@@ -137,7 +137,7 @@ func TestUnlockDifferentRepos(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnlockDifferentPulls(t *testing.T) {
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
t.Log("unlocking should work for different pulls")
|
||||
unlockFn1, err1 := locker.TryLock(repo, workspace, 1)
|
||||
Ok(t, err1)
|
||||
|
||||
@@ -236,23 +236,23 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy,
|
||||
}
|
||||
|
||||
defaultTFVersion := terraformClient.Version()
|
||||
locker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
locker := events.NewDefaultWorkingDirLocker()
|
||||
commandRunner := &events.DefaultCommandRunner{
|
||||
ProjectCommandRunner: &events.DefaultProjectCommandRunner{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: &mockLockURLGenerator{},
|
||||
InitStepRunner: runtime.InitStepRunner{
|
||||
InitStepRunner: &runtime.InitStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
PlanStepRunner: runtime.PlanStepRunner{
|
||||
PlanStepRunner: &runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
ApplyStepRunner: runtime.ApplyStepRunner{
|
||||
ApplyStepRunner: &runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepRunner: runtime.RunStepRunner{
|
||||
RunStepRunner: &runtime.RunStepRunner{
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
PullApprovedChecker: e2eVCSClient,
|
||||
@@ -277,7 +277,6 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy,
|
||||
WorkingDirLocker: locker,
|
||||
AllowRepoConfigFlag: "allow-repo-config",
|
||||
AllowRepoConfig: true,
|
||||
RequireApproval: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
lockingClient := locking.NewClient(boltdb)
|
||||
workingDirLocker := events.NewDefaultAtlantisWorkingDirLocker()
|
||||
workingDirLocker := events.NewDefaultWorkingDirLocker()
|
||||
workingDir := &events.FileWorkspace{
|
||||
DataDir: userConfig.DataDir,
|
||||
}
|
||||
@@ -239,31 +239,31 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
VCSClient: vcsClient,
|
||||
WorkingDir: workingDir,
|
||||
WorkingDirLocker: workingDirLocker,
|
||||
RequireApproval: userConfig.RequireApproval,
|
||||
AllowRepoConfig: userConfig.AllowRepoConfig,
|
||||
AllowRepoConfigFlag: config.AllowRepoConfigFlag,
|
||||
},
|
||||
ProjectCommandRunner: &events.DefaultProjectCommandRunner{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: router,
|
||||
InitStepRunner: runtime.InitStepRunner{
|
||||
InitStepRunner: &runtime.InitStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
PlanStepRunner: runtime.PlanStepRunner{
|
||||
PlanStepRunner: &runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
ApplyStepRunner: runtime.ApplyStepRunner{
|
||||
ApplyStepRunner: &runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepRunner: runtime.RunStepRunner{
|
||||
RunStepRunner: &runtime.RunStepRunner{
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
PullApprovedChecker: vcsClient,
|
||||
WorkingDir: workingDir,
|
||||
Webhooks: webhooksManager,
|
||||
WorkingDirLocker: workingDirLocker,
|
||||
PullApprovedChecker: vcsClient,
|
||||
WorkingDir: workingDir,
|
||||
Webhooks: webhooksManager,
|
||||
WorkingDirLocker: workingDirLocker,
|
||||
RequireApprovalOverride: userConfig.RequireApproval,
|
||||
},
|
||||
}
|
||||
repoWhitelist := &events.RepoWhitelistChecker{
|
||||
|
||||
Reference in New Issue
Block a user