1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-11-24 08:32:32 +02:00

feat(newmanExecute): golang implmementation for newmanExecute (#2513)

* Automates first parts of newmanExecute.groovy

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds newman installation

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Removes warning

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* makes tests robust for later shell calls

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds version logging

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds tests for version logging

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds newman shell execution

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Prepare cloud foundry apps with secrets handling

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds further process to CF Utils

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Fixes unit test

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adds error category

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Add fix to execute step locally

Currently only tested on windows machine locally in powershell.

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* Adapt unit test to fix of runCommand

Signed-off-by: Fabian Reh <fabian.reh@sap.com>

* refactored golang step to newmanExecute

* wip

* added test config

* refactored newmanExecute groovy wrapper step

* exclude newmanExecute from common step test

* cleaups

* add credential support

* fix groovy credential providing

* add import

* add stageName

* define script

* remove unused vars

* add import

* fix iterator ref

* golang secret handling and cleanups

* wip

* wip

* wip

* update go step

* implement cf credential proposal

* testRepository functionality implemented

* register secrets to logger

* add missing dependecies

* test xsuaa credential handling

* wip

* wip

* cleanups

* add import

* remove mandatory params

* add container definition

* test runCommand

* test runCommand

* fix npm path

* fix npm path

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* added newmanEnvironment to templating

* wip

* use env and globals params in runCommand when no templating

* fix condition

* wip

* reverted config edit

* updated documentation

* install with shell

* wip

* wip

* fix tests

* refactor tests

* wip

* remove old test

* wip

* escape go tmpl

* escape go tmpl

* fix defaults

* add doc comment

* remove test case

* refactored newman commands

* add cli reporter

* refactor options

* mock os getenv and fix all tests

* refactoring and doc update

* go generate

* small refactor

* spelling

* fix newman doc

* remove MaskPasswords wrapper; fix stash bug;

* docu fix

Co-authored-by: Fabian Reh <fabian.reh@sap.com>
This commit is contained in:
lndrschlz 2021-03-17 08:08:33 +01:00 committed by GitHub
parent b04e3140de
commit dea96a3ba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 901 additions and 394 deletions

View File

@ -61,6 +61,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"mavenExecuteIntegration": mavenExecuteIntegrationMetadata(),
"mavenExecuteStaticCodeChecks": mavenExecuteStaticCodeChecksMetadata(),
"mtaBuild": mtaBuildMetadata(),
"newmanExecute": newmanExecuteMetadata(),
"nexusUpload": nexusUploadMetadata(),
"npmExecuteLint": npmExecuteLintMetadata(),
"npmExecuteScripts": npmExecuteScriptsMetadata(),

217
cmd/newmanExecute.go Normal file
View File

@ -0,0 +1,217 @@
package cmd
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
type newmanExecuteUtils interface {
Glob(pattern string) (matches []string, err error)
RunExecutable(executable string, params ...string) error
Getenv(key string) string
}
type newmanExecuteUtilsBundle struct {
*command.Command
*piperutils.Files
}
func newNewmanExecuteUtils() newmanExecuteUtils {
utils := newmanExecuteUtilsBundle{
Command: &command.Command{},
Files: &piperutils.Files{},
}
// Reroute command output to logging framework
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func newmanExecute(config newmanExecuteOptions, _ *telemetry.CustomData) {
utils := newNewmanExecuteUtils()
err := runNewmanExecute(&config, utils)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runNewmanExecute(config *newmanExecuteOptions, utils newmanExecuteUtils) error {
if config.NewmanRunCommand != "" {
log.Entry().Warn("found configuration for deprecated parameter newmanRunCommand, please use runOptions instead")
log.Entry().Warn("setting runOptions to value of deprecated parameter newmanRunCommand")
config.RunOptions = strings.Split(config.NewmanRunCommand, " ")
}
collectionList, err := utils.Glob(config.NewmanCollection)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrapf(err, "Could not execute global search for '%v'", config.NewmanCollection)
}
if collectionList == nil {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("no collection found with pattern '%v'", config.NewmanCollection)
}
log.Entry().Infof("Found the following newman collections: %v", collectionList)
err = logVersions(utils)
if err != nil {
return err
}
err = installNewman(config.NewmanInstallCommand, utils)
if err != nil {
return err
}
// resolve environment and globals if not covered by templating
options := resolveOptions(config)
for _, collection := range collectionList {
runOptions := []string{}
runOptions, err := resolveTemplate(config, collection)
if err != nil {
return err
}
commandSecrets := handleCfAppCredentials(config)
runOptions = append(runOptions, options...)
runOptions = append(runOptions, commandSecrets...)
if !config.FailOnError {
runOptions = append(runOptions, "--suppress-exit-code")
}
newmanPath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/newman")
err = utils.RunExecutable(newmanPath, runOptions...)
if err != nil {
log.SetErrorCategory(log.ErrorService)
return errors.Wrap(err, "The execution of the newman tests failed, see the log for details.")
}
}
return nil
}
func logVersions(utils newmanExecuteUtils) error {
err := utils.RunExecutable("node", "--version")
if err != nil {
log.SetErrorCategory(log.ErrorInfrastructure)
return errors.Wrap(err, "error logging node version")
}
err = utils.RunExecutable("npm", "--version")
if err != nil {
log.SetErrorCategory(log.ErrorInfrastructure)
return errors.Wrap(err, "error logging npm version")
}
return nil
}
func installNewman(newmanInstallCommand string, utils newmanExecuteUtils) error {
installCommandTokens := strings.Split(newmanInstallCommand, " ")
installCommandTokens = append(installCommandTokens, "--prefix=~/.npm-global")
err := utils.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(err, "error installing newman")
}
return nil
}
func resolveOptions(config *newmanExecuteOptions) []string {
options := []string{}
if config.NewmanEnvironment != "" && !contains(config.RunOptions, "{{.Config.NewmanEnvironment}}") {
options = append(options, "--environment")
options = append(options, config.NewmanEnvironment)
}
if config.NewmanGlobals != "" && !contains(config.RunOptions, "{{.Config.NewmanGlobals}}") {
options = append(options, "--globals")
options = append(options, config.NewmanGlobals)
}
return options
}
func resolveTemplate(config *newmanExecuteOptions, collection string) ([]string, error) {
cmd := []string{}
collectionDisplayName := defineCollectionDisplayName(collection)
type TemplateConfig struct {
Config interface{}
CollectionDisplayName string
NewmanCollection string
}
for _, runOption := range config.RunOptions {
templ, err := template.New("template").Parse(runOption)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return nil, errors.Wrap(err, "could not parse newman command template")
}
buf := new(bytes.Buffer)
err = templ.Execute(buf, TemplateConfig{
Config: config,
CollectionDisplayName: collectionDisplayName,
NewmanCollection: collection,
})
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return nil, errors.Wrap(err, "error on executing template")
}
cmd = append(cmd, buf.String())
}
return cmd, nil
}
func defineCollectionDisplayName(collection string) string {
replacedSeparators := strings.Replace(collection, string(filepath.Separator), "_", -1)
displayName := strings.Split(replacedSeparators, ".")
if displayName[0] == "" && len(displayName) >= 2 {
return displayName[1]
}
return displayName[0]
}
func handleCfAppCredentials(config *newmanExecuteOptions) []string {
commandSecrets := []string{}
if len(config.CfAppsWithSecrets) > 0 {
for _, appName := range config.CfAppsWithSecrets {
var clientID, clientSecret string
clientID = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientid")
clientSecret = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientsecret")
if clientID != "" && clientSecret != "" {
log.RegisterSecret(clientSecret)
secretVar := fmt.Sprintf("--env-var %v_clientid=%v --env-var %v_clientsecret=%v", appName, clientID, appName, clientSecret)
commandSecrets = append(commandSecrets, strings.Split(secretVar, " ")...)
log.Entry().Infof("secrets found for app %v and forwarded to newman as --env-var parameter", appName)
} else {
log.Entry().Errorf("cannot fetch secrets from environment variables for app %v", appName)
}
}
}
return commandSecrets
}
func contains(slice []string, substr string) bool {
for _, e := range slice {
if strings.Contains(e, substr) {
return true
}
}
return false
}
func (utils newmanExecuteUtilsBundle) Getenv(key string) string {
return os.Getenv(key)
}

View File

@ -0,0 +1,181 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/spf13/cobra"
)
type newmanExecuteOptions struct {
NewmanCollection string `json:"newmanCollection,omitempty"`
NewmanRunCommand string `json:"newmanRunCommand,omitempty"`
RunOptions []string `json:"runOptions,omitempty"`
NewmanInstallCommand string `json:"newmanInstallCommand,omitempty"`
NewmanEnvironment string `json:"newmanEnvironment,omitempty"`
NewmanGlobals string `json:"newmanGlobals,omitempty"`
FailOnError bool `json:"failOnError,omitempty"`
CfAppsWithSecrets []string `json:"cfAppsWithSecrets,omitempty"`
}
// NewmanExecuteCommand Installs newman and executes specified newman collections.
func NewmanExecuteCommand() *cobra.Command {
const STEP_NAME = "newmanExecute"
metadata := newmanExecuteMetadata()
var stepConfig newmanExecuteOptions
var startTime time.Time
var createNewmanExecuteCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Installs newman and executes specified newman collections.",
Long: `This script executes [Postman](https://www.getpostman.com) tests from a collection via the [Newman](https://www.getpostman.com/docs/v6/postman/collection_runs/command_line_integration_with_newman) command line tool.`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
return nil
},
Run: func(_ *cobra.Command, _ []string) {
telemetryData := telemetry.CustomData{}
telemetryData.ErrorCode = "1"
handler := func() {
config.RemoveVaultSecretFiles()
telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
telemetryData.ErrorCategory = log.GetErrorCategory().String()
telemetry.Send(&telemetryData)
}
log.DeferExitHandler(handler)
defer handler()
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
newmanExecute(stepConfig, &telemetryData)
telemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addNewmanExecuteFlags(createNewmanExecuteCmd, &stepConfig)
return createNewmanExecuteCmd
}
func addNewmanExecuteFlags(cmd *cobra.Command, stepConfig *newmanExecuteOptions) {
cmd.Flags().StringVar(&stepConfig.NewmanCollection, "newmanCollection", `**/*.postman_collection.json`, "The test collection that should be executed. This could also be a file pattern.")
cmd.Flags().StringVar(&stepConfig.NewmanRunCommand, "newmanRunCommand", os.Getenv("PIPER_newmanRunCommand"), "+++ Deprecated +++ Please use list parameter `runOptions` instead.")
cmd.Flags().StringSliceVar(&stepConfig.RunOptions, "runOptions", []string{`run`, `{{.NewmanCollection}}`, `--reporters`, `cli,junit,html`, `--reporter-junit-export`, `target/newman/TEST-{{.CollectionDisplayName}}.xml`, `--reporter-html-export`, `target/newman/TEST-{{.CollectionDisplayName}}.html`}, "The newman command that will be executed inside the docker container.")
cmd.Flags().StringVar(&stepConfig.NewmanInstallCommand, "newmanInstallCommand", `npm install newman newman-reporter-html --global --quiet`, "The shell command that will be executed inside the docker container to install Newman.")
cmd.Flags().StringVar(&stepConfig.NewmanEnvironment, "newmanEnvironment", os.Getenv("PIPER_newmanEnvironment"), "Specify an environment file path or URL. Environments provide a set of variables that one can use within collections.")
cmd.Flags().StringVar(&stepConfig.NewmanGlobals, "newmanGlobals", os.Getenv("PIPER_newmanGlobals"), "Specify the file path or URL for global variables. Global variables are similar to environment variables but have a lower precedence and can be overridden by environment variables having the same name.")
cmd.Flags().BoolVar(&stepConfig.FailOnError, "failOnError", true, "Defines the behavior, in case tests fail.")
cmd.Flags().StringSliceVar(&stepConfig.CfAppsWithSecrets, "cfAppsWithSecrets", []string{}, "List of CloudFoundry apps with secrets")
}
// retrieve step metadata
func newmanExecuteMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "newmanExecute",
Aliases: []config.Alias{},
Description: "Installs newman and executes specified newman collections.",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Resources: []config.StepResources{
{Name: "tests", Type: "stash"},
},
Parameters: []config.StepParameters{
{
Name: "newmanCollection",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "newmanRunCommand",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "runOptions",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "newmanInstallCommand",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "newmanEnvironment",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "newmanGlobals",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "failOnError",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "cfAppsWithSecrets",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
Aliases: []config.Alias{},
},
},
},
Containers: []config.Container{
{Name: "newman", Image: "node:lts-stretch", WorkingDir: "/home/node"},
},
},
}
return theMetaData
}

View File

@ -0,0 +1,17 @@
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewmanExecuteCommand(t *testing.T) {
t.Parallel()
testCmd := NewmanExecuteCommand()
// only high level testing performed - details are tested in step generation procedure
assert.Equal(t, "newmanExecute", testCmd.Use, "command name incorrect")
}

324
cmd/newmanExecute_test.go Normal file
View File

@ -0,0 +1,324 @@
package cmd
import (
"path/filepath"
"strings"
"testing"
sliceUtils "github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
type executedExecutables struct {
executable string
params []string
}
type newmanExecuteMockUtils struct {
// *mock.ExecMockRunner
// *mock.FilesMock
errorOnGlob bool
errorOnNewmanInstall bool
errorOnRunShell bool
errorOnNewmanExecution bool
errorOnLoggingNode bool
errorOnLoggingNpm bool
executedExecutables []executedExecutables
filesToFind []string
commandIndex int
}
func newNewmanExecuteMockUtils() newmanExecuteMockUtils {
return newmanExecuteMockUtils{
filesToFind: []string{"localFile.json", "localFile2.json"},
}
}
func TestRunNewmanExecute(t *testing.T) {
t.Parallel()
allFineConfig := newmanExecuteOptions{
NewmanCollection: "**.json",
NewmanEnvironment: "env.json",
NewmanGlobals: "globals.json",
NewmanInstallCommand: "npm install newman --global --quiet",
NewmanRunCommand: "run {{.NewmanCollection}} --environment {{.Config.NewmanEnvironment}} --globals {{.Config.NewmanGlobals}} --reporters junit,html --reporter-junit-export target/newman/TEST-{{.CollectionDisplayName}}.xml --reporter-html-export target/newman/TEST-{{.CollectionDisplayName}}.html",
}
t.Run("happy path", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.NoError(t, err)
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "node", params: []string{"--version"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "npm", params: []string{"--version"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "npm", params: []string{"install", "newman", "--global", "--quiet", "--prefix=~/.npm-global"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "/home/node/.npm-global/bin/newman", params: []string{"run", "localFile.json", "--environment", "env.json", "--globals", "globals.json", "--reporters", "junit,html", "--reporter-junit-export", "target/newman/TEST-localFile.xml", "--reporter-html-export", "target/newman/TEST-localFile.html", "--suppress-exit-code"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "/home/node/.npm-global/bin/newman", params: []string{"run", "localFile2.json", "--environment", "env.json", "--globals", "globals.json", "--reporters", "junit,html", "--reporter-junit-export", "target/newman/TEST-localFile2.xml", "--reporter-html-export", "target/newman/TEST-localFile2.html", "--suppress-exit-code"}})
})
t.Run("happy path with fail on error", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
fineConfig := allFineConfig
fineConfig.FailOnError = true
// test
err := runNewmanExecute(&fineConfig, &utils)
// assert
assert.NoError(t, err)
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "node", params: []string{"--version"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "npm", params: []string{"--version"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "npm", params: []string{"install", "newman", "--global", "--quiet", "--prefix=~/.npm-global"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "/home/node/.npm-global/bin/newman", params: []string{"run", "localFile.json", "--environment", "env.json", "--globals", "globals.json", "--reporters", "junit,html", "--reporter-junit-export", "target/newman/TEST-localFile.xml", "--reporter-html-export", "target/newman/TEST-localFile.html"}})
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "/home/node/.npm-global/bin/newman", params: []string{"run", "localFile2.json", "--environment", "env.json", "--globals", "globals.json", "--reporters", "junit,html", "--reporter-junit-export", "target/newman/TEST-localFile2.xml", "--reporter-html-export", "target/newman/TEST-localFile2.html"}})
})
t.Run("error on newman execution", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
utils.errorOnNewmanExecution = true
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.EqualError(t, err, "The execution of the newman tests failed, see the log for details.: error on newman execution")
})
t.Run("error on newman installation", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
utils.errorOnNewmanInstall = true
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.EqualError(t, err, "error installing newman: error on newman install")
})
t.Run("error on npm version logging", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
utils.errorOnLoggingNpm = true
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.EqualError(t, err, "error logging npm version: error on RunExecutable")
})
t.Run("error on template resolution", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
config := allFineConfig
config.NewmanRunCommand = "this is my erroneous command {{.collectionDisplayName}"
// test
err := runNewmanExecute(&config, &utils)
// assert
assert.EqualError(t, err, "could not parse newman command template: template: template:1: unexpected \"}\" in operand")
})
t.Run("error on file search", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
utils.filesToFind = nil
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.EqualError(t, err, "no collection found with pattern '**.json'")
})
t.Run("no newman file", func(t *testing.T) {
t.Parallel()
// init
utils := newNewmanExecuteMockUtils()
utils.errorOnGlob = true
// test
err := runNewmanExecute(&allFineConfig, &utils)
// assert
assert.EqualError(t, err, "Could not execute global search for '**.json': error on Glob")
})
}
func TestDefineCollectionDisplayName(t *testing.T) {
t.Parallel()
t.Run("normal path", func(t *testing.T) {
t.Parallel()
path := filepath.Join("dir1", "dir2", "fancyFile.txt")
result := defineCollectionDisplayName(path)
assert.Equal(t, "dir1_dir2_fancyFile", result)
})
t.Run("directory", func(t *testing.T) {
t.Parallel()
path := filepath.Join("dir1", "dir2", "dir3")
result := defineCollectionDisplayName(path)
assert.Equal(t, "dir1_dir2_dir3", result)
})
t.Run("directory with dot prefix", func(t *testing.T) {
t.Parallel()
path := filepath.Join(".dir1", "dir2", "dir3", "file.json")
result := defineCollectionDisplayName(path)
assert.Equal(t, "dir1_dir2_dir3_file", result)
})
t.Run("empty path", func(t *testing.T) {
t.Parallel()
path := filepath.Join(".")
result := defineCollectionDisplayName(path)
assert.Equal(t, "", result)
})
}
func TestResolveTemplate(t *testing.T) {
t.Parallel()
t.Run("nothing to replace", func(t *testing.T) {
t.Parallel()
// config := newmanExecuteOptions{NewmanRunCommand: "this is my fancy command"}
config := newmanExecuteOptions{RunOptions: []string{"this", "is", "my", "fancy", "command"}}
cmd, err := resolveTemplate(&config, "collectionsDisplayName")
assert.NoError(t, err)
assert.Equal(t, []string{"this", "is", "my", "fancy", "command"}, cmd)
})
t.Run("replace display name", func(t *testing.T) {
t.Parallel()
config := newmanExecuteOptions{RunOptions: []string{"this", "is", "my", "fancy", "command", "{{.CollectionDisplayName}}"}}
cmd, err := resolveTemplate(&config, "theDisplayName")
assert.NoError(t, err)
assert.Equal(t, []string{"this", "is", "my", "fancy", "command", "theDisplayName"}, cmd)
})
t.Run("error when parameter cannot be resolved", func(t *testing.T) {
t.Parallel()
config := newmanExecuteOptions{RunOptions: []string{"this", "is", "my", "fancy", "command", "{{.collectionDisplayName}}"}}
_, err := resolveTemplate(&config, "theDisplayName")
assert.EqualError(t, err, "error on executing template: template: template:1:2: executing \"template\" at <.collectionDisplayName>: can't evaluate field collectionDisplayName in type cmd.TemplateConfig")
})
t.Run("error when template cannot be parsed", func(t *testing.T) {
t.Parallel()
config := newmanExecuteOptions{RunOptions: []string{"this", "is", "my", "fancy", "command", "{{.collectionDisplayName}"}}
_, err := resolveTemplate(&config, "theDisplayName")
assert.EqualError(t, err, "could not parse newman command template: template: template:1: unexpected \"}\" in operand")
})
}
func TestLogVersions(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
utils := newNewmanExecuteMockUtils()
err := logVersions(&utils)
assert.NoError(t, err)
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "npm", params: []string{"--version"}})
})
t.Run("error in node execution", func(t *testing.T) {
utils := newNewmanExecuteMockUtils()
utils.errorOnLoggingNode = true
err := logVersions(&utils)
assert.EqualError(t, err, "error logging node version: error on RunExecutable")
})
t.Run("error in npm execution", func(t *testing.T) {
utils := newNewmanExecuteMockUtils()
utils.errorOnLoggingNpm = true
err := logVersions(&utils)
assert.EqualError(t, err, "error logging npm version: error on RunExecutable")
assert.Contains(t, utils.executedExecutables, executedExecutables{executable: "node", params: []string{"--version"}})
})
}
func (e *newmanExecuteMockUtils) Glob(string) (matches []string, err error) {
if e.errorOnGlob {
return nil, errors.New("error on Glob")
}
return e.filesToFind, nil
}
func (e *newmanExecuteMockUtils) RunExecutable(executable string, params ...string) error {
if e.errorOnRunShell {
return errors.New("error on RunExecutable")
}
if e.errorOnLoggingNode && executable == "node" && params[0] == "--version" {
return errors.New("error on RunExecutable")
}
if e.errorOnLoggingNpm && executable == "npm" && params[0] == "--version" {
return errors.New("error on RunExecutable")
}
if e.errorOnNewmanExecution && strings.Contains(executable, "newman") {
return errors.New("error on newman execution")
}
if e.errorOnNewmanInstall && sliceUtils.ContainsString(params, "install") {
return errors.New("error on newman install")
}
length := len(e.executedExecutables)
if length < e.commandIndex+1 {
e.executedExecutables = append(e.executedExecutables, executedExecutables{})
length++
}
e.executedExecutables[length-1].executable = executable
e.executedExecutables[length-1].params = params
e.commandIndex++
return nil
}
func (e *newmanExecuteMockUtils) Getenv(key string) string {
if key == "HOME" {
return "/home/node"
}
return ""
}

View File

@ -127,6 +127,7 @@ func Execute() {
rootCmd.AddCommand(VaultRotateSecretIdCommand())
rootCmd.AddCommand(CheckChangeInDevelopmentCommand())
rootCmd.AddCommand(TransportRequestUploadCTSCommand())
rootCmd.AddCommand(NewmanExecuteCommand())
rootCmd.AddCommand(IntegrationArtifactDeployCommand())
rootCmd.AddCommand(TransportRequestUploadSOLMANCommand())
rootCmd.AddCommand(IntegrationArtifactUpdateConfigurationCommand())

View File

@ -1,5 +1,8 @@
# ${docGenStepName}
!!! warning "Deprecation notice"
Details of changes after the step migration to a golang can be found [below](#exceptions).
## ${docGenDescription}
## Prerequisites
@ -18,7 +21,30 @@ Step uses `dockerExecute` inside.
## Exceptions
none
The step has been migrated into a golang-based step. The following release notes belong to the new implementation:
- **newmanRunCommand**:
The parameter `newmanRunCommand` is deprecated by now and is replaced by list parameter `runOptions`. For backward compatibility, the `newmanRunCommand` parameter will still be used if configured. Nevertheless, using this parameter can break the step in some cases, e.g. when spaces are used in single quoted strings like spaces in file names. Also Groovy Templating is deprecated and now replaced by Go Templating. The example show the required changes:
```yaml
# deprecated groovy default
newmanRunCommand: "run '${config.newmanCollection}' --environment '${config.newmanEnvironment}' --globals '${config.newmanGlobals}' --reporters junit,html --reporter-junit-export 'target/newman/TEST-${collectionDisplayName}.xml' --reporter-html-export 'target/newman/TEST-${collectionDisplayName}.html'"
```
```yaml
# new run options using golang templating
{{`runOptions: ["run", "{{.NewmanCollection}}", "--environment", "{{.Config.NewmanEnvironment}}", "--globals", "{{.Config.NewmanGlobals}}", "--reporters", "junit,html", "--reporter-junit-export", "target/newman/TEST-{{.CollectionDisplayName}}.xml", "--reporter-html-export", "target/newman/TEST-{{.CollectionDisplayName}}.html"]`}}
```
If the following error occurs during the pipeline run, the `newmanRunCommand` is probably still configured with the deprecated groovy template syntax:
> info newmanExecute - error: collection could not be loaded
> info newmanExecute - unable to read data from file "${config.newmanCollection}"
> info newmanExecute - ENOENT: no such file or directory, open '${config.newmanCollection}'
- **newmanEnvironment and newmanGlobals**:
Referencing `newmanEnvironment` and `newmanGlobals` in the runOptions is redundant now. Both parameters are added to runCommand using `newmanEnvironment` and `newmanGlobals` from config when configured and not referenced by go templating using `"--environment", "{{`{{.Config.NewmanEnvironment}}`}}"` and `"--globals", "{{`{{.Config.NewmanGlobals}}`}}"` as shown above.
## Example

View File

@ -358,16 +358,6 @@ steps:
neoTargets: []
enableZeroDowntimeDeployment: false
parallelExecution: false
newmanExecute:
dockerImage: 'node:lts-stretch'
failOnError: true
newmanCollection: '**/*.postman_collection.json'
newmanEnvironment: ''
newmanGlobals: ''
newmanInstallCommand: 'npm install newman newman-reporter-html --global --quiet'
newmanRunCommand: "run '${config.newmanCollection}' --environment '${config.newmanEnvironment}' --globals '${config.newmanGlobals}' --reporters junit,html --reporter-junit-export 'target/newman/TEST-${collectionDisplayName}.xml' --reporter-html-export 'target/newman/TEST-${collectionDisplayName}.html'"
stashContent:
- 'tests'
npmExecute:
dockerImage: 'node:lts-stretch'
npmExecuteScripts:

View File

@ -0,0 +1,86 @@
metadata:
name: newmanExecute
description: Installs newman and executes specified newman collections.
longDescription: |
This script executes [Postman](https://www.getpostman.com) tests from a collection via the [Newman](https://www.getpostman.com/docs/v6/postman/collection_runs/command_line_integration_with_newman) command line tool.
spec:
inputs:
resources:
- name: tests
type: stash
params:
- name: newmanCollection
description: The test collection that should be executed. This could also be a file pattern.
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
default: '**/*.postman_collection.json'
- name: newmanRunCommand
description: +++ Deprecated +++ Please use list parameter `runOptions` instead.
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: runOptions
description: The newman command that will be executed inside the docker container.
scope:
- PARAMETERS
- STAGES
- STEPS
type: "[]string"
default:
- run
- "{{.NewmanCollection}}"
- --reporters
- cli,junit,html
- --reporter-junit-export
- target/newman/TEST-{{.CollectionDisplayName}}.xml
- --reporter-html-export
- target/newman/TEST-{{.CollectionDisplayName}}.html
- name: newmanInstallCommand
description: The shell command that will be executed inside the docker container to install Newman.
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
default: npm install newman newman-reporter-html --global --quiet
- name: newmanEnvironment
description: Specify an environment file path or URL. Environments provide a set of variables that one can use within collections.
longDescription: see also [Newman docs](https://github.com/postmanlabs/newman#newman-run-collection-file-source-options)
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: newmanGlobals
description: Specify the file path or URL for global variables. Global variables are similar to environment variables but have a lower precedence and can be overridden by environment variables having the same name.
longDescription: see also [Newman docs](https://github.com/postmanlabs/newman#newman-run-collection-file-source-options)
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: failOnError
description: Defines the behavior, in case tests fail.
scope:
- PARAMETERS
- STAGES
- STEPS
type: bool
default: true
- name: cfAppsWithSecrets
description: List of CloudFoundry apps with secrets
longDescription: Define name array of cloud foundry apps deployed for which secrets (clientid and clientsecret) will be appended to the newman command that overrides the environment json entries (--env-var <appName_clientid>=${clientid} & --env-var <appName_clientsecret>=${clientsecret})
scope:
- PARAMETERS
- STAGES
- STEPS
type: "[]string"
containers:
- name: newman
image: node:lts-stretch
workingDir: /home/node

View File

@ -171,6 +171,8 @@ public class CommonStepsTest extends BasePiperTest{
'kanikoExecute', //implementing new golang pattern without fields
'gitopsUpdateDeployment', //implementing new golang pattern without fields
'vaultRotateSecretId', //implementing new golang pattern without fields
'deployIntegrationArtifact', //implementing new golang pattern without fields
'newmanExecute', //implementing new golang pattern without fields
'whitesourceExecuteScan', //implementing new golang pattern without fields
'uiVeri5ExecuteTests', //implementing new golang pattern without fields
'integrationArtifactDeploy', //implementing new golang pattern without fields

View File

@ -1,206 +0,0 @@
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.Rule
import org.junit.rules.RuleChain
import static org.hamcrest.Matchers.hasItem
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.endsWith
import static org.hamcrest.Matchers.startsWith
import groovy.json.JsonSlurper
import static org.junit.Assert.assertThat
import util.BasePiperTest
import util.JenkinsStepRule
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsShellCallRule
import util.JenkinsDockerExecuteRule
import util.Rules
import org.junit.rules.ExpectedException
import util.JenkinsCredentialsRule
import com.sap.piper.Utils
class NewmanExecuteTest extends BasePiperTest {
private ExpectedException thrown = ExpectedException.none()
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
private JenkinsCredentialsRule jenkinsCredentialsRule = new JenkinsCredentialsRule(this)
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(thrown)
.around(dockerExecuteRule)
.around(shellRule)
.around(jenkinsCredentialsRule)
.around(loggingRule)
.around(stepRule) // needs to be activated after dockerExecuteRule, otherwise executeDocker is not mocked
def gitMap
@Before
void init() throws Exception {
helper.registerAllowedMethod('stash', [String.class], null)
helper.registerAllowedMethod('git', [Map.class], {m ->
gitMap = m
})
helper.registerAllowedMethod("findFiles", [Map.class], { map ->
def files
if(map.glob == 'notFound.json')
files = []
else if(map.glob == '**/*.postman_collection.json')
files = [
new File("testCollectionsFolder/A.postman_collection.json"),
new File("testCollectionsFolder/B.postman_collection.json")
]
else
files = [new File(map.glob)]
return files.toArray()
})
Utils.metaClass.echo = { def m -> }
}
@After
public void tearDown() {
Utils.metaClass = null
}
@Test
void testExecuteNewmanDefault() throws Exception {
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanCollection: 'testCollection',
newmanEnvironment: 'testEnvironment',
newmanGlobals: 'testGlobals'
)
// asserts
assertThat(shellRule.shell, hasItem(endsWith('npm install newman newman-reporter-html --global --quiet')))
assertThat(shellRule.shell, hasItem(endsWith('newman run \'testCollection\' --environment \'testEnvironment\' --globals \'testGlobals\' --reporters junit,html --reporter-junit-export \'target/newman/TEST-testCollection.xml\' --reporter-html-export \'target/newman/TEST-testCollection.html\'')))
assertThat(dockerExecuteRule.dockerParams.dockerImage, is('node:lts-stretch'))
assertThat(loggingRule.log, containsString('[newmanExecute] Found files [testCollection]'))
assertJobStatusSuccess()
}
@Test
void testDockerFromCustomStepConfiguration() {
def expectedImage = 'image:test'
def expectedEnvVars = ['env1': 'value1', 'env2': 'value2']
def expectedOptions = '--opt1=val1 --opt2=val2 --opt3'
def expectedWorkspace = '/path/to/workspace'
nullScript.commonPipelineEnvironment.configuration = [steps:[newmanExecute:[
dockerImage: expectedImage,
dockerOptions: expectedOptions,
dockerEnvVars: expectedEnvVars,
dockerWorkspace: expectedWorkspace
]]]
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils
)
assert expectedImage == dockerExecuteRule.dockerParams.dockerImage
assert expectedOptions == dockerExecuteRule.dockerParams.dockerOptions
assert expectedEnvVars.equals(dockerExecuteRule.dockerParams.dockerEnvVars)
assert expectedWorkspace == dockerExecuteRule.dockerParams.dockerWorkspace
}
@Test
void testGlobalInstall() throws Exception {
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanCollection: 'testCollection',
newmanEnvironment: 'testEnvironment',
newmanGlobals: 'testGlobals'
)
// asserts
assertThat(shellRule.shell, hasItem(startsWith('NPM_CONFIG_PREFIX=~/.npm-global ')))
assertThat(shellRule.shell, hasItem(startsWith('PATH=$PATH:~/.npm-global/bin')))
assertJobStatusSuccess()
}
@Test
void testExecuteNewmanWithNoCollection() throws Exception {
thrown.expectMessage('[newmanExecute] No collection found with pattern \'notFound.json\'')
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanCollection: 'notFound.json'
)
// asserts
assertJobStatusFailure()
}
@Test
void testExecuteNewmanFailOnError() throws Exception {
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanCollection: 'testCollection',
newmanEnvironment: 'testEnvironment',
newmanGlobals: 'testGlobals',
dockerImage: 'testImage',
testRepository: 'testRepo',
failOnError: false
)
// asserts
assertThat(dockerExecuteRule.dockerParams.dockerImage, is('testImage'))
assertThat(gitMap.url, is('testRepo'))
assertThat(shellRule.shell, hasItem(endsWith('newman run \'testCollection\' --environment \'testEnvironment\' --globals \'testGlobals\' --reporters junit,html --reporter-junit-export \'target/newman/TEST-testCollection.xml\' --reporter-html-export \'target/newman/TEST-testCollection.html\' --suppress-exit-code')))
assertJobStatusSuccess()
}
@Test
void testExecuteNewmanWithFolder() throws Exception {
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanRunCommand: 'run ${config.newmanCollection} --iteration-data testDataFile --reporters junit,html --reporter-junit-export target/newman/TEST-${config.newmanCollection.toString().replace(File.separatorChar,(char)\'_\').tokenize(\'.\').first()}.xml --reporter-html-export target/newman/TEST-${config.newmanCollection.toString().replace(File.separatorChar,(char)\'_\').tokenize(\'.\').first()}.html'
)
// asserts
assertThat(shellRule.shell, hasItem(endsWith('newman run testCollectionsFolder'+File.separatorChar+'A.postman_collection.json --iteration-data testDataFile --reporters junit,html --reporter-junit-export target/newman/TEST-testCollectionsFolder_A.xml --reporter-html-export target/newman/TEST-testCollectionsFolder_A.html')))
assertThat(shellRule.shell, hasItem(endsWith('newman run testCollectionsFolder'+File.separatorChar+'B.postman_collection.json --iteration-data testDataFile --reporters junit,html --reporter-junit-export target/newman/TEST-testCollectionsFolder_B.xml --reporter-html-export target/newman/TEST-testCollectionsFolder_B.html')))
assertJobStatusSuccess()
}
@Test
void testExecuteNewmanCfAppsWithSecrets() throws Exception {
def jsonResponse = '{ "system_env_json":{"VCAP_SERVICES":{"xsuaa":[{"credentials":{"clientid":"myclientid", "clientsecret":"myclientsecret"}}]}}, "authorization_endpoint": "myAuthEndPoint", "access_token": "myAccessToken", "resources":[{"guid":"myGuid", "links":{"self":{"href":"myAppUrl"}}}] }'
jenkinsCredentialsRule.withCredentials('credentialsId', 'myuser', 'topsecret')
helper.registerAllowedMethod('httpRequest', [Map.class] , {
return [content: jsonResponse, status: 200]
})
helper.registerAllowedMethod('readJSON', [Map.class] , {
return new JsonSlurper().parseText(jsonResponse)
})
stepRule.step.newmanExecute(
script: nullScript,
juStabUtils: utils,
newmanCollection: 'testCollection',
newmanEnvironment: 'testEnvironment',
newmanGlobals: 'testGlobals',
dockerImage: 'testImage',
testRepository: 'testRepo',
failOnError: false,
cloudFoundry: ["apiEndpoint": "http://fake.endpoint.com", "org":"myOrg", "space": "mySpace", "credentialsId": "credentialsId"],
cfAppsWithSecrets: ['app1', 'app2']
)
// asserts
assertThat(shellRule.shell, hasItem(endsWith('--env-var app1_clientid=myclientid --env-var app1_clientsecret=myclientsecret --env-var app2_clientid=myclientid --env-var app2_clientsecret=myclientsecret')))
assertJobStatusSuccess()
}
}

View File

@ -1,32 +1,25 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.integration.CloudFoundry
import groovy.transform.Field
import com.sap.piper.GitUtils
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GenerateDocumentation
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import com.sap.piper.integration.CloudFoundry
import groovy.text.GStringTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/newmanExecute.yaml'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
/** @see dockerExecute */
'dockerImage',
/** @see dockerExecute*/
'dockerEnvVars',
/** @see dockerExecute */
'dockerOptions',
/** @see dockerExecute*/
'dockerWorkspace',
@Field Set CONFIG_KEYS = [
/**
* Defines the behavior, in case tests fail.
* @possibleValues `true`, `false`
* Define name array of cloud foundry apps deployed for which secrets (clientid and clientsecret) will be appended
* to the newman command that overrides the environment json entries
* (--env-var <appName_clientid>=${clientid} & --env-var <appName_clientsecret>=${clientsecret})
*/
'failOnError',
"cfAppsWithSecrets",
/**
* Define an additional repository where the test implementation is located.
* For protected repositories the `testRepository` needs to contain the ssh git url.
*/
'testRepository',
/**
* Only if `testRepository` is provided: Branch of testRepository, defaults to master.
*/
@ -36,166 +29,41 @@ import groovy.transform.Field
* @possibleValues Jenkins credentials id
*/
'gitSshKeyCredentialsId',
/**
* The test collection that should be executed. This could also be a file pattern.
*/
'newmanCollection',
/**
* Specify an environment file path or URL. Environments provide a set of variables that one can use within collections.
* see also [Newman docs](https://github.com/postmanlabs/newman#newman-run-collection-file-source-options)
*/
'newmanEnvironment',
/**
* Specify the file path or URL for global variables. Global variables are similar to environment variables but have a lower precedence and can be overridden by environment variables having the same name.
* see also [Newman docs](https://github.com/postmanlabs/newman#newman-run-collection-file-source-options)
*/
'newmanGlobals',
/**
* The shell command that will be executed inside the docker container to install Newman.
*/
'newmanInstallCommand',
/**
* The newman command that will be executed inside the docker container.
*/
'newmanRunCommand',
/**
* If specific stashes should be considered for the tests, you can pass this via this parameter.
*/
'stashContent',
/**
* Define an additional repository where the test implementation is located.
* For protected repositories the `testRepository` needs to contain the ssh git url.
*/
'testRepository',
/**
* Define name array of cloud foundry apps deployed for which secrets (clientid and clientsecret) will be appended
* to the newman command that overrides the environment json entries
* (--env-var <appName_clientid>=${clientid} & --env-var <appName_clientsecret>=${clientsecret})
*/
'cfAppsWithSecrets',
'cloudFoundry',
/**
* Cloud Foundry API endpoint.
* @parentConfigKey cloudFoundry
*/
'apiEndpoint',
/**
* Credentials to be used for deployment.
* @parentConfigKey cloudFoundry
*/
'credentialsId',
/**
* Cloud Foundry target organization.
* @parentConfigKey cloudFoundry
*/
'org',
/**
* Cloud Foundry target space.
* @parentConfigKey cloudFoundry
*/
'space',
/**
* Print more detailed information into the log.
* @possibleValues `true`, `false`
*/
'verbose'
]
@Field Map CONFIG_KEY_COMPATIBILITY = [cloudFoundry: [apiEndpoint: 'cfApiEndpoint', credentialsId: 'cfCredentialsId', org: 'cfOrg', space: 'cfSpace']]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
/**
* This script executes [Postman](https://www.getpostman.com) tests from a collection via the [Newman](https://www.getpostman.com/docs/v6/postman/collection_runs/command_line_integration_with_newman) command line tool.
*/
@GenerateDocumentation
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = checkScript(this, parameters) ?: this
String stageName = parameters.stageName ?: env.STAGE_NAME
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults([:], stageName)
.mixinGeneralConfig(script.commonPipelineEnvironment, CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, stageName, CONFIG_KEYS)
.mixin(parameters, CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.use()
def script = checkScript(this, parameters) ?: this
def utils = parameters?.juStabUtils ?: new Utils()
String stageName = parameters.stageName ?: env.STAGE_NAME
if (parameters.testRepository || config.testRepository ) {
parameters.stashContent = [GitUtils.handleTestRepository(this, [gitBranch: config.gitBranch, gitSshKeyCredentialsId: config.gitSshKeyCredentialsId, testRepository: config.testRepository])]
}
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults([:], stageName)
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, stageName, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.use()
new Utils().pushToSWA([
step: STEP_NAME,
stepParamKey1: 'scriptMissing',
stepParam1: parameters?.script == null
], config)
config.stashContent = config.testRepository
?[GitUtils.handleTestRepository(this, config)]
:utils.unstashAll(config.stashContent)
List collectionList = findFiles(glob: config.newmanCollection)?.toList()
if (collectionList.isEmpty()) {
error "[${STEP_NAME}] No collection found with pattern '${config.newmanCollection}'"
} else {
echo "[${STEP_NAME}] Found files ${collectionList}"
}
dockerExecute(
script: script,
dockerImage: config.dockerImage,
dockerEnvVars: config.dockerEnvVars,
dockerOptions: config.dockerOptions,
dockerWorkspace: config.dockerWorkspace,
stashContent: config.stashContent
) {
sh returnStatus: true, script: """
node --version
npm --version
"""
sh "NPM_CONFIG_PREFIX=~/.npm-global ${config.newmanInstallCommand}"
for(String collection : collectionList){
def collectionDisplayName = collection.toString().replace(File.separatorChar,(char)'_').tokenize('.').first()
// resolve templates
def command = GStringTemplateEngine.newInstance()
.createTemplate(config.newmanRunCommand)
.make([
config: config.plus([newmanCollection: collection]),
collectionDisplayName: collectionDisplayName
]).toString()
def command_secrets = ''
if(config.cfAppsWithSecrets){
CloudFoundry cfUtils = new CloudFoundry(script);
config.cfAppsWithSecrets.each { appName ->
def xsuaaCredentials = cfUtils.getXsuaaCredentials(config.cloudFoundry.apiEndpoint,
config.cloudFoundry.org,
config.cloudFoundry.space,
config.cloudFoundry.credentialsId,
appName,
config.verbose ? true : false ) //to avoid config.verbose as "null" if undefined in yaml and since function parameter boolean
command_secrets += " --env-var ${appName}_clientid=${xsuaaCredentials.clientid} --env-var ${appName}_clientsecret=${xsuaaCredentials.clientsecret}"
echo "Exposing client id and secret for ${appName}: as ${appName}_clientid and ${appName}_clientsecret to newman"
}
}
if(!config.failOnError) command += ' --suppress-exit-code'
try {
if (config.cfAppsWithSecrets && command_secrets){
echo "PATH=\$PATH:~/.npm-global/bin newman ${command} **env/secrets**"
sh """
set +x
PATH=\$PATH:~/.npm-global/bin newman ${command} ${command_secrets}
"""
}
else{
sh "PATH=\$PATH:~/.npm-global/bin newman ${command}"
}
}
catch (e) {
error "[${STEP_NAME}] ERROR: The execution of the newman tests failed, see the log for details."
}
}
List<String> cfCredentials = []
if (config.cfAppsWithSecrets) {
CloudFoundry cfUtils = new CloudFoundry(script);
config.cfAppsWithSecrets.each { appName ->
def xsuaaCredentials = cfUtils.getXsuaaCredentials(config.cloudFoundry.apiEndpoint,
config.cloudFoundry.org,
config.cloudFoundry.space,
config.cloudFoundry.credentialsId,
appName,
config.verbose ? true : false )
cfCredentials.add("PIPER_NEWMANEXECUTE_${appName}_clientid=${xsuaaCredentials.clientid}")
cfCredentials.add("PIPER_NEWMANEXECUTE_${appName}_clientsecret=${xsuaaCredentials.clientsecret}")
echo "Exposing client id and secret for ${appName}: as ${appName}_clientid and ${appName}_clientsecret to newmanExecute"
}
}
withEnv(cfCredentials) {
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, [])
}
}

View File

@ -33,7 +33,7 @@ void call(Map parameters = [:]) {
.use()
if (parameters.testRepository || config.testRepository ) {
parameters.stashContent = GitUtils.handleTestRepository(this, [gitBranch: config.gitBranch, gitSshKeyCredentialsId: config.gitSshKeyCredentialsId, testRepository: config.testRepository])
parameters.stashContent = [GitUtils.handleTestRepository(this, [gitBranch: config.gitBranch, gitSshKeyCredentialsId: config.gitSshKeyCredentialsId, testRepository: config.testRepository])]
}
List credentials = [