From 8ab4857f77bc163db01ccea6bdb154fd33d63ee3 Mon Sep 17 00:00:00 2001 From: Luke Kysow Date: Fri, 15 Jun 2018 16:16:31 +0100 Subject: [PATCH] Fix all tests --- server/events/command_handler_test.go | 21 +- server/events/markdown_renderer_test.go | 75 ++++--- .../mocks/mock_pull_request_operator.go | 154 ++++++++++++++ server/events/pull_request_operator.go | 18 +- server/events/pull_request_operator_test.go | 198 ------------------ .../runtime/apply_step_operator_test.go | 85 ++++---- .../events/runtime/init_step_operator_test.go | 10 +- .../events/runtime/plan_step_operater_test.go | 144 ++++++------- server/events/yaml/parser_validator_test.go | 6 +- server/events/yaml/raw/project.go | 13 +- server/events/yaml/raw/project_test.go | 44 +++- server/events_controller_test.go | 94 ++++++--- server/logging/simple_logger.go | 2 - server/server.go | 2 +- 14 files changed, 459 insertions(+), 407 deletions(-) create mode 100644 server/events/mocks/mock_pull_request_operator.go diff --git a/server/events/command_handler_test.go b/server/events/command_handler_test.go index 598ed6e23..65bcf3c6f 100644 --- a/server/events/command_handler_test.go +++ b/server/events/command_handler_test.go @@ -33,8 +33,7 @@ import ( . "github.com/runatlantis/atlantis/testing" ) -var applier *mocks.MockExecutor -var planner *mocks.MockExecutor +var operator *mocks.MockPullRequestOperator var eventParsing *mocks.MockEventParsing var vcsClient *vcsmocks.MockClientProxy var ghStatus *mocks.MockCommitStatusUpdater @@ -46,8 +45,7 @@ var logBytes *bytes.Buffer func setup(t *testing.T) { RegisterMockTestingT(t) - applier = mocks.NewMockExecutor() - planner = mocks.NewMockExecutor() + operator = mocks.NewMockPullRequestOperator() eventParsing = mocks.NewMockEventParsing() ghStatus = mocks.NewMockCommitStatusUpdater() workspaceLocker = mocks.NewMockAtlantisWorkspaceLocker() @@ -58,8 +56,6 @@ func setup(t *testing.T) { logBytes = new(bytes.Buffer) When(logger.Underlying()).ThenReturn(log.New(logBytes, "", 0)) ch = events.CommandHandler{ - PlanExecutor: planner, - ApplyExecutor: applier, VCSClient: vcsClient, CommitStatusUpdater: ghStatus, EventParser: eventParsing, @@ -67,9 +63,10 @@ func setup(t *testing.T) { MarkdownRenderer: &events.MarkdownRenderer{}, GithubPullGetter: githubGetter, GitlabMergeRequestGetter: gitlabGetter, - Logger: logger, - AllowForkPRs: false, - AllowForkPRsFlag: "allow-fork-prs-flag", + Logger: logger, + AllowForkPRs: false, + AllowForkPRsFlag: "allow-fork-prs-flag", + PullRequestOperator: operator, } } @@ -203,9 +200,9 @@ func TestExecuteCommand_FullRun(t *testing.T) { When(workspaceLocker.TryLock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true) switch c { case events.Plan: - When(planner.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) + When(operator.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) case events.Apply: - When(applier.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) + When(operator.ApplyViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) } ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, &cmd) @@ -238,7 +235,7 @@ func TestExecuteCommand_ForkPREnabled(t *testing.T) { headRepo.Owner = "forkrepo" When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, headRepo, nil) When(workspaceLocker.TryLock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true) - When(planner.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) + When(operator.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse) ch.ExecuteCommand(fixtures.GithubRepo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, &cmd) diff --git a/server/events/markdown_renderer_test.go b/server/events/markdown_renderer_test.go index 287996ea4..414d30081 100644 --- a/server/events/markdown_renderer_test.go +++ b/server/events/markdown_renderer_test.go @@ -125,9 +125,11 @@ func TestRenderProjectResults(t *testing.T) { TerraformOutput: "terraform-output", LockURL: "lock-url", }, + Workspace: "workspace", + Path: "path", }, }, - "```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n\n", + "Ran Plan in dir: `path` workspace: `workspace`\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n\n", }, { "single successful apply", @@ -135,30 +137,34 @@ func TestRenderProjectResults(t *testing.T) { []events.ProjectResult{ { ApplySuccess: "success", + Workspace: "workspace", + Path: "path", }, }, - "```diff\nsuccess\n```\n\n", + "Ran Apply in dir: `path` workspace: `workspace`\n```diff\nsuccess\n```\n\n", }, { "multiple successful plans", events.Plan, []events.ProjectResult{ { - Path: "path", + Workspace: "workspace", + Path: "path", PlanSuccess: &events.PlanSuccess{ TerraformOutput: "terraform-output", LockURL: "lock-url", }, }, { - Path: "path2", + Workspace: "workspace", + Path: "path2", PlanSuccess: &events.PlanSuccess{ TerraformOutput: "terraform-output2", LockURL: "lock-url2", }, }, }, - "Ran Plan in 2 directories:\n * `path`\n * `path2`\n\n## path/\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n---\n## path2/\n```diff\nterraform-output2\n```\n\n* To **discard** this plan click [here](lock-url2).\n---\n\n", + "Ran Plan for 2 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n---\n### 2. workspace: `workspace` path: `path2`\n```diff\nterraform-output2\n```\n\n* To **discard** this plan click [here](lock-url2).\n---\n\n", }, { "multiple successful applies", @@ -166,75 +172,87 @@ func TestRenderProjectResults(t *testing.T) { []events.ProjectResult{ { Path: "path", + Workspace: "workspace", ApplySuccess: "success", }, { Path: "path2", + Workspace: "workspace", ApplySuccess: "success2", }, }, - "Ran Apply in 2 directories:\n * `path`\n * `path2`\n\n## path/\n```diff\nsuccess\n```\n---\n## path2/\n```diff\nsuccess2\n```\n---\n\n", + "Ran Apply for 2 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nsuccess\n```\n---\n### 2. workspace: `workspace` path: `path2`\n```diff\nsuccess2\n```\n---\n\n", }, { "single errored plan", events.Plan, []events.ProjectResult{ { - Error: errors.New("error"), + Error: errors.New("error"), + Path: "path", + Workspace: "workspace", }, }, - "**Plan Error**\n```\nerror\n```\n\n\n", + "Ran Plan in dir: `path` workspace: `workspace`\n**Plan Error**\n```\nerror\n```\n\n\n", }, { "single failed plan", events.Plan, []events.ProjectResult{ { - Failure: "failure", + Path: "path", + Workspace: "workspace", + Failure: "failure", }, }, - "**Plan Failed**: failure\n\n\n", + "Ran Plan in dir: `path` workspace: `workspace`\n**Plan Failed**: failure\n\n\n", }, { "successful, failed, and errored plan", events.Plan, []events.ProjectResult{ { - Path: "path", + Workspace: "workspace", + Path: "path", PlanSuccess: &events.PlanSuccess{ TerraformOutput: "terraform-output", LockURL: "lock-url", }, }, { - Path: "path2", - Failure: "failure", + Workspace: "workspace", + Path: "path2", + Failure: "failure", }, { - Path: "path3", - Error: errors.New("error"), + Workspace: "workspace", + Path: "path3", + Error: errors.New("error"), }, }, - "Ran Plan in 3 directories:\n * `path`\n * `path2`\n * `path3`\n\n## path/\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n---\n## path2/\n**Plan Failed**: failure\n\n---\n## path3/\n**Plan Error**\n```\nerror\n```\n\n---\n\n", + "Ran Plan for 3 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n1. workspace: `workspace` path: `path3`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n---\n### 2. workspace: `workspace` path: `path2`\n**Plan Failed**: failure\n\n---\n### 3. workspace: `workspace` path: `path3`\n**Plan Error**\n```\nerror\n```\n\n---\n\n", }, { "successful, failed, and errored apply", events.Apply, []events.ProjectResult{ { + Workspace: "workspace", Path: "path", ApplySuccess: "success", }, { - Path: "path2", - Failure: "failure", + Workspace: "workspace", + Path: "path2", + Failure: "failure", }, { - Path: "path3", - Error: errors.New("error"), + Workspace: "workspace", + Path: "path3", + Error: errors.New("error"), }, }, - "Ran Apply in 3 directories:\n * `path`\n * `path2`\n * `path3`\n\n## path/\n```diff\nsuccess\n```\n---\n## path2/\n**Apply Failed**: failure\n\n---\n## path3/\n**Apply Error**\n```\nerror\n```\n\n---\n\n", + "Ran Apply for 3 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n1. workspace: `workspace` path: `path3`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nsuccess\n```\n---\n### 2. workspace: `workspace` path: `path2`\n**Apply Failed**: failure\n\n---\n### 3. workspace: `workspace` path: `path3`\n**Apply Error**\n```\nerror\n```\n\n---\n\n", }, } @@ -244,13 +262,14 @@ func TestRenderProjectResults(t *testing.T) { ProjectResults: c.ProjectResults, } for _, verbose := range []bool{true, false} { - t.Log("testing " + c.Description) - s := r.Render(res, c.Command, "log", verbose) - if !verbose { - Equals(t, c.Expected, s) - } else { - Equals(t, c.Expected+"
Log\n

\n\n```\nlog```\n

\n", s) - } + t.Run(c.Description, func(t *testing.T) { + s := r.Render(res, c.Command, "log", verbose) + if !verbose { + Equals(t, c.Expected, s) + } else { + Equals(t, c.Expected+"
Log\n

\n\n```\nlog```\n

\n", s) + } + }) } } } diff --git a/server/events/mocks/mock_pull_request_operator.go b/server/events/mocks/mock_pull_request_operator.go new file mode 100644 index 000000000..443636863 --- /dev/null +++ b/server/events/mocks/mock_pull_request_operator.go @@ -0,0 +1,154 @@ +// Automatically generated by pegomock. DO NOT EDIT! +// Source: github.com/runatlantis/atlantis/server/events (interfaces: PullRequestOperator) + +package mocks + +import ( + "reflect" + + pegomock "github.com/petergtz/pegomock" + events "github.com/runatlantis/atlantis/server/events" +) + +type MockPullRequestOperator struct { + fail func(message string, callerSkip ...int) +} + +func NewMockPullRequestOperator() *MockPullRequestOperator { + return &MockPullRequestOperator{fail: pegomock.GlobalFailHandler} +} + +func (mock *MockPullRequestOperator) Autoplan(ctx *events.CommandContext) events.CommandResponse { + params := []pegomock.Param{ctx} + result := pegomock.GetGenericMockFrom(mock).Invoke("Autoplan", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()}) + var ret0 events.CommandResponse + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(events.CommandResponse) + } + } + return ret0 +} + +func (mock *MockPullRequestOperator) PlanViaComment(ctx *events.CommandContext) events.CommandResponse { + params := []pegomock.Param{ctx} + result := pegomock.GetGenericMockFrom(mock).Invoke("PlanViaComment", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()}) + var ret0 events.CommandResponse + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(events.CommandResponse) + } + } + return ret0 +} + +func (mock *MockPullRequestOperator) ApplyViaComment(ctx *events.CommandContext) events.CommandResponse { + params := []pegomock.Param{ctx} + result := pegomock.GetGenericMockFrom(mock).Invoke("ApplyViaComment", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()}) + var ret0 events.CommandResponse + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(events.CommandResponse) + } + } + return ret0 +} + +func (mock *MockPullRequestOperator) VerifyWasCalledOnce() *VerifierPullRequestOperator { + return &VerifierPullRequestOperator{mock, pegomock.Times(1), nil} +} + +func (mock *MockPullRequestOperator) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierPullRequestOperator { + return &VerifierPullRequestOperator{mock, invocationCountMatcher, nil} +} + +func (mock *MockPullRequestOperator) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierPullRequestOperator { + return &VerifierPullRequestOperator{mock, invocationCountMatcher, inOrderContext} +} + +type VerifierPullRequestOperator struct { + mock *MockPullRequestOperator + invocationCountMatcher pegomock.Matcher + inOrderContext *pegomock.InOrderContext +} + +func (verifier *VerifierPullRequestOperator) Autoplan(ctx *events.CommandContext) *PullRequestOperator_Autoplan_OngoingVerification { + params := []pegomock.Param{ctx} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Autoplan", params) + return &PullRequestOperator_Autoplan_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type PullRequestOperator_Autoplan_OngoingVerification struct { + mock *MockPullRequestOperator + methodInvocations []pegomock.MethodInvocation +} + +func (c *PullRequestOperator_Autoplan_OngoingVerification) GetCapturedArguments() *events.CommandContext { + ctx := c.GetAllCapturedArguments() + return ctx[len(ctx)-1] +} + +func (c *PullRequestOperator_Autoplan_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) { + params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(params) > 0 { + _param0 = make([]*events.CommandContext, len(params[0])) + for u, param := range params[0] { + _param0[u] = param.(*events.CommandContext) + } + } + return +} + +func (verifier *VerifierPullRequestOperator) PlanViaComment(ctx *events.CommandContext) *PullRequestOperator_PlanViaComment_OngoingVerification { + params := []pegomock.Param{ctx} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PlanViaComment", params) + return &PullRequestOperator_PlanViaComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type PullRequestOperator_PlanViaComment_OngoingVerification struct { + mock *MockPullRequestOperator + methodInvocations []pegomock.MethodInvocation +} + +func (c *PullRequestOperator_PlanViaComment_OngoingVerification) GetCapturedArguments() *events.CommandContext { + ctx := c.GetAllCapturedArguments() + return ctx[len(ctx)-1] +} + +func (c *PullRequestOperator_PlanViaComment_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) { + params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(params) > 0 { + _param0 = make([]*events.CommandContext, len(params[0])) + for u, param := range params[0] { + _param0[u] = param.(*events.CommandContext) + } + } + return +} + +func (verifier *VerifierPullRequestOperator) ApplyViaComment(ctx *events.CommandContext) *PullRequestOperator_ApplyViaComment_OngoingVerification { + params := []pegomock.Param{ctx} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ApplyViaComment", params) + return &PullRequestOperator_ApplyViaComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type PullRequestOperator_ApplyViaComment_OngoingVerification struct { + mock *MockPullRequestOperator + methodInvocations []pegomock.MethodInvocation +} + +func (c *PullRequestOperator_ApplyViaComment_OngoingVerification) GetCapturedArguments() *events.CommandContext { + ctx := c.GetAllCapturedArguments() + return ctx[len(ctx)-1] +} + +func (c *PullRequestOperator_ApplyViaComment_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) { + params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(params) > 0 { + _param0 = make([]*events.CommandContext, len(params[0])) + for u, param := range params[0] { + _param0[u] = param.(*events.CommandContext) + } + } + return +} diff --git a/server/events/pull_request_operator.go b/server/events/pull_request_operator.go index 61218e902..b393fb5b7 100644 --- a/server/events/pull_request_operator.go +++ b/server/events/pull_request_operator.go @@ -12,7 +12,15 @@ import ( "github.com/runatlantis/atlantis/server/logging" ) -type PullRequestOperator struct { +//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_pull_request_operator.go PullRequestOperator + +type PullRequestOperator interface { + Autoplan(ctx *CommandContext) CommandResponse + PlanViaComment(ctx *CommandContext) CommandResponse + ApplyViaComment(ctx *CommandContext) CommandResponse +} + +type DefaultPullRequestOperator struct { TerraformExecutor TerraformExec DefaultTFVersion *version.Version ParserValidator *yaml.ParserValidator @@ -26,7 +34,7 @@ type TerraformExec interface { RunCommandWithVersion(log *logging.SimpleLogger, path string, args []string, v *version.Version, workspace string) (string, error) } -func (p *PullRequestOperator) Autoplan(ctx *CommandContext) CommandResponse { +func (p *DefaultPullRequestOperator) Autoplan(ctx *CommandContext) CommandResponse { // check out repo to parse atlantis.yaml // this will check out the repo to a * dir repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace) @@ -99,7 +107,7 @@ func (p *PullRequestOperator) Autoplan(ctx *CommandContext) CommandResponse { return CommandResponse{ProjectResults: results} } -func (p *PullRequestOperator) PlanViaComment(ctx *CommandContext) CommandResponse { +func (p *DefaultPullRequestOperator) PlanViaComment(ctx *CommandContext) CommandResponse { repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace) if err != nil { return CommandResponse{Error: err} @@ -141,7 +149,7 @@ func (p *PullRequestOperator) PlanViaComment(ctx *CommandContext) CommandRespons } } -func (p *PullRequestOperator) ApplyViaComment(ctx *CommandContext) CommandResponse { +func (p *DefaultPullRequestOperator) ApplyViaComment(ctx *CommandContext) CommandResponse { repoDir, err := p.Workspace.GetWorkspace(ctx.BaseRepo, ctx.Pull, ctx.Command.Workspace) if err != nil { return CommandResponse{Failure: "No workspace found. Did you run plan?"} @@ -185,7 +193,7 @@ func (p *PullRequestOperator) ApplyViaComment(ctx *CommandContext) CommandRespon // matchingProjects returns the list of projects whose WhenModified fields match // any of the modifiedFiles. -func (p *PullRequestOperator) matchingProjects(modifiedFiles []string, config valid.Spec) []valid.Project { +func (p *DefaultPullRequestOperator) matchingProjects(modifiedFiles []string, config valid.Spec) []valid.Project { //todo // match the modified files against the config // remember the modified_files paths are relative to the project paths diff --git a/server/events/pull_request_operator_test.go b/server/events/pull_request_operator_test.go index 4bbdfd14b..79457f0dd 100644 --- a/server/events/pull_request_operator_test.go +++ b/server/events/pull_request_operator_test.go @@ -1,199 +1 @@ package events_test - -import ( - "io/ioutil" - "path/filepath" - "testing" - - "github.com/hashicorp/go-version" - "github.com/runatlantis/atlantis/server/events" - "github.com/runatlantis/atlantis/server/events/runtime" - "github.com/runatlantis/atlantis/server/logging" - . "github.com/runatlantis/atlantis/testing" -) - -// When there is no config file, should use the defaults. -func TestBuildStage_NoConfigFile(t *testing.T) { - var defaultTFVersion *version.Version - var terraformExecutor runtime.TerraformExec - e := events.PullRequestOperator{ - DefaultTFVersion: defaultTFVersion, - TerraformExecutor: terraformExecutor, - } - - log := logging.NewNoopLogger() - repoDir := "/willnotexist" - workspace := "myworkspace" - relProjectPath := "mydir" - var extraCommentArgs []string - username := "myuser" - meta := runtime.StepMeta{ - Log: log, - Workspace: workspace, - AbsolutePath: filepath.Join(repoDir, relProjectPath), - DirRelativeToRepoRoot: relProjectPath, - TerraformVersion: defaultTFVersion, - TerraformExecutor: terraformExecutor, - ExtraCommentArgs: extraCommentArgs, - Username: username, - } - - // Test the plan stage first. - t.Run("plan stage", func(t *testing.T) { - planStage, err := e.BuildPlanStage(log, repoDir, workspace, relProjectPath, extraCommentArgs, username) - Ok(t, err) - Equals(t, runtime.PlanStage{ - Steps: []runtime.Step{ - &runtime.InitStep{ - Meta: meta, - }, - &runtime.PlanStep{ - Meta: meta, - }, - }, - }, planStage) - }) - - // Then the apply stage. - t.Run("apply stage", func(t *testing.T) { - applyStage, err := e.BuildApplyStage(log, repoDir, workspace, relProjectPath, extraCommentArgs, username) - Ok(t, err) - Equals(t, runtime.ApplyStage{ - Steps: []runtime.Step{ - &runtime.ApplyStep{ - Meta: meta, - }, - }, - }, *applyStage) - }) -} - -func TestBuildStage(t *testing.T) { - var defaultTFVersion *version.Version - var terraformExecutor runtime.TerraformExec - e := events.PullRequestOperator{ - DefaultTFVersion: defaultTFVersion, - TerraformExecutor: terraformExecutor, - } - - // Write atlantis.yaml config. - tmpDir, cleanup := TempDir(t) - defer cleanup() - err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(` -version: 2 -projects: -- dir: "." - workflow: custom -workflows: - custom: - plan: - steps: - - init: - extra_args: [arg1, arg2] - - plan - - run: echo hi - apply: - steps: - - run: prerun - - apply: - extra_args: [arg3, arg4] - - run: postrun -`), 0644) - Ok(t, err) - - repoDir := tmpDir - log := logging.NewNoopLogger() - workspace := "myworkspace" - // Our config is for '.' so there will be no config for this project. - relProjectPath := "mydir" - var extraCommentArgs []string - username := "myuser" - meta := runtime.StepMeta{ - Log: log, - Workspace: workspace, - AbsolutePath: filepath.Join(repoDir, relProjectPath), - DirRelativeToRepoRoot: relProjectPath, - TerraformVersion: defaultTFVersion, - TerraformExecutor: terraformExecutor, - ExtraCommentArgs: extraCommentArgs, - Username: username, - } - - t.Run("plan stage for project without config", func(t *testing.T) { - // This project isn't listed so it should get the defaults. - planStage, err := e.BuildPlanStage(log, repoDir, workspace, relProjectPath, extraCommentArgs, username) - Ok(t, err) - Equals(t, runtime.PlanStage{ - Steps: []runtime.Step{ - &runtime.InitStep{ - Meta: meta, - }, - &runtime.PlanStep{ - Meta: meta, - }, - }, - }, planStage) - }) - - t.Run("apply stage for project without config", func(t *testing.T) { - // This project isn't listed so it should get the defaults. - applyStage, err := e.BuildApplyStage(log, repoDir, workspace, relProjectPath, extraCommentArgs, username) - Ok(t, err) - Equals(t, runtime.ApplyStage{ - Steps: []runtime.Step{ - &runtime.ApplyStep{ - Meta: meta, - }, - }, - }, *applyStage) - }) - - // Create the meta for the custom project. - customMeta := meta - customMeta.Workspace = "default" - customMeta.DirRelativeToRepoRoot = "." - customMeta.AbsolutePath = tmpDir - - t.Run("plan stage for custom config", func(t *testing.T) { - planStage, err := e.BuildPlanStage(log, repoDir, "default", ".", extraCommentArgs, username) - Ok(t, err) - - Equals(t, runtime.PlanStage{ - Steps: []runtime.Step{ - &runtime.InitStep{ - Meta: customMeta, - ExtraArgs: []string{"arg1", "arg2"}, - }, - &runtime.PlanStep{ - Meta: customMeta, - }, - &runtime.RunStep{ - Meta: customMeta, - Commands: []string{"echo", "hi"}, - }, - }, - }, planStage) - }) - - t.Run("apply stage for custom config", func(t *testing.T) { - planStage, err := e.BuildApplyStage(log, repoDir, "default", ".", extraCommentArgs, username) - Ok(t, err) - - Equals(t, runtime.ApplyStage{ - Steps: []runtime.Step{ - &runtime.RunStep{ - Meta: customMeta, - Commands: []string{"prerun"}, - }, - &runtime.ApplyStep{ - Meta: customMeta, - ExtraArgs: []string{"arg3", "arg4"}, - }, - &runtime.RunStep{ - Meta: customMeta, - Commands: []string{"postrun"}, - }, - }, - }, *planStage) - }) -} diff --git a/server/events/runtime/apply_step_operator_test.go b/server/events/runtime/apply_step_operator_test.go index 862f5e10e..1aad3c9e2 100644 --- a/server/events/runtime/apply_step_operator_test.go +++ b/server/events/runtime/apply_step_operator_test.go @@ -8,42 +8,35 @@ import ( "github.com/hashicorp/go-version" . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/models" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" + "github.com/runatlantis/atlantis/server/events/yaml/valid" . "github.com/runatlantis/atlantis/testing" ) func TestRun_NoDir(t *testing.T) { - s := runtime.ApplyStep{ - Meta: runtime.StepMeta{ - Workspace: "workspace", - AbsolutePath: "nonexistent/path", - DirRelativeToRepoRoot: ".", - TerraformVersion: nil, - ExtraCommentArgs: nil, - Username: "username", - }, + o := runtime.ApplyStepOperator{ + TerraformExecutor: nil, } - _, err := s.Run() + _, err := o.Run(models.ProjectCommandContext{ + RepoRelPath: ".", + Workspace: "workspace", + }, nil, "/nonexistent/path") ErrEquals(t, "no plan found at path \".\" and workspace \"workspace\"–did you run plan?", err) } func TestRun_NoPlanFile(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - - s := runtime.ApplyStep{ - Meta: runtime.StepMeta{ - Workspace: "workspace", - AbsolutePath: tmpDir, - DirRelativeToRepoRoot: ".", - TerraformVersion: nil, - ExtraCommentArgs: nil, - Username: "username", - }, + o := runtime.ApplyStepOperator{ + TerraformExecutor: nil, } - _, err := s.Run() + _, err := o.Run(models.ProjectCommandContext{ + RepoRelPath: ".", + Workspace: "workspace", + }, nil, tmpDir) ErrEquals(t, "no plan found at path \".\" and workspace \"workspace\"–did you run plan?", err) } @@ -56,24 +49,46 @@ func TestRun_Success(t *testing.T) { RegisterMockTestingT(t) terraform := mocks.NewMockClient() - - tfVersion, _ := version.NewVersion("0.11.4") - s := runtime.ApplyStep{ - Meta: runtime.StepMeta{ - Workspace: "workspace", - AbsolutePath: tmpDir, - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + o := runtime.ApplyStepOperator{ + TerraformExecutor: terraform, } When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). ThenReturn("output", nil) - output, err := s.Run() + output, err := o.Run(models.ProjectCommandContext{ + Workspace: "workspace", + RepoRelPath: ".", + CommentArgs: []string{"comment", "args"}, + }, []string{"extra", "args"}, tmpDir) + Ok(t, err) + Equals(t, "output", output) + terraform.VerifyWasCalledOnce().RunCommandWithVersion(nil, tmpDir, []string{"apply", "-no-color", "extra", "args", "comment", "args", planPath}, nil, "workspace") +} + +func TestRun_UsesConfiguredTFVersion(t *testing.T) { + tmpDir, cleanup := TempDir(t) + defer cleanup() + planPath := filepath.Join(tmpDir, "workspace.tfplan") + err := ioutil.WriteFile(planPath, nil, 0644) + Ok(t, err) + + RegisterMockTestingT(t) + terraform := mocks.NewMockClient() + o := runtime.ApplyStepOperator{ + TerraformExecutor: terraform, + } + tfVersion, _ := version.NewVersion("0.11.0") + + When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). + ThenReturn("output", nil) + output, err := o.Run(models.ProjectCommandContext{ + Workspace: "workspace", + RepoRelPath: ".", + CommentArgs: []string{"comment", "args"}, + ProjectConfig: &valid.Project{ + TerraformVersion: tfVersion, + }, + }, []string{"extra", "args"}, tmpDir) Ok(t, err) Equals(t, "output", output) terraform.VerifyWasCalledOnce().RunCommandWithVersion(nil, tmpDir, []string{"apply", "-no-color", "extra", "args", "comment", "args", planPath}, tfVersion, "workspace") diff --git a/server/events/runtime/init_step_operator_test.go b/server/events/runtime/init_step_operator_test.go index 829639497..efbf4ca28 100644 --- a/server/events/runtime/init_step_operator_test.go +++ b/server/events/runtime/init_step_operator_test.go @@ -3,9 +3,10 @@ package runtime_test import ( "testing" - "github.com/hashicorp/go-version" + version "github.com/hashicorp/go-version" . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/models" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" @@ -45,15 +46,16 @@ func TestRun_UsesGetOrInitForRightVersion(t *testing.T) { logger := logging.NewNoopLogger() iso := runtime.InitStepOperator{ TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). ThenReturn("output", nil) - output, err := iso.Run(runtime.ProjectCommandContext{ + + output, err := iso.Run(models.ProjectCommandContext{ Log: logger, Workspace: "workspace", - AbsPath: "/path", RepoRelPath: ".", - }, []string{"extra", "args"}) + }, []string{"extra", "args"}, "/path") Ok(t, err) // Shouldn't return output since we don't print init output to PR. Equals(t, "", output) diff --git a/server/events/runtime/plan_step_operater_test.go b/server/events/runtime/plan_step_operater_test.go index 53b45c2e6..bf80927b6 100644 --- a/server/events/runtime/plan_step_operater_test.go +++ b/server/events/runtime/plan_step_operater_test.go @@ -1,7 +1,6 @@ package runtime_test import ( - "errors" "io/ioutil" "os" "path/filepath" @@ -9,7 +8,9 @@ import ( "github.com/hashicorp/go-version" . "github.com/petergtz/pegomock" + "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/events/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/models" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" @@ -25,23 +26,20 @@ func TestRun_NoWorkspaceIn08(t *testing.T) { tfVersion, _ := version.NewVersion("0.8") logger := logging.NewNoopLogger() workspace := "default" - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: workspace, - AbsolutePath: "/path", - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + s := runtime.PlanStepOperator{ + DefaultTFVersion: tfVersion, + TerraformExecutor: terraform, } When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). ThenReturn("output", nil) - output, err := s.Run() + output, err := s.Run(models.ProjectCommandContext{ + Log: logger, + CommentArgs: []string{"comment", "args"}, + Workspace: workspace, + RepoRelPath: ".", + User: models.User{Username: "username"}, + }, []string{"extra", "args"}, "/path") Ok(t, err) Equals(t, "output", output) @@ -61,23 +59,19 @@ func TestRun_ErrWorkspaceIn08(t *testing.T) { tfVersion, _ := version.NewVersion("0.8") logger := logging.NewNoopLogger() workspace := "notdefault" - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: workspace, - AbsolutePath: "/path", - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + s := runtime.PlanStepOperator{ + TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). ThenReturn("output", nil) - _, err := s.Run() + _, err := s.Run(models.ProjectCommandContext{ + Log: logger, + Workspace: workspace, + RepoRelPath: ".", + User: models.User{Username: "username"}, + }, []string{"extra", "args"}, "/path") ErrEquals(t, "terraform version 0.8.0 does not support workspaces", err) } @@ -112,23 +106,21 @@ func TestRun_SwitchesWorkspace(t *testing.T) { tfVersion, _ := version.NewVersion(c.tfVersion) logger := logging.NewNoopLogger() - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: "workspace", - AbsolutePath: "/path", - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + + s := runtime.PlanStepOperator{ + TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } When(terraform.RunCommandWithVersion(matchers.AnyPtrToLoggingSimpleLogger(), AnyString(), AnyStringSlice(), matchers2.AnyPtrToGoVersionVersion(), AnyString())). ThenReturn("output", nil) - output, err := s.Run() + output, err := s.Run(models.ProjectCommandContext{ + Log: logger, + Workspace: "workspace", + RepoRelPath: ".", + User: models.User{Username: "username"}, + CommentArgs: []string{"comment", "args"}, + }, []string{"extra", "args"}, "/path") Ok(t, err) Equals(t, "output", output) @@ -170,18 +162,9 @@ func TestRun_CreatesWorkspace(t *testing.T) { terraform := mocks.NewMockClient() tfVersion, _ := version.NewVersion(c.tfVersion) logger := logging.NewNoopLogger() - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: "workspace", - AbsolutePath: "/path", - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + s := runtime.PlanStepOperator{ + TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } // Ensure that we actually try to switch workspaces by making the @@ -194,7 +177,13 @@ func TestRun_CreatesWorkspace(t *testing.T) { expPlanArgs := []string{"plan", "-refresh", "-no-color", "-out", "/path/workspace.tfplan", "-var", "atlantis_user=username", "extra", "args", "comment", "args"} When(terraform.RunCommandWithVersion(logger, "/path", expPlanArgs, tfVersion, "workspace")).ThenReturn("output", nil) - output, err := s.Run() + output, err := s.Run(models.ProjectCommandContext{ + Log: logger, + Workspace: "workspace", + RepoRelPath: ".", + User: models.User{Username: "username"}, + CommentArgs: []string{"comment", "args"}, + }, []string{"extra", "args"}, "/path") Ok(t, err) Equals(t, "output", output) @@ -212,26 +201,22 @@ func TestRun_NoWorkspaceSwitchIfNotNecessary(t *testing.T) { terraform := mocks.NewMockClient() tfVersion, _ := version.NewVersion("0.10.0") logger := logging.NewNoopLogger() - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: "workspace", - AbsolutePath: "/path", - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + s := runtime.PlanStepOperator{ + TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } - When(terraform.RunCommandWithVersion(logger, "/path", []string{"workspace", "show"}, tfVersion, "workspace")).ThenReturn("workspace\n", nil) expPlanArgs := []string{"plan", "-refresh", "-no-color", "-out", "/path/workspace.tfplan", "-var", "atlantis_user=username", "extra", "args", "comment", "args"} When(terraform.RunCommandWithVersion(logger, "/path", expPlanArgs, tfVersion, "workspace")).ThenReturn("output", nil) - output, err := s.Run() + output, err := s.Run(models.ProjectCommandContext{ + Log: logger, + Workspace: "workspace", + RepoRelPath: ".", + User: models.User{Username: "username"}, + CommentArgs: []string{"comment", "args"}, + }, []string{"extra", "args"}, "/path") Ok(t, err) Equals(t, "output", output) @@ -258,28 +243,25 @@ func TestRun_AddsEnvVarFile(t *testing.T) { // Using version >= 0.10 here so we don't expect any env commands. tfVersion, _ := version.NewVersion("0.10.0") logger := logging.NewNoopLogger() - s := runtime.PlanStep{ - Meta: runtime.StepMeta{ - Log: logger, - Workspace: "workspace", - AbsolutePath: tmpDir, - DirRelativeToRepoRoot: ".", - TerraformExecutor: terraform, - TerraformVersion: tfVersion, - ExtraCommentArgs: []string{"comment", "args"}, - Username: "username", - }, - ExtraArgs: []string{"extra", "args"}, + s := runtime.PlanStepOperator{ + TerraformExecutor: terraform, + DefaultTFVersion: tfVersion, } expPlanArgs := []string{"plan", "-refresh", "-no-color", "-out", filepath.Join(tmpDir, "workspace.tfplan"), "-var", "atlantis_user=username", "extra", "args", "comment", "args", "-var-file", envVarsFile} When(terraform.RunCommandWithVersion(logger, tmpDir, expPlanArgs, tfVersion, "workspace")).ThenReturn("output", nil) - output, err := s.Run() + output, err := s.Run(models.ProjectCommandContext{ + Log: logger, + Workspace: "workspace", + RepoRelPath: ".", + User: models.User{Username: "username"}, + CommentArgs: []string{"comment", "args"}, + }, []string{"extra", "args"}, tmpDir) Ok(t, err) - Equals(t, "output", output) // Verify that env select was never called since we're in version >= 0.10 terraform.VerifyWasCalled(Never()).RunCommandWithVersion(logger, tmpDir, []string{"env", "select", "-no-color", "workspace"}, tfVersion, "workspace") terraform.VerifyWasCalledOnce().RunCommandWithVersion(logger, tmpDir, expPlanArgs, tfVersion, "workspace") + Equals(t, "output", output) } diff --git a/server/events/yaml/parser_validator_test.go b/server/events/yaml/parser_validator_test.go index 51fa1ca70..15bcd1960 100644 --- a/server/events/yaml/parser_validator_test.go +++ b/server/events/yaml/parser_validator_test.go @@ -6,6 +6,7 @@ import ( "path/filepath" "testing" + "github.com/hashicorp/go-version" "github.com/runatlantis/atlantis/server/events/yaml" "github.com/runatlantis/atlantis/server/events/yaml/valid" . "github.com/runatlantis/atlantis/testing" @@ -72,6 +73,7 @@ func TestReadConfig_UnmarshalErrors(t *testing.T) { } func TestReadConfig(t *testing.T) { + tfVersion, _ := version.NewVersion("v0.11.0") cases := []struct { description string input string @@ -169,7 +171,7 @@ workflows: Dir: ".", Workspace: "myworkspace", Workflow: String("myworkflow"), - TerraformVersion: String("v0.11.0"), + TerraformVersion: tfVersion, Autoplan: valid.Autoplan{ WhenModified: []string{"**/*.tf"}, Enabled: true, @@ -203,7 +205,7 @@ workflows: Dir: ".", Workspace: "myworkspace", Workflow: String("myworkflow"), - TerraformVersion: String("v0.11.0"), + TerraformVersion: tfVersion, Autoplan: valid.Autoplan{ WhenModified: []string{"**/*.tf"}, Enabled: false, diff --git a/server/events/yaml/raw/project.go b/server/events/yaml/raw/project.go index 3273cb38a..29335e795 100644 --- a/server/events/yaml/raw/project.go +++ b/server/events/yaml/raw/project.go @@ -1,12 +1,12 @@ package raw import ( - "errors" "fmt" "strings" "github.com/go-ozzo/ozzo-validation" "github.com/hashicorp/go-version" + "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/events/yaml/valid" ) @@ -41,11 +41,12 @@ func (p Project) Validate() error { return nil } validTFVersion := func(value interface{}) error { - // Safe to dereference because this is only called if the pointer is - // not nil. - versionStr := *value.(*string) - _, err := version.NewVersion(versionStr) - return err + strPtr := value.(*string) + if strPtr == nil { + return nil + } + _, err := version.NewVersion(*strPtr) + return errors.Wrapf(err, "version %q could not be parsed", *strPtr) } return validation.ValidateStruct(&p, validation.Field(&p.Dir, validation.Required, validation.By(hasDotDot)), diff --git a/server/events/yaml/raw/project_test.go b/server/events/yaml/raw/project_test.go index 9c0691489..b10452afe 100644 --- a/server/events/yaml/raw/project_test.go +++ b/server/events/yaml/raw/project_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/go-ozzo/ozzo-validation" + "github.com/hashicorp/go-version" "github.com/runatlantis/atlantis/server/events/yaml/raw" "github.com/runatlantis/atlantis/server/events/yaml/valid" . "github.com/runatlantis/atlantis/testing" @@ -107,6 +108,30 @@ func TestProject_Validate(t *testing.T) { }, expErr: "", }, + { + description: "empty tf version string", + input: raw.Project{ + Dir: String("."), + TerraformVersion: String(""), + }, + expErr: "terraform_version: version \"\" could not be parsed: Malformed version: .", + }, + { + description: "tf version with v prepended", + input: raw.Project{ + Dir: String("."), + TerraformVersion: String("v1"), + }, + expErr: "", + }, + { + description: "tf version without prepended", + input: raw.Project{ + Dir: String("."), + TerraformVersion: String("1"), + }, + expErr: "", + }, } validation.ErrorTag = "yaml" for _, c := range cases { @@ -122,6 +147,7 @@ func TestProject_Validate(t *testing.T) { } func TestProject_ToValid(t *testing.T) { + tfVersionPointEleven, _ := version.NewVersion("v0.11.0") cases := []struct { description string input raw.Project @@ -161,7 +187,7 @@ func TestProject_ToValid(t *testing.T) { Dir: ".", Workspace: "myworkspace", Workflow: String("myworkflow"), - TerraformVersion: String("v0.11.0"), + TerraformVersion: tfVersionPointEleven, Autoplan: valid.Autoplan{ WhenModified: []string{"hi"}, Enabled: false, @@ -169,6 +195,22 @@ func TestProject_ToValid(t *testing.T) { ApplyRequirements: []string{"approved"}, }, }, + { + description: "tf version without 'v'", + input: raw.Project{ + Dir: String("."), + TerraformVersion: String("0.11.0"), + }, + exp: valid.Project{ + Dir: ".", + Workspace: "default", + TerraformVersion: tfVersionPointEleven, + Autoplan: valid.Autoplan{ + WhenModified: []string{"**/*.tf"}, + Enabled: true, + }, + }, + }, } for _, c := range cases { t.Run(c.description, func(t *testing.T) { diff --git a/server/events_controller_test.go b/server/events_controller_test.go index 7997a56d8..bb0682ba6 100644 --- a/server/events_controller_test.go +++ b/server/events_controller_test.go @@ -293,31 +293,6 @@ func TestPost_GithubCommentSuccess(t *testing.T) { cr.VerifyWasCalledOnce().ExecuteCommand(baseRepo, baseRepo, user, 1, &cmd) } -func TestPost_GithubPullRequestNotClosed(t *testing.T) { - t.Log("when the event is a github pull reuqest but it's not a closed event we ignore it") - e, v, _, _, _, _, _, _ := setup(t) - req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil)) - req.Header.Set(githubHeader, "pull_request") - event := `{"action": "opened"}` - When(v.Validate(req, secret)).ThenReturn([]byte(event), nil) - w := httptest.NewRecorder() - e.Post(w, req) - responseContains(t, w, http.StatusOK, "Ignoring opened pull request event") -} - -func TestPost_GitlabMergeRequestNotClosed(t *testing.T) { - t.Log("when the event is a gitlab merge request but it's not a closed event we ignore it") - e, _, gl, p, _, _, _, _ := setup(t) - req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil)) - req.Header.Set(gitlabHeader, "value") - event := gitlab.MergeEvent{} - When(gl.Validate(req, secret)).ThenReturn(event, nil) - When(p.ParseGitlabMergeEvent(event)).ThenReturn(models.PullRequest{State: models.Open}, models.Repo{}, nil) - w := httptest.NewRecorder() - e.Post(w, req) - responseContains(t, w, http.StatusOK, "Ignoring opened pull request event") -} - func TestPost_GithubPullRequestInvalid(t *testing.T) { t.Log("when the event is a github pull request with invalid data we return a 400") e, v, _, p, _, _, _, _ := setup(t) @@ -383,15 +358,14 @@ func TestPost_GithubPullRequestErrCleaningPull(t *testing.T) { } func TestPost_GitlabMergeRequestErrCleaningPull(t *testing.T) { - t.Log("when the event is a gitlab merge request and an error occurs calling CleanUpPull we return a 503") + t.Log("when the event is a gitlab merge request and an error occurs calling CleanUpPull we return a 500") e, _, gl, p, _, c, _, _ := setup(t) req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil)) req.Header.Set(gitlabHeader, "value") - event := gitlab.MergeEvent{} - When(gl.Validate(req, secret)).ThenReturn(event, nil) + When(gl.Validate(req, secret)).ThenReturn(gitlabMergeEvent, nil) repo := models.Repo{} pullRequest := models.PullRequest{State: models.Closed} - When(p.ParseGitlabMergeEvent(event)).ThenReturn(pullRequest, repo, nil) + When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, nil) When(c.CleanUpPull(repo, pullRequest)).ThenReturn(errors.New("err")) w := httptest.NewRecorder() e.Post(w, req) @@ -421,11 +395,10 @@ func TestPost_GitlabMergeRequestSuccess(t *testing.T) { e, _, gl, p, _, _, _, _ := setup(t) req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil)) req.Header.Set(gitlabHeader, "value") - event := gitlab.MergeEvent{} - When(gl.Validate(req, secret)).ThenReturn(event, nil) + When(gl.Validate(req, secret)).ThenReturn(gitlabMergeEvent, nil) repo := models.Repo{} pullRequest := models.PullRequest{State: models.Closed} - When(p.ParseGitlabMergeEvent(event)).ThenReturn(pullRequest, repo, nil) + When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, nil) w := httptest.NewRecorder() e.Post(w, req) responseContains(t, w, http.StatusOK, "Pull request cleaned successfully") @@ -458,3 +431,60 @@ func setup(t *testing.T) (server.EventsController, *mocks.MockGithubRequestValid } return e, v, gl, p, cr, c, vcsmock, cp } + +var gitlabMergeEvent = gitlab.MergeEvent{ + ObjectAttributes: struct { + ID int `json:"id"` + TargetBranch string `json:"target_branch"` + SourceBranch string `json:"source_branch"` + SourceProjectID int `json:"source_project_id"` + AuthorID int `json:"author_id"` + AssigneeID int `json:"assignee_id"` + Title string `json:"title"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + StCommits []*gitlab.Commit `json:"st_commits"` + StDiffs []*gitlab.Diff `json:"st_diffs"` + MilestoneID int `json:"milestone_id"` + State string `json:"state"` + MergeStatus string `json:"merge_status"` + TargetProjectID int `json:"target_project_id"` + IID int `json:"iid"` + Description string `json:"description"` + Position int `json:"position"` + LockedAt string `json:"locked_at"` + UpdatedByID int `json:"updated_by_id"` + MergeError string `json:"merge_error"` + MergeParams struct { + ForceRemoveSourceBranch string `json:"force_remove_source_branch"` + } `json:"merge_params"` + MergeWhenBuildSucceeds bool `json:"merge_when_build_succeeds"` + MergeUserID int `json:"merge_user_id"` + MergeCommitSha string `json:"merge_commit_sha"` + DeletedAt string `json:"deleted_at"` + ApprovalsBeforeMerge string `json:"approvals_before_merge"` + RebaseCommitSha string `json:"rebase_commit_sha"` + InProgressMergeCommitSha string `json:"in_progress_merge_commit_sha"` + LockVersion int `json:"lock_version"` + TimeEstimate int `json:"time_estimate"` + Source *gitlab.Repository `json:"source"` + Target *gitlab.Repository `json:"target"` + LastCommit struct { + ID string `json:"id"` + Message string `json:"message"` + Timestamp *time.Time `json:"timestamp"` + URL string `json:"url"` + Author *gitlab.Author `json:"author"` + } `json:"last_commit"` + WorkInProgress bool `json:"work_in_progress"` + URL string `json:"url"` + Action string `json:"action"` + Assignee struct { + Name string `json:"name"` + Username string `json:"username"` + AvatarURL string `json:"avatar_url"` + } `json:"assignee"` + }{ + Action: "merge", + }, +} diff --git a/server/logging/simple_logger.go b/server/logging/simple_logger.go index eca5778e4..ddb56ebcd 100644 --- a/server/logging/simple_logger.go +++ b/server/logging/simple_logger.go @@ -23,8 +23,6 @@ import ( "unicode" ) -//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_simple_logging.go SimpleLogging - // SimpleLogging is the interface that our SimpleLogger implements. // It's really only used for mocking when we need to test what's being logged. type SimpleLogging interface { diff --git a/server/server.go b/server/server.go index fe6e3f7b5..734a36de4 100644 --- a/server/server.go +++ b/server/server.go @@ -232,7 +232,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { Logger: logger, AllowForkPRs: userConfig.AllowForkPRs, AllowForkPRsFlag: config.AllowForkPRsFlag, - PullRequestOperator: events.PullRequestOperator{ + PullRequestOperator: &events.DefaultPullRequestOperator{ TerraformExecutor: terraformClient, DefaultTFVersion: defaultTfVersion, ParserValidator: &yaml.ParserValidator{},