Files
atlantis/server/handlers/project_command_output_handler_test.go
2021-12-30 09:52:52 -05:00

217 lines
6.2 KiB
Go

package handlers_test
import (
"errors"
"sync"
"testing"
"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/handlers"
"github.com/runatlantis/atlantis/server/handlers/mocks"
"github.com/runatlantis/atlantis/server/handlers/mocks/matchers"
"github.com/runatlantis/atlantis/server/logging"
"github.com/stretchr/testify/assert"
. "github.com/petergtz/pegomock"
. "github.com/runatlantis/atlantis/testing"
)
func createTestProjectCmdContext(t *testing.T) models.ProjectCommandContext {
logger := logging.NewNoopLogger(t)
return models.ProjectCommandContext{
BaseRepo: models.Repo{
Name: "test-repo",
Owner: "test-org",
},
HeadRepo: models.Repo{
Name: "test-repo",
Owner: "test-org",
},
Pull: models.PullRequest{
Num: 1,
HeadBranch: "master",
BaseBranch: "master",
Author: "test-user",
},
User: models.User{
Username: "test-user",
},
Log: logger,
Workspace: "myworkspace",
RepoRelDir: "test-dir",
ProjectName: "test-project",
}
}
func createProjectCommandOutputHandler(t *testing.T) handlers.ProjectCommandOutputHandler {
logger := logging.NewNoopLogger(t)
prjCmdOutputChan := make(chan *models.ProjectCmdOutputLine)
projectStatusUpdater := mocks.NewMockProjectStatusUpdater()
projectJobURLGenerator := mocks.NewMockProjectJobURLGenerator()
prjCmdOutputHandler := handlers.NewAsyncProjectCommandOutputHandler(
prjCmdOutputChan,
projectStatusUpdater,
projectJobURLGenerator,
logger,
)
go func() {
prjCmdOutputHandler.Handle()
}()
return prjCmdOutputHandler
}
func TestProjectCommandOutputHandler(t *testing.T) {
Msg := "Test Terraform Output"
ctx := createTestProjectCmdContext(t)
t.Run("receive message from main channel", func(t *testing.T) {
var wg sync.WaitGroup
var expectedMsg string
projectOutputHandler := createProjectCommandOutputHandler(t)
ch := make(chan string)
// register channel and backfill from buffer
// Note: We call this synchronously because otherwise
// there could be a race where we are unable to register the channel
// before sending messages due to the way we lock our buffer memory cache
projectOutputHandler.Register(ctx.PullInfo(), ch)
wg.Add(1)
// read from channel
go func() {
for msg := range ch {
expectedMsg = msg
wg.Done()
}
}()
projectOutputHandler.Send(ctx, Msg)
wg.Wait()
close(ch)
// Wait for the msg to be read.
wg.Wait()
Equals(t, expectedMsg, Msg)
})
t.Run("clear buffer", func(t *testing.T) {
var wg sync.WaitGroup
projectOutputHandler := createProjectCommandOutputHandler(t)
ch := make(chan string)
// register channel and backfill from buffer
// Note: We call this synchronously because otherwise
// there could be a race where we are unable to register the channel
// before sending messages due to the way we lock our buffer memory cache
projectOutputHandler.Register(ctx.PullInfo(), ch)
wg.Add(1)
// read from channel asynchronously
go func() {
for msg := range ch {
// we are done once we receive the clear message.
// prior message doesn't matter for this test.
if msg == models.LogStreamingClearMsg {
wg.Done()
}
}
}()
// send regular message followed by clear message
projectOutputHandler.Send(ctx, Msg)
projectOutputHandler.Clear(ctx)
wg.Wait()
close(ch)
dfProjectOutputHandler, ok := projectOutputHandler.(*handlers.AsyncProjectCommandOutputHandler)
assert.True(t, ok)
assert.Empty(t, dfProjectOutputHandler.GetProjectOutputBuffer(ctx.PullInfo()))
})
t.Run("copies buffer to new channels", func(t *testing.T) {
var wg sync.WaitGroup
projectOutputHandler := createProjectCommandOutputHandler(t)
// send first message to populated the buffer
projectOutputHandler.Send(ctx, Msg)
ch := make(chan string)
receivedMsgs := []string{}
wg.Add(1)
// read from channel asynchronously
go func() {
for msg := range ch {
receivedMsgs = append(receivedMsgs, msg)
// we're only expecting two messages here.
if len(receivedMsgs) >= 2 {
wg.Done()
}
}
}()
// register channel and backfill from buffer
// Note: We call this synchronously because otherwise
// there could be a race where we are unable to register the channel
// before sending messages due to the way we lock our buffer memory cache
projectOutputHandler.Register(ctx.PullInfo(), ch)
projectOutputHandler.Send(ctx, Msg)
wg.Wait()
close(ch)
expectedMsgs := []string{Msg, Msg}
assert.Equal(t, len(expectedMsgs), len(receivedMsgs))
for i := range expectedMsgs {
assert.Equal(t, expectedMsgs[i], receivedMsgs[i])
}
})
t.Run("update project status with project jobs url", func(t *testing.T) {
RegisterMockTestingT(t)
logger := logging.NewNoopLogger(t)
prjCmdOutputChan := make(chan *models.ProjectCmdOutputLine)
projectStatusUpdater := mocks.NewMockProjectStatusUpdater()
projectJobURLGenerator := mocks.NewMockProjectJobURLGenerator()
prjCmdOutputHandler := handlers.NewAsyncProjectCommandOutputHandler(
prjCmdOutputChan,
projectStatusUpdater,
projectJobURLGenerator,
logger,
)
When(projectJobURLGenerator.GenerateProjectJobURL(matchers.EqModelsProjectCommandContext(ctx))).ThenReturn("url-to-project-jobs", nil)
err := prjCmdOutputHandler.SetJobURLWithStatus(ctx, models.PlanCommand, models.PendingCommitStatus)
Ok(t, err)
projectStatusUpdater.VerifyWasCalledOnce().UpdateProject(ctx, models.PlanCommand, models.PendingCommitStatus, "url-to-project-jobs")
})
t.Run("update project status with project jobs url error", func(t *testing.T) {
RegisterMockTestingT(t)
logger := logging.NewNoopLogger(t)
prjCmdOutputChan := make(chan *models.ProjectCmdOutputLine)
projectStatusUpdater := mocks.NewMockProjectStatusUpdater()
projectJobURLGenerator := mocks.NewMockProjectJobURLGenerator()
prjCmdOutputHandler := handlers.NewAsyncProjectCommandOutputHandler(
prjCmdOutputChan,
projectStatusUpdater,
projectJobURLGenerator,
logger,
)
When(projectJobURLGenerator.GenerateProjectJobURL(matchers.EqModelsProjectCommandContext(ctx))).ThenReturn("url-to-project-jobs", errors.New("some error"))
err := prjCmdOutputHandler.SetJobURLWithStatus(ctx, models.PlanCommand, models.PendingCommitStatus)
assert.Error(t, err)
})
}