Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
(GH-349) Add updated install unit tests
Browse files Browse the repository at this point in the history
In this commit, update unit tests have been added for the newly
refactored `pkg/install/install.go`.

The `install_config.go` mock file for mocking the private config
processor has been updated, implementing the new config processor
interface.
  • Loading branch information
petergmurphy committed Feb 18, 2022
1 parent 9c1ebda commit 5431fc1
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 54 deletions.
234 changes: 199 additions & 35 deletions pkg/install/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package install_test
import (
"bytes"
"fmt"
"github.com/puppetlabs/pdkgo/pkg/config_processor"
"io/ioutil"
"net/http"
"path"
"path/filepath"
"testing"

Expand All @@ -14,24 +16,6 @@ import (
"github.com/stretchr/testify/assert"
)

type InstallTest struct {
name string
args args
expected expected
mocks mocks
mockReponses mockReponses
mockExecutions mockExecutions
mockInstallConfig mockInstallConfig
}

// what goes in
type args struct {
templatePath string
targetDir string
gitUri string
force bool
}

// what comes out
type expected struct {
errorMsg string
Expand Down Expand Up @@ -59,14 +43,29 @@ type mockExecutions struct {
}

type mockInstallConfig struct {
ExpectedSourceDir string
ExpectedTargetDir string
ExpectedForce bool
NamespacedPathResponse string
ErrResponse error
expectedConfigFile string
metadata config_processor.ConfigMetadata
ErrResponse error
}

func TestInstall(t *testing.T) {
// what goes in
type args struct {
templatePath string
targetDir string
gitUri string
force bool
}

type InstallTest struct {
name string
args args
expected expected
mocks mocks
mockReponses mockReponses
mockExecutions mockExecutions
mockInstallConfig mockInstallConfig
}

templatePath := "path/to/somewhere"
remoteTemplatPath := "https://somewhere.online/templates"
Expand Down Expand Up @@ -119,9 +118,8 @@ func TestInstall(t *testing.T) {
},
},
mockInstallConfig: mockInstallConfig{
ExpectedSourceDir: filepath.Join(extractionPath, "good-project"),
ExpectedTargetDir: extractionPath,
NamespacedPathResponse: filepath.Join(extractionPath, "puppetlabs/good-project/1.0.0"),
expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
metadata: config_processor.ConfigMetadata{Author: "puppetlabs", Id: "good-project", Version: "1.0.0"},
},
mocks: mocks{
dirs: []string{
Expand Down Expand Up @@ -291,7 +289,7 @@ func TestInstall(t *testing.T) {
{
name: "should download and extract a remote tar.gz to a package folder",
args: args{
templatePath: fmt.Sprintf("%s/%s", remoteTemplatPath, "good-project.tar.gz"),
templatePath: path.Join(remoteTemplatPath, "good-project.tar.gz"),
targetDir: extractionPath,
},
expected: expected{
Expand Down Expand Up @@ -322,9 +320,18 @@ func TestInstall(t *testing.T) {
},
},
mockInstallConfig: mockInstallConfig{
ExpectedSourceDir: filepath.Join(extractionPath, "good-project"),
ExpectedTargetDir: extractionPath,
NamespacedPathResponse: filepath.Join(extractionPath, "puppetlabs/good-project/1.0.0"),
expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
metadata: config_processor.ConfigMetadata{Author: "puppetlabs", Id: "good-project", Version: "1.0.0"},
},
mocks: mocks{
dirs: []string{
remoteTemplatPath,
extractionPath,
filepath.Join(extractionPath, "good-project"),
},
files: map[string]string{
filepath.Join(remoteTemplatPath, "good-project.tar.gz"): string(tarballBytes),
},
},
},
{
Expand Down Expand Up @@ -367,9 +374,18 @@ func TestInstall(t *testing.T) {
responseError: false,
},
mockInstallConfig: mockInstallConfig{
ExpectedSourceDir: filepath.Join(tempWorkingPath, "temp"),
ExpectedTargetDir: templatePath,
NamespacedPathResponse: filepath.Join(templatePath, "test-user/test-template/0.1.0"),
expectedConfigFile: filepath.Join(tempWorkingPath, "temp", "pct-config.yml"),
metadata: config_processor.ConfigMetadata{Author: "test-user", Id: "test-template", Version: "0.1.0"},
},
mocks: mocks{
dirs: []string{
filepath.Join(tempWorkingPath, "temp"),
extractionPath,
filepath.Join(extractionPath, "test-template"),
},
files: map[string]string{
filepath.Join(remoteTemplatPath, "good-project.tar.gz"): string(tarballBytes),
},
},
},
{
Expand Down Expand Up @@ -427,7 +443,8 @@ func TestInstall(t *testing.T) {
IOFS: &afero.IOFS{Fs: fs},
HTTPClient: &mock.HTTPClient{RequestResponse: tt.mockReponses.get.RequestResponse},
Exec: &mock.Exec{ExpectedName: tt.mockExecutions.name, ExpectedArg: tt.mockExecutions.args, ResponseMsg: tt.mockExecutions.responseMsg, ResponseError: tt.mockExecutions.responseError},
ConfigProcessor: &mock.InstallConfig{ExpectedSourceDir: tt.mockInstallConfig.ExpectedSourceDir, ExpectedTargetDir: tt.mockInstallConfig.ExpectedTargetDir, NamespacedPathResponse: tt.mockInstallConfig.NamespacedPathResponse, ExpectedForce: tt.mockInstallConfig.ExpectedForce, ErrResponse: tt.mockInstallConfig.ErrResponse},
ConfigProcessor: &mock.InstallConfig{ExpectedConfigFile: tt.mockInstallConfig.expectedConfigFile, Metadata: tt.mockInstallConfig.metadata, ErrResponse: tt.mockInstallConfig.ErrResponse},
ConfigFileName: "pct-config.yml",
}

var err error
Expand All @@ -439,7 +456,154 @@ func TestInstall(t *testing.T) {
returnedPath, err = installer.Install(tt.args.templatePath, tt.args.targetDir, tt.args.force)
}

if tt.expected.errorMsg != "" && err != nil {
if tt.expected.errorMsg != "" {
assert.Contains(t, err.Error(), tt.expected.errorMsg)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expected.filepath, returnedPath)
})
}
}

func TestInstaller_InstallFromConfig(t *testing.T) {
type args struct {
configFile string
targetDir string
force bool
}

type InstallFromConfigTest struct {
name string
args args
expected expected
mocks mocks
mockInstallConfig mockInstallConfig
}

extractionPath := "path/to/extract/to"

tests := []InstallFromConfigTest{
{
name: "Installs successfully and returns the correct namespaced path",
mockInstallConfig: mockInstallConfig{
metadata: config_processor.ConfigMetadata{
Author: "puppetlabs",
Id: "good-project",
Version: "0.1.0",
},
expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
},
args: args{configFile: filepath.Join(extractionPath, "pct-config.yml"), targetDir: extractionPath},
expected: expected{
filepath: filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0"),
},
mocks: mocks{
dirs: []string{
extractionPath,
},
},
},
{
name: "Install fails because of a missing attribute",
mockInstallConfig: mockInstallConfig{
metadata: config_processor.ConfigMetadata{
Author: "puppetlabs",
Version: "0.1.0",
},
expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
ErrResponse: fmt.Errorf("attribute missing in config metadata: id"),
},
args: args{configFile: filepath.Join(extractionPath, "pct-config.yml"), targetDir: extractionPath},
expected: expected{
errorMsg: "attribute missing in config metadata: id",
},
mocks: mocks{
dirs: []string{
extractionPath,
},
},
},
// Neither of these tests work. Most likely a problem with the AFS.Rename(path1, path2) function (pkg/install/install.go:202)
//{
// name: "Force installs over a template with the same namespaced path",
// mockInstallConfig: mockInstallConfig{
// metadata: config_processor.ConfigMetadata{
// Author: "puppetlabs",
// Id: "good-project",
// Version: "0.1.0",
// },
// expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
// },
// args: args{configFile: filepath.Join(extractionPath, "pct-config.yml"), targetDir: extractionPath, force: true},
// expected: expected{
// filepath: filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0"),
// },
// mocks: mocks{
// dirs: []string{
// extractionPath,
// },
// files: map[string]string{ // Writes a config file to namespaced path to simulate a previously installed template
// filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0/pct-config.yml"): "",
// filepath.Join(extractionPath, "good-project", "pct-config.yml"): "",
// },
// },
//},
//{
// name: "Fails to install as a template already exists on the namespaced path and force is false",
// mockInstallConfig: mockInstallConfig{
// metadata: config_processor.ConfigMetadata{
// Author: "puppetlabs",
// Id: "good-project",
// Version: "0.1.0",
// },
// expectedConfigFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"),
// },
// args: args{configFile: filepath.Join(extractionPath, "good-project", "pct-config.yml"), targetDir: extractionPath, force: false},
// expected: expected{
// filepath: filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0"),
// errorMsg: "Template already installed",
// },
// mocks: mocks{
// dirs: []string{
// extractionPath,
// filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0"),
// },
// files: map[string]string{ // Writes a config file to namespaced path to simulate a previously installed template
// filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0/pct-config.yml"): "test1",
// filepath.Join(extractionPath, "puppetlabs/good-project/0.1.0/content/testfile"): "test1",
// filepath.Join(extractionPath, "good-project", "pct-config.yml"): "test2",
// },
// },
//},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}

for _, path := range tt.mocks.dirs {
afs.Mkdir(path, 0750) //nolint:gosec,errcheck // this result is not used in a secure application
}

for file, content := range tt.mocks.files {
config, _ := afs.Create(file) //nolint:gosec,errcheck // this result is not used in a secure application
config.Write([]byte(content)) //nolint:errcheck
}

p := &install.Installer{
Tar: &mock.Tar{},
Gunzip: &mock.Gunzip{},
AFS: afs,
IOFS: &afero.IOFS{Fs: fs},
HTTPClient: &mock.HTTPClient{},
Exec: &mock.Exec{},
ConfigProcessor: &mock.InstallConfig{ExpectedConfigFile: tt.args.configFile, Metadata: tt.mockInstallConfig.metadata, ErrResponse: tt.mockInstallConfig.ErrResponse},
ConfigFileName: "pct-config.yml",
}
returnedPath, err := p.InstallFromConfig(tt.args.configFile, tt.args.targetDir, tt.args.force)
if tt.expected.errorMsg != "" {
assert.Contains(t, err.Error(), tt.expected.errorMsg)
} else {
assert.NoError(t, err)
Expand Down
39 changes: 20 additions & 19 deletions pkg/mock/install_config.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
package mock

import "fmt"
import (
"fmt"
"github.com/puppetlabs/pdkgo/pkg/config_processor"
)

type InstallConfig struct {
ExpectedSourceDir string
ExpectedTargetDir string
ExpectedForce bool
NamespacedPathResponse string

ErrResponse error
ExpectedConfigFile string
Metadata config_processor.ConfigMetadata
ErrResponse error
}

func (ic *InstallConfig) ProcessConfig(sourceDir, targetDir string, force bool) (string, error) {
func (ic *InstallConfig) GetConfigMetadata(configFile string) (metadata config_processor.ConfigMetadata, err error) {
if ic.ErrResponse != nil {
return "", ic.ErrResponse
return metadata, ic.ErrResponse
}

if sourceDir != ic.ExpectedSourceDir {
return "", fmt.Errorf("sourceDir (%v) did not match expected value (%v)", sourceDir, ic.ExpectedSourceDir)
if ic.ExpectedConfigFile != configFile {
return ic.Metadata, fmt.Errorf("configFile (%v) did not match expected value (%v)", configFile, ic.ExpectedConfigFile)
}

if targetDir != ic.ExpectedTargetDir {
return "", fmt.Errorf("targetDir (%v) did not match expected value (%v)", targetDir, ic.ExpectedTargetDir)
return ic.Metadata, nil
}

func (ic *InstallConfig) CheckConfig(configFile string) error {
if ic.ErrResponse != nil {
return ic.ErrResponse
}

if force != ic.ExpectedForce {
return "", fmt.Errorf("force (%v) did not match expected value (%v)", force, ic.ExpectedForce)
if ic.ExpectedConfigFile != configFile {
return fmt.Errorf("configFile (%v) did not match expected value (%v)", configFile, ic.ExpectedConfigFile)
}
return ic.NamespacedPathResponse, nil
}

func (ic *InstallConfig) CheckConfig(configFile string) error {
return nil
return ic.ErrResponse
}

0 comments on commit 5431fc1

Please sign in to comment.