From 08874007741a9dedfd1b9588632996e228a5995f Mon Sep 17 00:00:00 2001 From: petergmurphy Date: Fri, 18 Feb 2022 12:15:43 +0000 Subject: [PATCH] (GH-349) Add updated install unit tests 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. --- pkg/install/install_test.go | 234 ++++++++++++++++++++++++++++++------ pkg/mock/install_config.go | 37 +++--- 2 files changed, 218 insertions(+), 53 deletions(-) diff --git a/pkg/install/install_test.go b/pkg/install/install_test.go index 8fa8d3f1..c3d2dadf 100644 --- a/pkg/install/install_test.go +++ b/pkg/install/install_test.go @@ -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" @@ -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 @@ -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" @@ -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{ @@ -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{ @@ -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), + }, }, }, { @@ -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), + }, }, }, { @@ -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 @@ -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}, + 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) diff --git a/pkg/mock/install_config.go b/pkg/mock/install_config.go index 6c3a412c..db48227e 100644 --- a/pkg/mock/install_config.go +++ b/pkg/mock/install_config.go @@ -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 - + 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 config_processor.ConfigMetadata{}, 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 }