Add BoltDB interface and some relevant tests

With BoltDB interface we are able to mock/stub and
improve test coverage.
Some other refactoring was required to make code aware
and compatible with the new interface
This commit is contained in:
Paris Morali
2020-05-11 15:13:36 +01:00
parent ce1d577f1d
commit ade9b31593
12 changed files with 420 additions and 25 deletions

View File

@@ -97,7 +97,7 @@ type DefaultCommandRunner struct {
GlobalAutomerge bool
PendingPlanFinder PendingPlanFinder
WorkingDir WorkingDir
DB *db.BoltDB
DB db.BoltDB
}
// RunAutoplanCommand runs plan when a pull request is opened or updated.

View File

@@ -24,6 +24,8 @@ import (
"github.com/google/go-github/v28/github"
. "github.com/petergtz/pegomock"
"github.com/runatlantis/atlantis/server/events"
dbmocks "github.com/runatlantis/atlantis/server/events/db/mocks"
dbmatchers "github.com/runatlantis/atlantis/server/events/db/mocks/matchers"
"github.com/runatlantis/atlantis/server/events/mocks"
"github.com/runatlantis/atlantis/server/events/mocks/matchers"
"github.com/runatlantis/atlantis/server/events/models"
@@ -43,6 +45,7 @@ var ch events.DefaultCommandRunner
var pullLogger *logging.SimpleLogger
var workingDir events.WorkingDir
var pendingPlanFinder *mocks.MockPendingPlanFinder
var boltDB *dbmocks.MockBoltDB
func setup(t *testing.T) *vcsmocks.MockClient {
RegisterMockTestingT(t)
@@ -57,6 +60,7 @@ func setup(t *testing.T) *vcsmocks.MockClient {
projectCommandRunner = mocks.NewMockProjectCommandRunner()
workingDir = mocks.NewMockWorkingDir()
pendingPlanFinder = mocks.NewMockPendingPlanFinder()
boltDB = dbmocks.NewMockBoltDB()
When(logger.GetLevel()).ThenReturn(logging.Info)
When(logger.NewLogger("runatlantis/atlantis#1", true, logging.Info)).
ThenReturn(pullLogger)
@@ -76,6 +80,7 @@ func setup(t *testing.T) *vcsmocks.MockClient {
PendingPlanFinder: pendingPlanFinder,
WorkingDir: workingDir,
DisableApplyAll: false,
DB: boltDB,
}
return vcsClient
}
@@ -234,3 +239,53 @@ func TestRunAutoplanCommand_DeletePlans(t *testing.T) {
ch.RunAutoplanCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.Pull, fixtures.User)
pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp)
}
func TestApplyWithAutoMerge_VSCMerge(t *testing.T) {
t.Log("if \"atlantis apply\" is run with automerge and at least one project" +
" has a discarded plan, automerge should not take place")
vcsClient := setup(t)
pull := &github.PullRequest{
State: github.String("open"),
}
modelPull := models.PullRequest{State: models.OpenPullState}
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, modelPull.BaseRepo, fixtures.GithubRepo, nil)
ch.GlobalAutomerge = true
ch.RunCommentCommand(fixtures.GithubRepo, &fixtures.GithubRepo, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.ApplyCommand})
vcsClient.VerifyWasCalledOnce().MergePull(modelPull)
}
func TestApplyWithAutoMerge_DiscardedPlan(t *testing.T) {
t.Log("if \"atlantis apply\" is run with automerge and at least one project" +
" has a discarded plan, automerge should not take place")
setup(t)
pull := &github.PullRequest{
State: github.String("open"),
}
modelPull := models.PullRequest{State: models.OpenPullState}
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, modelPull.BaseRepo, fixtures.GithubRepo, nil)
ch.GlobalAutomerge = true
ch.RunCommentCommand(fixtures.GithubRepo, &fixtures.GithubRepo, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.ApplyCommand})
projectStatuses := []models.ProjectStatus{
{
RepoRelDir: ".",
Workspace: "default",
ProjectName: "automerge-test",
Status: models.DiscardedPlanStatus,
},
}
pullStatus := models.PullStatus{
Projects: projectStatuses,
Pull: modelPull,
}
//When(boltDB.UpdatePullWithResults(modelPull, nil)).ThenReturn(pullStatus, nil)
When(boltDB.UpdatePullWithResults(dbmatchers.EqModelsPullRequest(modelPull), dbmatchers.EqSliceOfModelsProjectResult(nil))).ThenReturn(pullStatus, nil)
// TODO: stubbing here doesn't seem to work? pullStatus defined here is not actually returned and so I cannot uncomment the next two lines which would verify our scenario here
//vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, modelPull.Num, "not automerging because project at dir %q, workspace %q has status %q")
//VerifyWasCalled(Never()).MergePull(modelPull)
}

View File

@@ -15,8 +15,17 @@ import (
bolt "go.etcd.io/bbolt"
)
// BoltDB is a database using BoltDB
type BoltDB struct {
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_boltdb.go BoltDB
// BoltDB interface defines the set of methods the DB implements. Use this to allow DB mocking when testing
type BoltDB interface {
UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error)
DeletePullStatus(pull models.PullRequest) error
UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error
}
// DefaultBoltDB is a database using BoltDB
type DefaultBoltDB struct {
db *bolt.DB
locksBucketName []byte
pullsBucketName []byte
@@ -30,7 +39,7 @@ const (
// New returns a valid locker. We need to be able to write to dataDir
// since bolt stores its data as a file
func New(dataDir string) (*BoltDB, error) {
func New(dataDir string) (*DefaultBoltDB, error) {
if err := os.MkdirAll(dataDir, 0700); err != nil {
return nil, errors.Wrap(err, "creating data dir")
}
@@ -56,19 +65,19 @@ func New(dataDir string) (*BoltDB, error) {
return nil, errors.Wrap(err, "starting BoltDB")
}
// todo: close BoltDB when server is sigtermed
return &BoltDB{db: db, locksBucketName: []byte(locksBucketName), pullsBucketName: []byte(pullsBucketName)}, nil
return &DefaultBoltDB{db: db, locksBucketName: []byte(locksBucketName), pullsBucketName: []byte(pullsBucketName)}, nil
}
// NewWithDB is used for testing.
func NewWithDB(db *bolt.DB, bucket string) (*BoltDB, error) {
return &BoltDB{db: db, locksBucketName: []byte(bucket), pullsBucketName: []byte(pullsBucketName)}, nil
func NewWithDB(db *bolt.DB, bucket string) (*DefaultBoltDB, error) {
return &DefaultBoltDB{db: db, locksBucketName: []byte(bucket), pullsBucketName: []byte(pullsBucketName)}, nil
}
// TryLock attempts to create a new lock. If the lock is
// acquired, it will return true and the lock returned will be newLock.
// If the lock is not acquired, it will return false and the current
// lock that is preventing this lock from being acquired.
func (b *BoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, error) {
func (b *DefaultBoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, error) {
var lockAcquired bool
var currLock models.ProjectLock
key := b.lockKey(newLock.Project, newLock.Workspace)
@@ -105,7 +114,7 @@ func (b *BoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock,
// If there is no lock, then it will return a nil pointer.
// If there is a lock, then it will delete it, and then return a pointer
// to the deleted lock.
func (b *BoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock, error) {
func (b *DefaultBoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock, error) {
var lock models.ProjectLock
foundLock := false
key := b.lockKey(p, workspace)
@@ -128,7 +137,7 @@ func (b *BoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock
}
// List lists all current locks.
func (b *BoltDB) List() ([]models.ProjectLock, error) {
func (b *DefaultBoltDB) List() ([]models.ProjectLock, error) {
var locks []models.ProjectLock
var locksBytes [][]byte
err := b.db.View(func(tx *bolt.Tx) error {
@@ -156,7 +165,7 @@ func (b *BoltDB) List() ([]models.ProjectLock, error) {
}
// UnlockByPull deletes all locks associated with that pull request and returns them.
func (b *BoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.ProjectLock, error) {
func (b *DefaultBoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.ProjectLock, error) {
var locks []models.ProjectLock
err := b.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(b.locksBucketName).Cursor()
@@ -188,7 +197,7 @@ func (b *BoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.Projec
// GetLock returns a pointer to the lock for that project and workspace.
// If there is no lock, it returns a nil pointer.
func (b *BoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLock, error) {
func (b *DefaultBoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLock, error) {
key := b.lockKey(p, workspace)
var lockBytes []byte
err := b.db.View(func(tx *bolt.Tx) error {
@@ -216,7 +225,7 @@ func (b *BoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLoc
// UpdatePullWithResults updates pull's status with the latest project results.
// It returns the new PullStatus object.
func (b *BoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) {
func (b *DefaultBoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) {
key, err := b.pullKey(pull)
if err != nil {
return models.PullStatus{}, err
@@ -281,7 +290,7 @@ func (b *BoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []mod
// GetPullStatus returns the status for pull.
// If there is no status, returns a nil pointer.
func (b *BoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, error) {
func (b *DefaultBoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, error) {
key, err := b.pullKey(pull)
if err != nil {
return nil, err
@@ -297,7 +306,7 @@ func (b *BoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, err
}
// DeletePullStatus deletes the status for pull.
func (b *BoltDB) DeletePullStatus(pull models.PullRequest) error {
func (b *DefaultBoltDB) DeletePullStatus(pull models.PullRequest) error {
key, err := b.pullKey(pull)
if err != nil {
return err
@@ -311,7 +320,7 @@ func (b *BoltDB) DeletePullStatus(pull models.PullRequest) error {
// UpdateProjectStatus updates all project statuses under pull that match
// workspace and repoRelDir.
func (b *BoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error {
func (b *DefaultBoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error {
key, err := b.pullKey(pull)
if err != nil {
return err
@@ -344,7 +353,7 @@ func (b *BoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string,
return errors.Wrap(err, "DB transaction failed")
}
func (b *BoltDB) pullKey(pull models.PullRequest) ([]byte, error) {
func (b *DefaultBoltDB) pullKey(pull models.PullRequest) ([]byte, error) {
hostname := pull.BaseRepo.VCSHost.Hostname
if strings.Contains(hostname, pullKeySeparator) {
return nil, fmt.Errorf("vcs hostname %q contains illegal string %q", hostname, pullKeySeparator)
@@ -358,11 +367,11 @@ func (b *BoltDB) pullKey(pull models.PullRequest) ([]byte, error) {
nil
}
func (b *BoltDB) lockKey(p models.Project, workspace string) string {
func (b *DefaultBoltDB) lockKey(p models.Project, workspace string) string {
return fmt.Sprintf("%s/%s/%s", p.RepoFullName, p.Path, workspace)
}
func (b *BoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.PullStatus, error) {
func (b *DefaultBoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.PullStatus, error) {
serialized := bucket.Get(key)
if serialized == nil {
return nil, nil
@@ -375,7 +384,7 @@ func (b *BoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.Pul
return &p, nil
}
func (b *BoltDB) writePullToBucket(bucket *bolt.Bucket, key []byte, pull models.PullStatus) error {
func (b *DefaultBoltDB) writePullToBucket(bucket *bolt.Bucket, key []byte, pull models.PullStatus) error {
serialized, err := json.Marshal(pull)
if err != nil {
return errors.Wrap(err, "serializing")
@@ -383,7 +392,7 @@ func (b *BoltDB) writePullToBucket(bucket *bolt.Bucket, key []byte, pull models.
return bucket.Put(key, serialized)
}
func (b *BoltDB) projectResultToProject(p models.ProjectResult) models.ProjectStatus {
func (b *DefaultBoltDB) projectResultToProject(p models.ProjectResult) models.ProjectStatus {
return models.ProjectStatus{
Workspace: p.Workspace,
RepoRelDir: p.RepoRelDir,

View File

@@ -692,7 +692,7 @@ func TestPullStatus_UpdateMerge(t *testing.T) {
}
// newTestDB returns a TestDB using a temporary path.
func newTestDB() (*bolt.DB, *db.BoltDB) {
func newTestDB() (*bolt.DB, *db.DefaultBoltDB) {
// Retrieve a temporary path.
f, err := ioutil.TempFile("", "")
if err != nil {
@@ -718,7 +718,7 @@ func newTestDB() (*bolt.DB, *db.BoltDB) {
return boltDB, b
}
func newTestDB2(t *testing.T) (*db.BoltDB, func()) {
func newTestDB2(t *testing.T) (*db.DefaultBoltDB, func()) {
tmp, cleanup := TempDir(t)
boltDB, err := db.New(tmp)
Ok(t, err)

View File

@@ -0,0 +1,20 @@
// Code generated by pegomock. DO NOT EDIT.
package matchers
import (
"reflect"
"github.com/petergtz/pegomock"
models "github.com/runatlantis/atlantis/server/events/models"
)
func AnyModelsProjectPlanStatus() models.ProjectPlanStatus {
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.ProjectPlanStatus))(nil)).Elem()))
var nullValue models.ProjectPlanStatus
return nullValue
}
func EqModelsProjectPlanStatus(value models.ProjectPlanStatus) models.ProjectPlanStatus {
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
var nullValue models.ProjectPlanStatus
return nullValue
}

View File

@@ -0,0 +1,20 @@
// Code generated by pegomock. DO NOT EDIT.
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
}

View File

@@ -0,0 +1,20 @@
// Code generated by pegomock. DO NOT EDIT.
package matchers
import (
"reflect"
"github.com/petergtz/pegomock"
models "github.com/runatlantis/atlantis/server/events/models"
)
func AnyModelsPullStatus() models.PullStatus {
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.PullStatus))(nil)).Elem()))
var nullValue models.PullStatus
return nullValue
}
func EqModelsPullStatus(value models.PullStatus) models.PullStatus {
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
var nullValue models.PullStatus
return nullValue
}

View File

@@ -0,0 +1,20 @@
// Code generated by pegomock. DO NOT EDIT.
package matchers
import (
"reflect"
"github.com/petergtz/pegomock"
models "github.com/runatlantis/atlantis/server/events/models"
)
func AnySliceOfModelsProjectResult() []models.ProjectResult {
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*([]models.ProjectResult))(nil)).Elem()))
var nullValue []models.ProjectResult
return nullValue
}
func EqSliceOfModelsProjectResult(value []models.ProjectResult) []models.ProjectResult {
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
var nullValue []models.ProjectResult
return nullValue
}

View File

@@ -0,0 +1,209 @@
// Code generated by pegomock. DO NOT EDIT.
// Source: github.com/runatlantis/atlantis/server/events/db (interfaces: BoltDB)
package mocks
import (
pegomock "github.com/petergtz/pegomock"
models "github.com/runatlantis/atlantis/server/events/models"
"reflect"
"time"
)
type MockBoltDB struct {
fail func(message string, callerSkip ...int)
}
func NewMockBoltDB(options ...pegomock.Option) *MockBoltDB {
mock := &MockBoltDB{}
for _, option := range options {
option.Apply(mock)
}
return mock
}
func (mock *MockBoltDB) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
func (mock *MockBoltDB) FailHandler() pegomock.FailHandler { return mock.fail }
func (mock *MockBoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockBoltDB().")
}
params := []pegomock.Param{pull, newResults}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdatePullWithResults", params, []reflect.Type{reflect.TypeOf((*models.PullStatus)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 models.PullStatus
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(models.PullStatus)
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockBoltDB) DeletePullStatus(pull models.PullRequest) error {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockBoltDB().")
}
params := []pegomock.Param{pull}
result := pegomock.GetGenericMockFrom(mock).Invoke("DeletePullStatus", 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 *MockBoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockBoltDB().")
}
params := []pegomock.Param{pull, workspace, repoRelDir, targetStatus}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateProjectStatus", 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 *MockBoltDB) VerifyWasCalledOnce() *VerifierMockBoltDB {
return &VerifierMockBoltDB{
mock: mock,
invocationCountMatcher: pegomock.Times(1),
}
}
func (mock *MockBoltDB) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierMockBoltDB {
return &VerifierMockBoltDB{
mock: mock,
invocationCountMatcher: invocationCountMatcher,
}
}
func (mock *MockBoltDB) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierMockBoltDB {
return &VerifierMockBoltDB{
mock: mock,
invocationCountMatcher: invocationCountMatcher,
inOrderContext: inOrderContext,
}
}
func (mock *MockBoltDB) VerifyWasCalledEventually(invocationCountMatcher pegomock.Matcher, timeout time.Duration) *VerifierMockBoltDB {
return &VerifierMockBoltDB{
mock: mock,
invocationCountMatcher: invocationCountMatcher,
timeout: timeout,
}
}
type VerifierMockBoltDB struct {
mock *MockBoltDB
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
timeout time.Duration
}
func (verifier *VerifierMockBoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) *MockBoltDB_UpdatePullWithResults_OngoingVerification {
params := []pegomock.Param{pull, newResults}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdatePullWithResults", params, verifier.timeout)
return &MockBoltDB_UpdatePullWithResults_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockBoltDB_UpdatePullWithResults_OngoingVerification struct {
mock *MockBoltDB
methodInvocations []pegomock.MethodInvocation
}
func (c *MockBoltDB_UpdatePullWithResults_OngoingVerification) GetCapturedArguments() (models.PullRequest, []models.ProjectResult) {
pull, newResults := c.GetAllCapturedArguments()
return pull[len(pull)-1], newResults[len(newResults)-1]
}
func (c *MockBoltDB_UpdatePullWithResults_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest, _param1 [][]models.ProjectResult) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.PullRequest, len(c.methodInvocations))
for u, param := range params[0] {
_param0[u] = param.(models.PullRequest)
}
_param1 = make([][]models.ProjectResult, len(c.methodInvocations))
for u, param := range params[1] {
_param1[u] = param.([]models.ProjectResult)
}
}
return
}
func (verifier *VerifierMockBoltDB) DeletePullStatus(pull models.PullRequest) *MockBoltDB_DeletePullStatus_OngoingVerification {
params := []pegomock.Param{pull}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "DeletePullStatus", params, verifier.timeout)
return &MockBoltDB_DeletePullStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockBoltDB_DeletePullStatus_OngoingVerification struct {
mock *MockBoltDB
methodInvocations []pegomock.MethodInvocation
}
func (c *MockBoltDB_DeletePullStatus_OngoingVerification) GetCapturedArguments() models.PullRequest {
pull := c.GetAllCapturedArguments()
return pull[len(pull)-1]
}
func (c *MockBoltDB_DeletePullStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.PullRequest, len(c.methodInvocations))
for u, param := range params[0] {
_param0[u] = param.(models.PullRequest)
}
}
return
}
func (verifier *VerifierMockBoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) *MockBoltDB_UpdateProjectStatus_OngoingVerification {
params := []pegomock.Param{pull, workspace, repoRelDir, targetStatus}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateProjectStatus", params, verifier.timeout)
return &MockBoltDB_UpdateProjectStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockBoltDB_UpdateProjectStatus_OngoingVerification struct {
mock *MockBoltDB
methodInvocations []pegomock.MethodInvocation
}
func (c *MockBoltDB_UpdateProjectStatus_OngoingVerification) GetCapturedArguments() (models.PullRequest, string, string, models.ProjectPlanStatus) {
pull, workspace, repoRelDir, targetStatus := c.GetAllCapturedArguments()
return pull[len(pull)-1], workspace[len(workspace)-1], repoRelDir[len(repoRelDir)-1], targetStatus[len(targetStatus)-1]
}
func (c *MockBoltDB_UpdateProjectStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest, _param1 []string, _param2 []string, _param3 []models.ProjectPlanStatus) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.PullRequest, len(c.methodInvocations))
for u, param := range params[0] {
_param0[u] = param.(models.PullRequest)
}
_param1 = make([]string, len(c.methodInvocations))
for u, param := range params[1] {
_param1[u] = param.(string)
}
_param2 = make([]string, len(c.methodInvocations))
for u, param := range params[2] {
_param2[u] = param.(string)
}
_param3 = make([]models.ProjectPlanStatus, len(c.methodInvocations))
for u, param := range params[3] {
_param3[u] = param.(models.ProjectPlanStatus)
}
}
return
}

View File

@@ -46,7 +46,7 @@ type PullClosedExecutor struct {
VCSClient vcs.Client
WorkingDir WorkingDir
Logger logging.SimpleLogging
DB *db.BoltDB
DB db.BoltDB
}
type templatedProject struct {

View File

@@ -25,7 +25,7 @@ type LocksController struct {
LockDetailTemplate TemplateWriter
WorkingDir events.WorkingDir
WorkingDirLocker events.WorkingDirLocker
DB *db.BoltDB
DB db.BoltDB
}
// GetLock is the GET /locks/{id} route. It renders the lock detail view.

View File

@@ -15,6 +15,8 @@ import (
. "github.com/petergtz/pegomock"
"github.com/runatlantis/atlantis/server"
"github.com/runatlantis/atlantis/server/events"
dbmocks "github.com/runatlantis/atlantis/server/events/db/mocks"
"github.com/runatlantis/atlantis/server/events/locking/mocks"
mocks2 "github.com/runatlantis/atlantis/server/events/mocks"
"github.com/runatlantis/atlantis/server/events/models"
@@ -192,6 +194,46 @@ func TestDeleteLock_OldFormat(t *testing.T) {
cp.VerifyWasCalled(Never()).CreateComment(AnyRepo(), AnyInt(), AnyString())
}
func TestDeleteLock_UpdateProjectStatus(t *testing.T) {
t.Log("When deleting a lock, pull status has to be updated to reflect discarded plan")
RegisterMockTestingT(t)
repoName := "owner/repo"
projectPath := "path"
workspaceName := "workspace"
cp := vcsmocks.NewMockClient()
l := mocks.NewMockLocker()
workingDir := mocks2.NewMockWorkingDir()
workingDirLocker := events.NewDefaultWorkingDirLocker()
pull := models.PullRequest{
BaseRepo: models.Repo{FullName: repoName},
}
When(l.Unlock("id")).ThenReturn(&models.ProjectLock{
Pull: pull,
Workspace: workspaceName,
Project: models.Project{
Path: projectPath,
RepoFullName: repoName,
},
}, nil)
db := dbmocks.NewMockBoltDB()
lc := server.LocksController{
Locker: l,
Logger: logging.NewNoopLogger(),
VCSClient: cp,
WorkingDirLocker: workingDirLocker,
WorkingDir: workingDir,
DB: db,
}
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
req = mux.SetURLVars(req, map[string]string{"id": "id"})
w := httptest.NewRecorder()
lc.DeleteLock(w, req)
responseContains(t, w, http.StatusOK, "Deleted lock id \"id\"")
db.VerifyWasCalledOnce().UpdateProjectStatus(pull, workspaceName, projectPath, models.DiscardedPlanStatus)
}
func TestDeleteLock_CommentFailed(t *testing.T) {
t.Log("If the commenting fails we return an error")
RegisterMockTestingT(t)