Skip to content

Commit

Permalink
Update Mock Service to be a bit more concurrent
Browse files Browse the repository at this point in the history
It is a simple set of changes to make it work for the current testing
use cases. If we need to we can move the Called fields to counters or
re-architect the mock.

This change also fixes a race condition when appending to the Slice of
errs when calling PopulateIteration.
  • Loading branch information
nywilken committed Feb 23, 2022
1 parent c80d9e0 commit a3898f6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 55 deletions.
110 changes: 66 additions & 44 deletions internal/registry/mock_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import (
"google.golang.org/grpc/status"
)

//MockPackerClientService represents a basic mock of the Cloud Packer Service.
//Upon calling a service method a boolean is set to true to indicate that a method has been called.
//To skip the setting of these booleans set TrackCalledServiceMethods to false; defaults to true in NewMockPackerClientService().
type MockPackerClientService struct {
CreateBucketCalled, UpdateBucketCalled, BucketAlreadyExist bool
CreateIterationCalled, GetIterationCalled, IterationAlreadyExist, IterationCompleted bool
CreateBuildCalled, UpdateBuildCalled, ListBuildsCalled, BuildAlreadyDone bool
TrackCalledServiceMethods bool

// Mock Creates
CreateBucketResp *models.HashicorpCloudPackerCreateBucketResponse
Expand All @@ -31,41 +35,21 @@ type MockPackerClientService struct {
packerSvc.ClientService
}

//NewMockPackerClientService returns a basic mock of the Cloud Packer Service.
//Upon calling a service method a boolean is set to true to indicate that a method has been called.
//To skip the setting of these booleans set TrackCalledServiceMethods to false. By default it is true.
func NewMockPackerClientService() *MockPackerClientService {
m := MockPackerClientService{
ExistingBuilds: make([]string, 0),
}

m.CreateBucketResp = &models.HashicorpCloudPackerCreateBucketResponse{
Bucket: &models.HashicorpCloudPackerBucket{
ID: "bucket-id",
},
}

m.CreateIterationResp = &models.HashicorpCloudPackerCreateIterationResponse{
Iteration: &models.HashicorpCloudPackerIteration{
ID: "iteration-id",
},
}

m.CreateBuildResp = &models.HashicorpCloudPackerCreateBuildResponse{
Build: &models.HashicorpCloudPackerBuild{
PackerRunUUID: "test-uuid",
Status: models.HashicorpCloudPackerBuildStatusUNSET,
},
}

m.GetIterationResp = &models.HashicorpCloudPackerGetIterationResponse{
Iteration: &models.HashicorpCloudPackerIteration{
ID: "iteration-id",
Builds: make([]*models.HashicorpCloudPackerBuild, 0),
},
ExistingBuilds: make([]string, 0),
ExistingBuildLabels: make(map[string]string),
TrackCalledServiceMethods: true,
}

return &m
}

func (svc *MockPackerClientService) PackerServiceCreateBucket(params *packerSvc.PackerServiceCreateBucketParams, _ runtime.ClientAuthInfoWriter) (*packerSvc.PackerServiceCreateBucketOK, error) {

if svc.BucketAlreadyExist {
return nil, status.Error(codes.AlreadyExists, fmt.Sprintf("Code:%d %s", codes.AlreadyExists, codes.AlreadyExists.String()))
}
Expand All @@ -77,19 +61,27 @@ func (svc *MockPackerClientService) PackerServiceCreateBucket(params *packerSvc.
return nil, errors.New("No bucket slug was passed in")
}

svc.CreateBucketCalled = true
// This is set in NewMockPackerClientService()
svc.CreateBucketResp.Bucket.Slug = params.Body.BucketSlug
if svc.TrackCalledServiceMethods {
svc.CreateBucketCalled = true
}
payload := &models.HashicorpCloudPackerCreateBucketResponse{
Bucket: &models.HashicorpCloudPackerBucket{
ID: "bucket-id",
},
}
payload.Bucket.Slug = params.Body.BucketSlug

ok := &packerSvc.PackerServiceCreateBucketOK{
Payload: svc.CreateBucketResp,
Payload: payload,
}

return ok, nil
}

func (svc *MockPackerClientService) PackerServiceUpdateBucket(params *packerSvc.PackerServiceUpdateBucketParams, _ runtime.ClientAuthInfoWriter) (*packerSvc.PackerServiceUpdateBucketOK, error) {
svc.UpdateBucketCalled = true
if svc.TrackCalledServiceMethods {
svc.UpdateBucketCalled = true
}

return packerSvc.NewPackerServiceUpdateBucketOK(), nil
}
Expand All @@ -107,12 +99,20 @@ func (svc *MockPackerClientService) PackerServiceCreateIteration(params *packerS
return nil, errors.New("No valid Fingerprint was passed in")
}

svc.CreateIterationCalled = true
svc.CreateIterationResp.Iteration.BucketSlug = params.Body.BucketSlug
svc.CreateIterationResp.Iteration.Fingerprint = params.Body.Fingerprint
if svc.TrackCalledServiceMethods {
svc.CreateIterationCalled = true
}
payload := &models.HashicorpCloudPackerCreateIterationResponse{
Iteration: &models.HashicorpCloudPackerIteration{
ID: "iteration-id",
},
}

payload.Iteration.BucketSlug = params.Body.BucketSlug
payload.Iteration.Fingerprint = params.Body.Fingerprint

ok := &packerSvc.PackerServiceCreateIterationOK{
Payload: svc.CreateIterationResp,
Payload: payload,
}

return ok, nil
Expand All @@ -131,11 +131,21 @@ func (svc *MockPackerClientService) PackerServiceGetIteration(params *packerSvc.
return nil, errors.New("No valid Fingerprint was passed in")
}

svc.GetIterationCalled = true
if svc.TrackCalledServiceMethods {
svc.GetIterationCalled = true
}

//
payload := &models.HashicorpCloudPackerGetIterationResponse{
Iteration: &models.HashicorpCloudPackerIteration{
ID: "iteration-id",
Builds: make([]*models.HashicorpCloudPackerBuild, 0),
},
}

payload.Iteration.BucketSlug = params.BucketSlug
payload.Iteration.Fingerprint = *params.Fingerprint
ok := &packerSvc.PackerServiceGetIterationOK{
Payload: svc.GetIterationResp,
Payload: payload,
}

if svc.IterationCompleted {
Expand Down Expand Up @@ -168,13 +178,22 @@ func (svc *MockPackerClientService) PackerServiceCreateBuild(params *packerSvc.P
return nil, errors.New("No build componentType was passed in")
}

svc.CreateBuildCalled = true
if svc.TrackCalledServiceMethods {
svc.CreateBuildCalled = true
}

payload := &models.HashicorpCloudPackerCreateBuildResponse{
Build: &models.HashicorpCloudPackerBuild{
PackerRunUUID: "test-uuid",
Status: models.HashicorpCloudPackerBuildStatusUNSET,
},
}

svc.CreateBuildResp.Build.ComponentType = params.Body.Build.ComponentType
svc.CreateBuildResp.Build.IterationID = params.IterationID
payload.Build.ComponentType = params.Body.Build.ComponentType
payload.Build.IterationID = params.IterationID

ok := packerSvc.NewPackerServiceCreateBuildOK()
ok.Payload = svc.CreateBuildResp
ok.Payload = payload

return ok, nil
}
Expand All @@ -192,7 +211,10 @@ func (svc *MockPackerClientService) PackerServiceUpdateBuild(params *packerSvc.P
return nil, errors.New("No build status was passed in")
}

svc.UpdateBuildCalled = true
if svc.TrackCalledServiceMethods {
svc.UpdateBuildCalled = true
}

ok := packerSvc.NewPackerServiceUpdateBuildOK()
ok.Payload = &models.HashicorpCloudPackerUpdateBuildResponse{
Build: &models.HashicorpCloudPackerBuild{
Expand Down
5 changes: 4 additions & 1 deletion internal/registry/types.bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ func (b *Bucket) PopulateIteration(ctx context.Context) error {

var errs *multierror.Error
var wg sync.WaitGroup
var mu sync.Mutex
for _, buildName := range toCreate {
wg.Add(1)
go func(name string) {
Expand All @@ -365,10 +366,12 @@ func (b *Bucket) PopulateIteration(ctx context.Context) error {
return
}

mu.Lock()
errs = multierror.Append(errs, err)
mu.Unlock()
}(buildName)
wg.Wait()
}
wg.Wait()

return errs.ErrorOrNil()
}
Expand Down
17 changes: 7 additions & 10 deletions internal/registry/types.bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/google/go-cmp/cmp"
)

func createInitialTestBucket(t testing.TB) (*Bucket, *MockPackerClientService) {
func createInitialTestBucket(t testing.TB) *Bucket {
oldEnv := os.Getenv("HCP_PACKER_BUILD_FINGERPRINT")
os.Setenv("HCP_PACKER_BUILD_FINGERPRINT", "no-fingerprint-here")
defer func() {
Expand All @@ -22,12 +22,13 @@ func createInitialTestBucket(t testing.TB) (*Bucket, *MockPackerClientService) {
}

mockService := NewMockPackerClientService()
mockService.TrackCalledServiceMethods = false
bucket.Slug = "TestBucket"
bucket.client = &Client{
Packer: mockService,
}

return bucket, mockService
return bucket
}

func checkError(t testing.TB, err error) {
Expand All @@ -41,7 +42,7 @@ func checkError(t testing.TB, err error) {
}

func TestBucket_CreateInitialBuildForIteration(t *testing.T) {
bucket, _ := createInitialTestBucket(t)
bucket := createInitialTestBucket(t)

componentName := "happycloud.image"
bucket.RegisterBuildForComponent(componentName)
Expand Down Expand Up @@ -118,7 +119,7 @@ func TestBucket_UpdateLabelsForBuild(t *testing.T) {
for _, tt := range tc {
tt := tt
t.Run(tt.desc, func(t *testing.T) {
bucket, mockService := createInitialTestBucket(t)
bucket := createInitialTestBucket(t)

componentName := tt.buildName
bucket.RegisterBuildForComponent(componentName)
Expand All @@ -127,13 +128,9 @@ func TestBucket_UpdateLabelsForBuild(t *testing.T) {
bucket.BuildLabels[k] = v
}

err := bucket.PopulateIteration(context.TODO())
err := bucket.CreateInitialBuildForIteration(context.TODO(), componentName)
checkError(t, err)

if !mockService.CreateBuildCalled {
t.Errorf("expected an initial build for %s to be created by calling CreateBuild", componentName)
}

// Assert that the build is stored on the iteration
build, err := bucket.Iteration.Build(componentName)
if err != nil {
Expand Down Expand Up @@ -161,7 +158,7 @@ func TestBucket_UpdateLabelsForBuild(t *testing.T) {
}

func TestBucket_UpdateLabelsForBuild_withMultipleBuilds(t *testing.T) {
bucket, _ := createInitialTestBucket(t)
bucket := createInitialTestBucket(t)

firstComponent := "happycloud.image"
bucket.RegisterBuildForComponent(firstComponent)
Expand Down

0 comments on commit a3898f6

Please sign in to comment.