1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-11-06 09:09:19 +02:00

Add easy mode for AUnit & ATC (#3389)

* remove mandatory flag from config files

* Enable repo.yml as config

* Adapt to merge

* Refactoring

* Refactoring

* avoid panic

* Add comments

* Add easy mode for atc

* Add tests

* Add test

* Refactor

* Add test for MPS

* Updates

* Rename functions

* Add files to gitignore

* Rename

* Renaming

* Renaming

* Renaming

* Improve error messages

* Update documentation

* Add logging

* Rename

* Extend gitignore
This commit is contained in:
Daniel Mieg
2022-01-12 12:02:27 +01:00
committed by GitHub
parent 24a2340921
commit 8634d8bb12
14 changed files with 528 additions and 366 deletions

6
.gitignore vendored
View File

@@ -40,3 +40,9 @@ debug.test
*errorDetails.json *errorDetails.json
*_links.json *_links.json
*_reports.json *_reports.json
# Result files
ATCResults.xml
AUnitResults.xml
ATCResults.html
AUnitResults.html

View File

@@ -8,7 +8,6 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -19,7 +18,6 @@ import (
"github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry" "github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/ghodss/yaml"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -61,10 +59,10 @@ func abapEnvironmentRunATCCheck(options abapEnvironmentRunATCCheckOptions, telem
details.XCsrfToken, err = fetchXcsrfToken("GET", details, nil, &client) details.XCsrfToken, err = fetchXcsrfToken("GET", details, nil, &client)
} }
if err == nil { if err == nil {
resp, err = triggerATCrun(options, details, &client) resp, err = triggerATCRun(options, details, &client)
} }
if err == nil { if err == nil {
err = handleATCresults(resp, details, &client, options.AtcResultsFileName, options.GenerateHTML) err = fetchAndPersistATCResults(resp, details, &client, options.AtcResultsFileName, options.GenerateHTML)
} }
if err != nil { if err != nil {
log.Entry().WithError(err).Fatal("step execution failed") log.Entry().WithError(err).Fatal("step execution failed")
@@ -73,7 +71,7 @@ func abapEnvironmentRunATCCheck(options abapEnvironmentRunATCCheckOptions, telem
log.Entry().Info("ATC run completed successfully. If there are any results from the respective run they will be listed in the logs above as well as being saved in the output .xml file") log.Entry().Info("ATC run completed successfully. If there are any results from the respective run they will be listed in the logs above as well as being saved in the output .xml file")
} }
func handleATCresults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, atcResultFileName string, generateHTML bool) error { func fetchAndPersistATCResults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, atcResultFileName string, generateHTML bool) error {
var err error var err error
var abapEndpoint string var abapEndpoint string
abapEndpoint = details.URL abapEndpoint = details.URL
@@ -91,7 +89,7 @@ func handleATCresults(resp *http.Response, details abaputils.ConnectionDetailsHT
} }
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
err = parseATCResult(body, atcResultFileName, generateHTML) err = logAndPersistATCResult(body, atcResultFileName, generateHTML)
} }
if err != nil { if err != nil {
return fmt.Errorf("Handling ATC result failed: %w", err) return fmt.Errorf("Handling ATC result failed: %w", err)
@@ -99,83 +97,107 @@ func handleATCresults(resp *http.Response, details abaputils.ConnectionDetailsHT
return nil return nil
} }
func triggerATCrun(config abapEnvironmentRunATCCheckOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (*http.Response, error) { func triggerATCRun(config abapEnvironmentRunATCCheckOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (*http.Response, error) {
var atcConfigyamlFile []byte
abapEndpoint := details.URL
filelocation, err := filepath.Glob(config.AtcConfig)
//Parse YAML ATC run configuration as body for ATC run trigger
if err == nil {
filename, err := filepath.Abs(filelocation[0])
if err == nil {
atcConfigyamlFile, err = ioutil.ReadFile(filename)
}
}
var ATCConfig ATCconfig
if err == nil {
var result []byte
result, err = yaml.YAMLToJSON(atcConfigyamlFile)
json.Unmarshal(result, &ATCConfig)
}
var checkVariantString, packageString, softwareComponentString string
if err == nil {
checkVariantString, packageString, softwareComponentString, err = buildATCCheckBody(ATCConfig)
}
//Trigger ATC run bodyString, err := buildATCRequestBody(config)
if err != nil {
return nil, err
}
var resp *http.Response var resp *http.Response
var bodyString = `<?xml version="1.0" encoding="UTF-8"?><atc:runparameters xmlns:atc="http://www.sap.com/adt/atc" xmlns:obj="http://www.sap.com/adt/objectset"` + checkVariantString + `><obj:objectSet>` + softwareComponentString + packageString + `</obj:objectSet></atc:runparameters>` abapEndpoint := details.URL
log.Entry().Debugf("Request Body: %s", bodyString) log.Entry().Debugf("Request Body: %s", bodyString)
var body = []byte(bodyString) var body = []byte(bodyString)
if err == nil { details.URL = abapEndpoint + "/sap/bc/adt/api/atc/runs?clientWait=false"
details.URL = abapEndpoint + "/sap/bc/adt/api/atc/runs?clientWait=false" resp, err = runATC("POST", details, body, client)
resp, err = runATC("POST", details, body, client) return resp, err
}
if err != nil {
return resp, fmt.Errorf("Triggering ATC run failed: %w", err)
}
return resp, nil
} }
func buildATCCheckBody(ATCConfig ATCconfig) (checkVariantString string, packageString string, softwareComponentString string, err error) { func buildATCRequestBody(config abapEnvironmentRunATCCheckOptions) (bodyString string, err error) {
if len(ATCConfig.Objects.Package) == 0 && len(ATCConfig.Objects.SoftwareComponent) == 0 {
log.SetErrorCategory(log.ErrorConfiguration) atcConfig, err := resolveATCConfiguration(config)
return "", "", "", fmt.Errorf("Error while parsing ATC run config. Please provide the packages and/or the software components to be checked! %w", errors.New("No Package or Software Component specified. Please provide either one or both of them")) if err != nil {
return "", err
} }
//Build Check Variant and Configuration XML Body // Create string for the run parameters
if len(ATCConfig.CheckVariant) != 0 { variant := "ABAP_CLOUD_DEVELOPMENT_DEFAULT"
checkVariantString += ` checkVariant="` + ATCConfig.CheckVariant + `"` if atcConfig.CheckVariant != "" {
log.Entry().Infof("ATC Check Variant: %s", ATCConfig.CheckVariant) variant = atcConfig.CheckVariant
if len(ATCConfig.Configuration) != 0 { }
checkVariantString += ` configuration="` + ATCConfig.Configuration + `"` log.Entry().Infof("ATC Check Variant: %s", variant)
runParameters := ` checkVariant="` + variant + `"`
if atcConfig.Configuration != "" {
runParameters += ` configuration="` + atcConfig.Configuration + `"`
}
objectSet, err := getATCObjectSet(atcConfig)
bodyString = `<?xml version="1.0" encoding="UTF-8"?><atc:runparameters xmlns:atc="http://www.sap.com/adt/atc" xmlns:obj="http://www.sap.com/adt/objectset"` + runParameters + `>` + objectSet + `</atc:runparameters>`
return bodyString, err
}
func resolveATCConfiguration(config abapEnvironmentRunATCCheckOptions) (atcConfig ATCConfiguration, err error) {
if config.AtcConfig != "" {
// Configuration defaults to AUnitConfig
log.Entry().Infof("ATC Configuration: %s", config.AtcConfig)
atcConfigFile, err := abaputils.ReadConfigFile(config.AtcConfig)
if err != nil {
return atcConfig, err
} }
json.Unmarshal(atcConfigFile, &atcConfig)
return atcConfig, err
} else if config.Repositories != "" {
// Fallback / EasyMode is the Repositories configuration
log.Entry().Infof("ATC Configuration derived from: %s", config.Repositories)
repositories, err := abaputils.GetRepositories((&abaputils.RepositoriesConfig{Repositories: config.Repositories}))
if err != nil {
return atcConfig, err
}
for _, repository := range repositories {
atcConfig.Objects.SoftwareComponent = append(atcConfig.Objects.SoftwareComponent, SoftwareComponent{Name: repository.Name})
}
return atcConfig, nil
} else { } else {
const defaultCheckVariant = "ABAP_CLOUD_DEVELOPMENT_DEFAULT" // Fail if no configuration is provided
checkVariantString += ` checkVariant="` + defaultCheckVariant + `"` return atcConfig, errors.New("No configuration provided - please provide either an ATC configuration file or a repository configuration file")
log.Entry().Infof("ATC Check Variant: %s", checkVariantString) }
}
func getATCObjectSet(ATCConfig ATCConfiguration) (objectSet string, err error) {
if len(ATCConfig.Objects.Package) == 0 && len(ATCConfig.Objects.SoftwareComponent) == 0 {
log.SetErrorCategory(log.ErrorConfiguration)
return "", fmt.Errorf("Error while parsing ATC run config. Please provide the packages and/or the software components to be checked! %w", errors.New("No Package or Software Component specified. Please provide either one or both of them"))
}
objectSet += `<obj:objectSet>`
//Build SC XML body
if len(ATCConfig.Objects.SoftwareComponent) != 0 {
objectSet += "<obj:softwarecomponents>"
for _, s := range ATCConfig.Objects.SoftwareComponent {
objectSet += `<obj:softwarecomponent value="` + s.Name + `"/>`
}
objectSet += "</obj:softwarecomponents>"
} }
//Build Package XML body //Build Package XML body
if len(ATCConfig.Objects.Package) != 0 { if len(ATCConfig.Objects.Package) != 0 {
packageString += "<obj:packages>" objectSet += "<obj:packages>"
for _, s := range ATCConfig.Objects.Package { for _, s := range ATCConfig.Objects.Package {
packageString += `<obj:package value="` + s.Name + `" includeSubpackages="` + strconv.FormatBool(s.IncludeSubpackages) + `"/>` objectSet += `<obj:package value="` + s.Name + `" includeSubpackages="` + strconv.FormatBool(s.IncludeSubpackages) + `"/>`
} }
packageString += "</obj:packages>" objectSet += "</obj:packages>"
} }
//Build SC XML body objectSet += `</obj:objectSet>`
if len(ATCConfig.Objects.SoftwareComponent) != 0 {
softwareComponentString += "<obj:softwarecomponents>" return objectSet, nil
for _, s := range ATCConfig.Objects.SoftwareComponent {
softwareComponentString += `<obj:softwarecomponent value="` + s.Name + `"/>`
}
softwareComponentString += "</obj:softwarecomponents>"
}
return checkVariantString, packageString, softwareComponentString, nil
} }
func parseATCResult(body []byte, atcResultFileName string, generateHTML bool) (err error) { func logAndPersistATCResult(body []byte, atcResultFileName string, generateHTML bool) (err error) {
if len(body) == 0 { if len(body) == 0 {
return fmt.Errorf("Parsing ATC result failed: %w", errors.New("Body is empty, can't parse empty body")) return fmt.Errorf("Parsing ATC result failed: %w", errors.New("Body is empty, can't parse empty body"))
} }
@@ -253,9 +275,6 @@ func fetchXcsrfToken(requestType string, details abaputils.ConnectionDetailsHTTP
} }
defer req.Body.Close() defer req.Body.Close()
// workaround until golang version 1.16 is used
time.Sleep(1 * time.Second)
token := req.Header.Get("X-Csrf-Token") token := req.Header.Get("X-Csrf-Token")
return token, err return token, err
} }
@@ -293,8 +312,6 @@ func pollATCRun(details abaputils.ConnectionDetailsHTTP, body []byte, client pip
func getHTTPResponseATCRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) { func getHTTPResponseATCRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Polling ATC run status")
header := make(map[string][]string) header := make(map[string][]string)
header["Accept"] = []string{"application/vnd.sap.atc.run.v1+xml"} header["Accept"] = []string{"application/vnd.sap.atc.run.v1+xml"}
@@ -364,8 +381,8 @@ func generateHTMLDocument(parsedXML *Result) (htmlDocumentString string) {
return htmlDocumentString return htmlDocumentString
} }
//ATCconfig object for parsing yaml config of software components and packages //ATCConfiguration object for parsing yaml config of software components and packages
type ATCconfig struct { type ATCConfiguration struct {
CheckVariant string `json:"checkvariant,omitempty"` CheckVariant string `json:"checkvariant,omitempty"`
Configuration string `json:"configuration,omitempty"` Configuration string `json:"configuration,omitempty"`
Objects ATCObjects `json:"atcobjects"` Objects ATCObjects `json:"atcobjects"`

View File

@@ -17,6 +17,7 @@ import (
type abapEnvironmentRunATCCheckOptions struct { type abapEnvironmentRunATCCheckOptions struct {
AtcConfig string `json:"atcConfig,omitempty"` AtcConfig string `json:"atcConfig,omitempty"`
Repositories string `json:"repositories,omitempty"`
CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"` CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"`
CfOrg string `json:"cfOrg,omitempty"` CfOrg string `json:"cfOrg,omitempty"`
CfServiceInstance string `json:"cfServiceInstance,omitempty"` CfServiceInstance string `json:"cfServiceInstance,omitempty"`
@@ -128,6 +129,7 @@ Regardless of the option you chose, please make sure to provide the configuratio
func addAbapEnvironmentRunATCCheckFlags(cmd *cobra.Command, stepConfig *abapEnvironmentRunATCCheckOptions) { func addAbapEnvironmentRunATCCheckFlags(cmd *cobra.Command, stepConfig *abapEnvironmentRunATCCheckOptions) {
cmd.Flags().StringVar(&stepConfig.AtcConfig, "atcConfig", os.Getenv("PIPER_atcConfig"), "Path to a YAML configuration file for Packages and/or Software Components to be checked during ATC run") cmd.Flags().StringVar(&stepConfig.AtcConfig, "atcConfig", os.Getenv("PIPER_atcConfig"), "Path to a YAML configuration file for Packages and/or Software Components to be checked during ATC run")
cmd.Flags().StringVar(&stepConfig.Repositories, "repositories", os.Getenv("PIPER_repositories"), "Specifies a YAML file containing the repositories configuration")
cmd.Flags().StringVar(&stepConfig.CfAPIEndpoint, "cfApiEndpoint", os.Getenv("PIPER_cfApiEndpoint"), "Cloud Foundry API endpoint") cmd.Flags().StringVar(&stepConfig.CfAPIEndpoint, "cfApiEndpoint", os.Getenv("PIPER_cfApiEndpoint"), "Cloud Foundry API endpoint")
cmd.Flags().StringVar(&stepConfig.CfOrg, "cfOrg", os.Getenv("PIPER_cfOrg"), "CF org") cmd.Flags().StringVar(&stepConfig.CfOrg, "cfOrg", os.Getenv("PIPER_cfOrg"), "CF org")
cmd.Flags().StringVar(&stepConfig.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Parameter of ServiceInstance Name to delete CloudFoundry Service") cmd.Flags().StringVar(&stepConfig.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Parameter of ServiceInstance Name to delete CloudFoundry Service")
@@ -139,7 +141,6 @@ func addAbapEnvironmentRunATCCheckFlags(cmd *cobra.Command, stepConfig *abapEnvi
cmd.Flags().StringVar(&stepConfig.AtcResultsFileName, "atcResultsFileName", `ATCResults.xml`, "Specifies output file name for the results from the ATC run. This file name will also be used for generating the HTML file") cmd.Flags().StringVar(&stepConfig.AtcResultsFileName, "atcResultsFileName", `ATCResults.xml`, "Specifies output file name for the results from the ATC run. This file name will also be used for generating the HTML file")
cmd.Flags().BoolVar(&stepConfig.GenerateHTML, "generateHTML", false, "Specifies whether the ATC results should also be generated as an HTML document") cmd.Flags().BoolVar(&stepConfig.GenerateHTML, "generateHTML", false, "Specifies whether the ATC results should also be generated as an HTML document")
cmd.MarkFlagRequired("atcConfig")
cmd.MarkFlagRequired("username") cmd.MarkFlagRequired("username")
cmd.MarkFlagRequired("password") cmd.MarkFlagRequired("password")
} }
@@ -163,10 +164,19 @@ func abapEnvironmentRunATCCheckMetadata() config.StepData {
ResourceRef: []config.ResourceReference{}, ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string", Type: "string",
Mandatory: true, Mandatory: false,
Aliases: []config.Alias{}, Aliases: []config.Alias{},
Default: os.Getenv("PIPER_atcConfig"), Default: os.Getenv("PIPER_atcConfig"),
}, },
{
Name: "repositories",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_repositories"),
},
{ {
Name: "cfApiEndpoint", Name: "cfApiEndpoint",
ResourceRef: []config.ResourceReference{}, ResourceRef: []config.ResourceReference{},

View File

@@ -248,7 +248,7 @@ func TestParseATCResult(t *testing.T) {
</file> </file>
</checkstyle>` </checkstyle>`
body := []byte(bodyString) body := []byte(bodyString)
err = parseATCResult(body, "ATCResults.xml", false) err = logAndPersistATCResult(body, "ATCResults.xml", false)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
t.Run("succes case: test parsing empty XML result", func(t *testing.T) { t.Run("succes case: test parsing empty XML result", func(t *testing.T) {
@@ -267,14 +267,14 @@ func TestParseATCResult(t *testing.T) {
<checkstyle> <checkstyle>
</checkstyle>` </checkstyle>`
body := []byte(bodyString) body := []byte(bodyString)
err = parseATCResult(body, "ATCResults.xml", false) err = logAndPersistATCResult(body, "ATCResults.xml", false)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
t.Run("failure case: parsing empty xml", func(t *testing.T) { t.Run("failure case: parsing empty xml", func(t *testing.T) {
var bodyString string var bodyString string
body := []byte(bodyString) body := []byte(bodyString)
err := parseATCResult(body, "ATCResults.xml", false) err := logAndPersistATCResult(body, "ATCResults.xml", false)
assert.EqualError(t, err, "Parsing ATC result failed: Body is empty, can't parse empty body") assert.EqualError(t, err, "Parsing ATC result failed: Body is empty, can't parse empty body")
}) })
t.Run("failure case: html response", func(t *testing.T) { t.Run("failure case: html response", func(t *testing.T) {
@@ -291,37 +291,27 @@ func TestParseATCResult(t *testing.T) {
}() }()
bodyString := `<html><head><title>HTMLTestResponse</title</head></html>` bodyString := `<html><head><title>HTMLTestResponse</title</head></html>`
body := []byte(bodyString) body := []byte(bodyString)
err = parseATCResult(body, "ATCResults.xml", false) err = logAndPersistATCResult(body, "ATCResults.xml", false)
assert.EqualError(t, err, "The Software Component could not be checked. Please make sure the respective Software Component has been cloned successfully on the system") assert.EqualError(t, err, "The Software Component could not be checked. Please make sure the respective Software Component has been cloned successfully on the system")
}) })
} }
func TestBuildATCCheckBody(t *testing.T) { func TestBuildATCCheckBody(t *testing.T) {
t.Run("Test build body with no software component and package", func(t *testing.T) { t.Run("Test build body with no software component and package", func(t *testing.T) {
expectedpackagestring := "" expectedObjectSet := ""
expectedsoftwarecomponentstring := ""
expectedcheckvariantstring := ""
var err error var config ATCConfiguration
var config ATCconfig
var checkVariantString, packageString, softwarecomponentString string
checkVariantString, packageString, softwarecomponentString, err = buildATCCheckBody(config) objectSet, err := getATCObjectSet(config)
assert.Equal(t, expectedcheckvariantstring, checkVariantString) assert.Equal(t, expectedObjectSet, objectSet)
assert.Equal(t, expectedpackagestring, packageString)
assert.Equal(t, expectedsoftwarecomponentstring, softwarecomponentString)
assert.EqualError(t, err, "Error while parsing ATC run config. Please provide the packages and/or the software components to be checked! No Package or Software Component specified. Please provide either one or both of them") assert.EqualError(t, err, "Error while parsing ATC run config. Please provide the packages and/or the software components to be checked! No Package or Software Component specified. Please provide either one or both of them")
}) })
t.Run("success case: Test build body with example yaml config", func(t *testing.T) { t.Run("success case: Test build body with example yaml config", func(t *testing.T) {
expectedcheckvariantstring := " checkVariant=\"ABAP_CLOUD_DEVELOPMENT_DEFAULT\""
expectedpackagestring := "<obj:packages><obj:package value=\"testPackage\" includeSubpackages=\"true\"/><obj:package value=\"testPackage2\" includeSubpackages=\"false\"/></obj:packages>"
expectedsoftwarecomponentstring := "<obj:softwarecomponents><obj:softwarecomponent value=\"testSoftwareComponent\"/><obj:softwarecomponent value=\"testSoftwareComponent2\"/></obj:softwarecomponents>"
var err error expectedObjectSet := "<obj:objectSet><obj:softwarecomponents><obj:softwarecomponent value=\"testSoftwareComponent\"/><obj:softwarecomponent value=\"testSoftwareComponent2\"/></obj:softwarecomponents><obj:packages><obj:package value=\"testPackage\" includeSubpackages=\"true\"/><obj:package value=\"testPackage2\" includeSubpackages=\"false\"/></obj:packages></obj:objectSet>"
var config ATCconfig
config = ATCconfig{ config := ATCConfiguration{
"", "",
"", "",
ATCObjects{ ATCObjects{
@@ -336,24 +326,19 @@ func TestBuildATCCheckBody(t *testing.T) {
}, },
} }
var checkvariantString, packageString, softwarecomponentString string objectSet, err := getATCObjectSet(config)
checkvariantString, packageString, softwarecomponentString, err = buildATCCheckBody(config) assert.Equal(t, expectedObjectSet, objectSet)
assert.Equal(t, expectedcheckvariantstring, checkvariantString)
assert.Equal(t, expectedpackagestring, packageString)
assert.Equal(t, expectedsoftwarecomponentstring, softwarecomponentString)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
t.Run("failure case: Test build body with example yaml config with only packages and no software components", func(t *testing.T) { t.Run("failure case: Test build body with example yaml config with only packages and no software components", func(t *testing.T) {
expectedcheckvariantstring := " checkVariant=\"ABAP_CLOUD_DEVELOPMENT_DEFAULT\""
expectedpackagestring := `<obj:packages><obj:package value="testPackage" includeSubpackages="true"/><obj:package value="testPackage2" includeSubpackages="false"/></obj:packages>` expectedObjectSet := `<obj:objectSet><obj:packages><obj:package value="testPackage" includeSubpackages="true"/><obj:package value="testPackage2" includeSubpackages="false"/></obj:packages></obj:objectSet>`
expectedsoftwarecomponentstring := ""
var err error var err error
var config ATCconfig var config ATCConfiguration
config = ATCconfig{ config = ATCConfiguration{
"", "",
"", "",
ATCObjects{ ATCObjects{
@@ -364,25 +349,17 @@ func TestBuildATCCheckBody(t *testing.T) {
}, },
} }
var checkvariantString, packageString, softwarecomponentString string objectSet, err := getATCObjectSet(config)
checkvariantString, packageString, softwarecomponentString, err = buildATCCheckBody(config) assert.Equal(t, expectedObjectSet, objectSet)
assert.Equal(t, expectedcheckvariantstring, checkvariantString)
assert.Equal(t, expectedpackagestring, packageString)
assert.Equal(t, expectedsoftwarecomponentstring, softwarecomponentString)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
t.Run("success case: Test build body with example yaml config with no packages and only software components", func(t *testing.T) { t.Run("success case: Test build body with example yaml config with no packages and only software components", func(t *testing.T) {
expectedcheckvariantstring := " checkVariant=\"ABAP_CLOUD_DEVELOPMENT_DEFAULT\""
expectedpackagestring := ""
expectedsoftwarecomponentstring := `<obj:softwarecomponents><obj:softwarecomponent value="testSoftwareComponent"/><obj:softwarecomponent value="testSoftwareComponent2"/></obj:softwarecomponents>`
var err error expectedObjectSet := `<obj:objectSet><obj:softwarecomponents><obj:softwarecomponent value="testSoftwareComponent"/><obj:softwarecomponent value="testSoftwareComponent2"/></obj:softwarecomponents></obj:objectSet>`
var config ATCconfig
config = ATCconfig{ config := ATCConfiguration{
"", "",
"", "",
ATCObjects{ ATCObjects{
@@ -393,45 +370,9 @@ func TestBuildATCCheckBody(t *testing.T) {
}, },
} }
var checkvariantString, packageString, softwarecomponentString string objectSet, err := getATCObjectSet(config)
checkvariantString, packageString, softwarecomponentString, err = buildATCCheckBody(config) assert.Equal(t, expectedObjectSet, objectSet)
assert.Equal(t, expectedcheckvariantstring, checkvariantString)
assert.Equal(t, expectedpackagestring, packageString)
assert.Equal(t, expectedsoftwarecomponentstring, softwarecomponentString)
assert.Equal(t, nil, err)
})
t.Run("success case: Test build body with example yaml config with check variant configuration", func(t *testing.T) {
expectedcheckvariantstring := ` checkVariant="TestVariant" configuration="TestConfiguration"`
expectedpackagestring := `<obj:packages><obj:package value="testPackage" includeSubpackages="true"/><obj:package value="testPackage2" includeSubpackages="false"/></obj:packages>`
expectedsoftwarecomponentstring := `<obj:softwarecomponents><obj:softwarecomponent value="testSoftwareComponent"/><obj:softwarecomponent value="testSoftwareComponent2"/></obj:softwarecomponents>`
var err error
var config ATCconfig
config = ATCconfig{
"TestVariant",
"TestConfiguration",
ATCObjects{
SoftwareComponent: []SoftwareComponent{
{Name: "testSoftwareComponent"},
{Name: "testSoftwareComponent2"},
},
Package: []Package{
{Name: "testPackage", IncludeSubpackages: true},
{Name: "testPackage2", IncludeSubpackages: false},
},
},
}
var checkvariantString, packageString, softwarecomponentString string
checkvariantString, packageString, softwarecomponentString, err = buildATCCheckBody(config)
assert.Equal(t, expectedcheckvariantstring, checkvariantString)
assert.Equal(t, expectedpackagestring, packageString)
assert.Equal(t, expectedsoftwarecomponentstring, softwarecomponentString)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
} }
@@ -463,3 +404,99 @@ func TestGenerateHTMLDocument(t *testing.T) {
} }
}) })
} }
func TestResolveConfiguration(t *testing.T) {
t.Run("resolve atcConfig-yml", func(t *testing.T) {
expectedBodyString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><atc:runparameters xmlns:atc=\"http://www.sap.com/adt/atc\" xmlns:obj=\"http://www.sap.com/adt/objectset\" checkVariant=\"MY_TEST\" configuration=\"MY_CONFIG\"><obj:objectSet><obj:softwarecomponents><obj:softwarecomponent value=\"Z_TEST\"/><obj:softwarecomponent value=\"/DMO/SWC\"/></obj:softwarecomponents><obj:packages><obj:package value=\"Z_TEST\" includeSubpackages=\"false\"/></obj:packages></obj:objectSet></atc:runparameters>"
config := abapEnvironmentRunATCCheckOptions{
AtcConfig: "atc.yml",
}
dir, err := ioutil.TempDir("", "atcDir")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
yamlBody := `checkvariant: MY_TEST
configuration: MY_CONFIG
atcobjects:
package:
- name: Z_TEST
softwarecomponent:
- name: Z_TEST
- name: /DMO/SWC
`
err = ioutil.WriteFile(config.AtcConfig, []byte(yamlBody), 0644)
if assert.Equal(t, err, nil) {
bodyString, err := buildATCRequestBody(config)
assert.Equal(t, nil, err)
assert.Equal(t, expectedBodyString, bodyString)
}
})
t.Run("resolve repo-yml", func(t *testing.T) {
expectedBodyString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><atc:runparameters xmlns:atc=\"http://www.sap.com/adt/atc\" xmlns:obj=\"http://www.sap.com/adt/objectset\" checkVariant=\"ABAP_CLOUD_DEVELOPMENT_DEFAULT\"><obj:objectSet><obj:softwarecomponents><obj:softwarecomponent value=\"Z_TEST\"/><obj:softwarecomponent value=\"/DMO/SWC\"/></obj:softwarecomponents></obj:objectSet></atc:runparameters>"
config := abapEnvironmentRunATCCheckOptions{
Repositories: "repo.yml",
}
dir, err := ioutil.TempDir("", "test parse AUnit yaml config2")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
yamlBody := `repositories:
- name: Z_TEST
- name: /DMO/SWC
`
err = ioutil.WriteFile(config.Repositories, []byte(yamlBody), 0644)
if assert.Equal(t, err, nil) {
bodyString, err := buildATCRequestBody(config)
assert.Equal(t, nil, err)
assert.Equal(t, expectedBodyString, bodyString)
}
})
t.Run("Missing config files", func(t *testing.T) {
config := abapEnvironmentRunATCCheckOptions{
AtcConfig: "atc.yml",
}
bodyString, err := buildATCRequestBody(config)
assert.Equal(t, "Could not find atc.yml", err.Error())
assert.Equal(t, "", bodyString)
})
t.Run("Config file not specified", func(t *testing.T) {
config := abapEnvironmentRunATCCheckOptions{}
bodyString, err := buildATCRequestBody(config)
assert.Equal(t, "No configuration provided - please provide either an ATC configuration file or a repository configuration file", err.Error())
assert.Equal(t, "", bodyString)
})
}

View File

@@ -8,7 +8,6 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"path/filepath"
"reflect" "reflect"
"strings" "strings"
"time" "time"
@@ -19,7 +18,6 @@ import (
"github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry" "github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/ghodss/yaml"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -96,7 +94,7 @@ func runAbapEnvironmentRunAUnitTest(config *abapEnvironmentRunAUnitTestOptions,
resp, err = triggerAUnitrun(*config, details, client) resp, err = triggerAUnitrun(*config, details, client)
} }
if err == nil { if err == nil {
err = handleAUnitResults(resp, details, client, config.AUnitResultsFileName, config.GenerateHTML) err = fetchAndPersistAUnitResults(resp, details, client, config.AUnitResultsFileName, config.GenerateHTML)
} }
if err != nil { if err != nil {
log.Entry().WithError(err).Fatal("step execution failed") log.Entry().WithError(err).Fatal("step execution failed")
@@ -106,40 +104,51 @@ func runAbapEnvironmentRunAUnitTest(config *abapEnvironmentRunAUnitTestOptions,
} }
func triggerAUnitrun(config abapEnvironmentRunAUnitTestOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (*http.Response, error) { func triggerAUnitrun(config abapEnvironmentRunAUnitTestOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (*http.Response, error) {
var aUnitConfigYamlFile []byte
abapEndpoint := details.URL abapEndpoint := details.URL
filelocation, err := filepath.Glob(config.AUnitConfig) bodyString, err := buildAUnitRequestBody(config)
//Parse YAML AUnit run configuration as body for AUnit run trigger if err != nil {
if err == nil { return nil, err
filename, err := filepath.Abs(filelocation[0])
if err == nil {
aUnitConfigYamlFile, err = ioutil.ReadFile(filename)
}
}
var AUnitConfig AUnitConfig
if err == nil {
var result []byte
result, err = yaml.YAMLToJSON(aUnitConfigYamlFile)
json.Unmarshal(result, &AUnitConfig)
}
var metadataString, optionsString, objectSetString string
if err == nil {
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(AUnitConfig)
} }
//Trigger AUnit run //Trigger AUnit run
var resp *http.Response var resp *http.Response
var bodyString = `<?xml version="1.0" encoding="UTF-8"?>` + metadataString + optionsString + objectSetString
var body = []byte(bodyString) var body = []byte(bodyString)
if err == nil { log.Entry().Debugf("Request Body: %s", bodyString)
log.Entry().Debugf("Request Body: %s", bodyString) details.URL = abapEndpoint + "/sap/bc/adt/api/abapunit/runs"
details.URL = abapEndpoint + "/sap/bc/adt/api/abapunit/runs" resp, err = runAUnit("POST", details, body, client)
resp, err = runAUnit("POST", details, body, client) return resp, err
}
func resolveAUnitConfiguration(config abapEnvironmentRunAUnitTestOptions) (aUnitConfig AUnitConfig, err error) {
if config.AUnitConfig != "" {
// Configuration defaults to AUnitConfig
log.Entry().Infof("AUnit Configuration: %s", config.AUnitConfig)
result, err := abaputils.ReadConfigFile(config.AUnitConfig)
if err != nil {
return aUnitConfig, err
}
err = json.Unmarshal(result, &aUnitConfig)
return aUnitConfig, err
} else if config.Repositories != "" {
// Fallback / EasyMode is the Repositories configuration
log.Entry().Infof("AUnit Configuration derived from: %s", config.Repositories)
repos, err := abaputils.GetRepositories((&abaputils.RepositoriesConfig{Repositories: config.Repositories}))
if err != nil {
return aUnitConfig, err
}
for _, repo := range repos {
aUnitConfig.ObjectSet.SoftwareComponents = append(aUnitConfig.ObjectSet.SoftwareComponents, SoftwareComponents{Name: repo.Name})
}
aUnitConfig.Title = "AUnit Test Run"
return aUnitConfig, nil
} else {
// Fail if no configuration is provided
return aUnitConfig, errors.New("No configuration provided - please provide either an AUnit configuration file or a repository configuration file")
} }
if err != nil {
return resp, fmt.Errorf("Triggering AUnit test run failed: %w", err)
}
return resp, nil
} }
func convertAUnitOptions(options *abapEnvironmentRunAUnitTestOptions) abaputils.AbapEnvironmentOptions { func convertAUnitOptions(options *abapEnvironmentRunAUnitTestOptions) abaputils.AbapEnvironmentOptions {
@@ -157,7 +166,7 @@ func convertAUnitOptions(options *abapEnvironmentRunAUnitTestOptions) abaputils.
return subOptions return subOptions
} }
func handleAUnitResults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, aunitResultFileName string, generateHTML bool) error { func fetchAndPersistAUnitResults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, aunitResultFileName string, generateHTML bool) error {
var err error var err error
var abapEndpoint string var abapEndpoint string
abapEndpoint = details.URL abapEndpoint = details.URL
@@ -166,7 +175,7 @@ func handleAUnitResults(resp *http.Response, details abaputils.ConnectionDetails
location, err = pollAUnitRun(details, nil, client) location, err = pollAUnitRun(details, nil, client)
if err == nil { if err == nil {
details.URL = abapEndpoint + location details.URL = abapEndpoint + location
resp, err = getResultAUnitRun("GET", details, nil, client) resp, err = getAUnitResults("GET", details, nil, client)
} }
//Parse response //Parse response
var body []byte var body []byte
@@ -175,7 +184,7 @@ func handleAUnitResults(resp *http.Response, details abaputils.ConnectionDetails
} }
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
err = parseAUnitResult(body, aunitResultFileName, generateHTML) err = persistAUnitResult(body, aunitResultFileName, generateHTML)
} }
if err != nil { if err != nil {
return fmt.Errorf("Handling AUnit result failed: %w", err) return fmt.Errorf("Handling AUnit result failed: %w", err)
@@ -183,29 +192,35 @@ func handleAUnitResults(resp *http.Response, details abaputils.ConnectionDetails
return nil return nil
} }
func buildAUnitTestBody(AUnitConfig AUnitConfig) (metadataString string, optionsString string, objectSetString string, err error) { func buildAUnitRequestBody(config abapEnvironmentRunAUnitTestOptions) (bodyString string, err error) {
bodyString = ""
AUnitConfig, err := resolveAUnitConfiguration(config)
if err != nil {
return bodyString, err
}
//Checks before building the XML body //Checks before building the XML body
if AUnitConfig.Title == "" { if AUnitConfig.Title == "" {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No title for the AUnit run has been provided. Please configure an appropriate title for the respective test run") return bodyString, fmt.Errorf("Error while parsing AUnit test run config. No title for the AUnit run has been provided. Please configure an appropriate title for the respective test run")
} }
if AUnitConfig.Context == "" { if AUnitConfig.Context == "" {
AUnitConfig.Context = "ABAP Environment Pipeline" AUnitConfig.Context = "ABAP Environment Pipeline"
} }
if reflect.DeepEqual(ObjectSet{}, AUnitConfig.ObjectSet) { if reflect.DeepEqual(ObjectSet{}, AUnitConfig.ObjectSet) {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run object set config. No object set has been provided. Please configure the objects you want to be checked for the respective test run") return bodyString, fmt.Errorf("Error while parsing AUnit test run object set config. No object set has been provided. Please configure the objects you want to be checked for the respective test run")
} }
//Build Options //Build Options
optionsString += buildAUnitOptionsString(AUnitConfig) optionsString := buildAUnitOptionsString(AUnitConfig)
//Build metadata string //Build metadata string
metadataString += `<aunit:run title="` + AUnitConfig.Title + `" context="` + AUnitConfig.Context + `" xmlns:aunit="http://www.sap.com/adt/api/aunit">` metadataString := `<aunit:run title="` + AUnitConfig.Title + `" context="` + AUnitConfig.Context + `" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
//Build Object Set //Build Object Set
objectSetString += buildAUnitObjectSetString(AUnitConfig) objectSetString := buildAUnitObjectSetString(AUnitConfig)
objectSetString += `</aunit:run>`
return metadataString, optionsString, objectSetString, nil bodyString += `<?xml version="1.0" encoding="UTF-8"?>` + metadataString + optionsString + objectSetString + `</aunit:run>`
return bodyString, nil
} }
func runAUnit(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) { func runAUnit(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
@@ -282,11 +297,6 @@ func buildAUnitOptionsString(AUnitConfig AUnitConfig) (optionsString string) {
return optionsString return optionsString
} }
func buildOSLObjectSets(multipropertyset MultiPropertySet) (objectSetString string) {
objectSetString += writeObjectSetProperties(multipropertyset)
return objectSetString
}
func writeObjectSetProperties(set MultiPropertySet) (objectSetString string) { func writeObjectSetProperties(set MultiPropertySet) (objectSetString string) {
for _, packages := range set.PackageNames { for _, packages := range set.PackageNames {
objectSetString += `<osl:package name="` + packages.Name + `"/>` objectSetString += `<osl:package name="` + packages.Name + `"/>`
@@ -342,10 +352,10 @@ func buildAUnitObjectSetString(AUnitConfig AUnitConfig) (objectSetString string)
PackageNames: s.PackageNames, PackageNames: s.PackageNames,
SoftwareComponents: s.SoftwareComponents, SoftwareComponents: s.SoftwareComponents,
} }
objectSetString += buildOSLObjectSets(mps) objectSetString += writeObjectSetProperties(mps)
} }
objectSetString += buildOSLObjectSets(s.MultiPropertySet) objectSetString += writeObjectSetProperties(s.MultiPropertySet)
if !(reflect.DeepEqual(s.MultiPropertySet, MultiPropertySet{})) { if !(reflect.DeepEqual(s.MultiPropertySet, MultiPropertySet{})) {
log.Entry().Info("Wrong configuration has been detected: MultiPropertySet has been used. Please note that there is no official documentation for this usage. Please check the step documentation for more information") log.Entry().Info("Wrong configuration has been detected: MultiPropertySet has been used. Please note that there is no official documentation for this usage. Please check the step documentation for more information")
@@ -422,7 +432,7 @@ func getHTTPResponseAUnitRun(requestType string, details abaputils.ConnectionDet
return req, err return req, err
} }
func getResultAUnitRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) { func getAUnitResults(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Getting AUnit results") log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Getting AUnit results")
@@ -437,7 +447,7 @@ func getResultAUnitRun(requestType string, details abaputils.ConnectionDetailsHT
return req, err return req, err
} }
func parseAUnitResult(body []byte, aunitResultFileName string, generateHTML bool) (err error) { func persistAUnitResult(body []byte, aunitResultFileName string, generateHTML bool) (err error) {
if len(body) == 0 { if len(body) == 0 {
return fmt.Errorf("Parsing AUnit result failed: %w", errors.New("Body is empty, can't parse empty body")) return fmt.Errorf("Parsing AUnit result failed: %w", errors.New("Body is empty, can't parse empty body"))
} }

View File

@@ -17,6 +17,7 @@ import (
type abapEnvironmentRunAUnitTestOptions struct { type abapEnvironmentRunAUnitTestOptions struct {
AUnitConfig string `json:"aUnitConfig,omitempty"` AUnitConfig string `json:"aUnitConfig,omitempty"`
Repositories string `json:"repositories,omitempty"`
CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"` CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"`
CfOrg string `json:"cfOrg,omitempty"` CfOrg string `json:"cfOrg,omitempty"`
CfServiceInstance string `json:"cfServiceInstance,omitempty"` CfServiceInstance string `json:"cfServiceInstance,omitempty"`
@@ -128,6 +129,7 @@ Regardless of the option you chose, please make sure to provide the object set c
func addAbapEnvironmentRunAUnitTestFlags(cmd *cobra.Command, stepConfig *abapEnvironmentRunAUnitTestOptions) { func addAbapEnvironmentRunAUnitTestFlags(cmd *cobra.Command, stepConfig *abapEnvironmentRunAUnitTestOptions) {
cmd.Flags().StringVar(&stepConfig.AUnitConfig, "aUnitConfig", os.Getenv("PIPER_aUnitConfig"), "Path to a YAML configuration file for the object set to be checked during the AUnit test run") cmd.Flags().StringVar(&stepConfig.AUnitConfig, "aUnitConfig", os.Getenv("PIPER_aUnitConfig"), "Path to a YAML configuration file for the object set to be checked during the AUnit test run")
cmd.Flags().StringVar(&stepConfig.Repositories, "repositories", os.Getenv("PIPER_repositories"), "Specifies a YAML file containing the repositories configuration")
cmd.Flags().StringVar(&stepConfig.CfAPIEndpoint, "cfApiEndpoint", os.Getenv("PIPER_cfApiEndpoint"), "Cloud Foundry API endpoint") cmd.Flags().StringVar(&stepConfig.CfAPIEndpoint, "cfApiEndpoint", os.Getenv("PIPER_cfApiEndpoint"), "Cloud Foundry API endpoint")
cmd.Flags().StringVar(&stepConfig.CfOrg, "cfOrg", os.Getenv("PIPER_cfOrg"), "Cloud Foundry org") cmd.Flags().StringVar(&stepConfig.CfOrg, "cfOrg", os.Getenv("PIPER_cfOrg"), "Cloud Foundry org")
cmd.Flags().StringVar(&stepConfig.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Parameter of ServiceInstance Name to delete Cloud Foundry Service") cmd.Flags().StringVar(&stepConfig.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Parameter of ServiceInstance Name to delete Cloud Foundry Service")
@@ -139,7 +141,6 @@ func addAbapEnvironmentRunAUnitTestFlags(cmd *cobra.Command, stepConfig *abapEnv
cmd.Flags().StringVar(&stepConfig.AUnitResultsFileName, "aUnitResultsFileName", `AUnitResults.xml`, "Specifies output file name for the results from the AUnit run.") cmd.Flags().StringVar(&stepConfig.AUnitResultsFileName, "aUnitResultsFileName", `AUnitResults.xml`, "Specifies output file name for the results from the AUnit run.")
cmd.Flags().BoolVar(&stepConfig.GenerateHTML, "generateHTML", false, "Specifies whether the AUnit results should also be generated as an HTML document") cmd.Flags().BoolVar(&stepConfig.GenerateHTML, "generateHTML", false, "Specifies whether the AUnit results should also be generated as an HTML document")
cmd.MarkFlagRequired("aUnitConfig")
cmd.MarkFlagRequired("username") cmd.MarkFlagRequired("username")
cmd.MarkFlagRequired("password") cmd.MarkFlagRequired("password")
} }
@@ -163,10 +164,19 @@ func abapEnvironmentRunAUnitTestMetadata() config.StepData {
ResourceRef: []config.ResourceReference{}, ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string", Type: "string",
Mandatory: true, Mandatory: false,
Aliases: []config.Alias{}, Aliases: []config.Alias{},
Default: os.Getenv("PIPER_aUnitConfig"), Default: os.Getenv("PIPER_aUnitConfig"),
}, },
{
Name: "repositories",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_repositories"),
},
{ {
Name: "cfApiEndpoint", Name: "cfApiEndpoint",
ResourceRef: []config.ResourceReference{}, ResourceRef: []config.ResourceReference{},

View File

@@ -27,40 +27,29 @@ func newAbapEnvironmentRunAUnitTestTestsUtils() abapEnvironmentRunAUnitTestMockU
return utils return utils
} }
func TestBuildAUnitTestBody(t *testing.T) { func TestBuildAUnitRequestBody(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("Test AUnit test run body with no data", func(t *testing.T) { t.Run("Test AUnit test run body with no data", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := "" var config abapEnvironmentRunAUnitTestOptions
expectedoptionsString := ""
expectedobjectSetString := ""
var err error bodyString, err := buildAUnitRequestBody(config)
var config AUnitConfig expectedBodyString := ""
var metadataString, optionsString, objectSetString string
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config) assert.Equal(t, expectedBodyString, bodyString)
assert.EqualError(t, err, "No configuration provided - please provide either an AUnit configuration file or a repository configuration file")
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString)
assert.EqualError(t, err, "Error while parsing AUnit test run config. No title for the AUnit run has been provided. Please configure an appropriate title for the respective test run")
}) })
t.Run("Test AUnit test run body with example yaml config of not supported Object Sets", func(t *testing.T) { t.Run("Test AUnit test run body with example yaml config of not supported Object Sets", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := `<aunit:run title="Test Title" context="Test Context" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>` expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>`
expectedobjectSetString := `</aunit:run>` expectedobjectSetString := ``
var err error config := AUnitConfig{
var config AUnitConfig
config = AUnitConfig{
Title: "Test Title", Title: "Test Title",
Context: "Test Context", Context: "Test Context",
Options: AUnitOptions{ Options: AUnitOptions{
@@ -110,27 +99,20 @@ func TestBuildAUnitTestBody(t *testing.T) {
}, },
} }
var metadataString, optionsString, objectSetString string objectSetString := buildAUnitObjectSetString(config)
optionsString := buildAUnitOptionsString(config)
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString) assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString) assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, nil, err)
}) })
t.Run("Test AUnit test run body with example yaml config of only Multi Property Set", func(t *testing.T) { t.Run("Test AUnit test run body with example yaml config of only Multi Property Set", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := `<aunit:run title="Test Title" context="Test Context" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>` expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>`
expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet></aunit:run>` expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet>`
var err error config := AUnitConfig{
var config AUnitConfig
config = AUnitConfig{
Title: "Test Title", Title: "Test Title",
Context: "Test Context", Context: "Test Context",
Options: AUnitOptions{ Options: AUnitOptions{
@@ -165,27 +147,20 @@ func TestBuildAUnitTestBody(t *testing.T) {
}, },
} }
var metadataString, optionsString, objectSetString string objectSetString := buildAUnitObjectSetString(config)
optionsString := buildAUnitOptionsString(config)
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString) assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString) assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, nil, err)
}) })
t.Run("Test AUnit test run body with example yaml config of only Multi Property Set but empty type", func(t *testing.T) { t.Run("Test AUnit test run body with example yaml config of only Multi Property Set but empty type", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := `<aunit:run title="Test Title" context="Test Context" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>` expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>`
expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet></aunit:run>` expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet>`
var err error config := AUnitConfig{
var config AUnitConfig
config = AUnitConfig{
Title: "Test Title", Title: "Test Title",
Context: "Test Context", Context: "Test Context",
Options: AUnitOptions{ Options: AUnitOptions{
@@ -220,27 +195,20 @@ func TestBuildAUnitTestBody(t *testing.T) {
}, },
} }
var metadataString, optionsString, objectSetString string objectSetString := buildAUnitObjectSetString(config)
optionsString := buildAUnitOptionsString(config)
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString) assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString) assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, nil, err)
}) })
t.Run("Test AUnit test run body with example yaml config of only Multi Property Set with scomps & packages on top level", func(t *testing.T) { t.Run("Test AUnit test run body with example yaml config of only Multi Property Set with scomps & packages on top level", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := `<aunit:run title="Test Title" context="Test Context" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>` expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="false" foreignTests="false"/><aunit:riskLevel harmless="false" dangerous="false" critical="false"/><aunit:duration short="false" medium="false" long="false"/></aunit:options>`
expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:package name="testPackage1"/><osl:package name="testPackage2"/><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet></aunit:run>` expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:package name="testPackage1"/><osl:package name="testPackage2"/><osl:softwareComponent name="testComponent1"/><osl:softwareComponent name="testComponent2"/></osl:objectSet>`
var err error config := AUnitConfig{
var config AUnitConfig
config = AUnitConfig{
Title: "Test Title", Title: "Test Title",
Context: "Test Context", Context: "Test Context",
Options: AUnitOptions{ Options: AUnitOptions{
@@ -274,79 +242,18 @@ func TestBuildAUnitTestBody(t *testing.T) {
}, },
} }
var metadataString, optionsString, objectSetString string objectSetString := buildAUnitObjectSetString(config)
optionsString := buildAUnitOptionsString(config)
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString) assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString) assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, nil, err)
})
t.Run("Test AUnit test run body with example yaml config fail: no Title", func(t *testing.T) {
t.Parallel()
expectedmetadataString := ""
expectedoptionsString := ""
expectedobjectSetString := ""
var err error
var config AUnitConfig
config = AUnitConfig{}
var metadataString, optionsString, objectSetString string
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString)
assert.EqualError(t, err, "Error while parsing AUnit test run config. No title for the AUnit run has been provided. Please configure an appropriate title for the respective test run")
})
t.Run("Test AUnit test run body with example yaml config: no Context", func(t *testing.T) {
t.Parallel()
expectedmetadataString := `<aunit:run title="Test" context="ABAP Environment Pipeline" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="true" foreignTests="true"/><aunit:riskLevel harmless="true" dangerous="true" critical="true"/><aunit:duration short="true" medium="true" long="true"/></aunit:options>`
expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:package name="testPackage1"/><osl:softwareComponent name="testComponent1"/></osl:objectSet></aunit:run>`
var err error
var config AUnitConfig
config = AUnitConfig{Title: "Test",
ObjectSet: ObjectSet{
PackageNames: []AUnitPackage{{
Name: "testPackage1",
}},
SoftwareComponents: []SoftwareComponents{{
Name: "testComponent1",
}},
}}
var metadataString, optionsString, objectSetString string
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, err, nil)
}) })
t.Run("Test AUnit test run body with example yaml config: no Options", func(t *testing.T) { t.Run("Test AUnit test run body with example yaml config: no Options", func(t *testing.T) {
t.Parallel() t.Parallel()
expectedmetadataString := `<aunit:run title="Test" context="Test" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="true" foreignTests="true"/><aunit:riskLevel harmless="true" dangerous="true" critical="true"/><aunit:duration short="true" medium="true" long="true"/></aunit:options>` expectedoptionsString := `<aunit:options><aunit:measurements type="none"/><aunit:scope ownTests="true" foreignTests="true"/><aunit:riskLevel harmless="true" dangerous="true" critical="true"/><aunit:duration short="true" medium="true" long="true"/></aunit:options>`
expectedobjectSetString := `<osl:objectSet xsi:type="multiPropertySet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:package name="testPackage1"/><osl:softwareComponent name="testComponent1"/></osl:objectSet></aunit:run>` config := AUnitConfig{Title: "Test", Context: "Test",
var err error
var config AUnitConfig
config = AUnitConfig{Title: "Test", Context: "Test",
ObjectSet: ObjectSet{ ObjectSet: ObjectSet{
PackageNames: []AUnitPackage{{ PackageNames: []AUnitPackage{{
Name: "testPackage1", Name: "testPackage1",
@@ -356,36 +263,134 @@ func TestBuildAUnitTestBody(t *testing.T) {
}}, }},
}} }}
var metadataString, optionsString, objectSetString string optionsString := buildAUnitOptionsString(config)
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config)
assert.Equal(t, expectedmetadataString, metadataString)
assert.Equal(t, expectedoptionsString, optionsString) assert.Equal(t, expectedoptionsString, optionsString)
assert.Equal(t, expectedobjectSetString, objectSetString)
assert.Equal(t, err, nil)
}) })
t.Run("Test AUnit test run body with example yaml config fail: no ObjectSet", func(t *testing.T) { t.Run("Config with repository-yml", func(t *testing.T) {
t.Parallel()
expectedmetadataString := "" config := abapEnvironmentRunAUnitTestOptions{
expectedoptionsString := "" AUnitResultsFileName: "aUnitResults.xml",
expectedobjectSetString := "" Repositories: "repositories.yml",
}
var err error dir, err := ioutil.TempDir("", "test parse AUnit yaml config2")
var config AUnitConfig if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
config = AUnitConfig{Title: "Test", Context: "Test", Options: AUnitOptions{Measurements: "Test"}} repositories := `repositories:
- name: /DMO/REPO
branch: main
`
expectedBodyString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><aunit:run title=\"AUnit Test Run\" context=\"ABAP Environment Pipeline\" xmlns:aunit=\"http://www.sap.com/adt/api/aunit\"><aunit:options><aunit:measurements type=\"none\"/><aunit:scope ownTests=\"true\" foreignTests=\"true\"/><aunit:riskLevel harmless=\"true\" dangerous=\"true\" critical=\"true\"/><aunit:duration short=\"true\" medium=\"true\" long=\"true\"/></aunit:options><osl:objectSet xsi:type=\"multiPropertySet\" xmlns:osl=\"http://www.sap.com/api/osl\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><osl:softwareComponent name=\"/DMO/REPO\"/></osl:objectSet></aunit:run>"
err = ioutil.WriteFile(config.Repositories, []byte(repositories), 0644)
if assert.Equal(t, err, nil) {
bodyString, err := buildAUnitRequestBody(config)
assert.Equal(t, nil, err)
assert.Equal(t, expectedBodyString, bodyString)
}
})
var metadataString, optionsString, objectSetString string t.Run("Config with aunitconfig-yml", func(t *testing.T) {
metadataString, optionsString, objectSetString, err = buildAUnitTestBody(config) config := abapEnvironmentRunAUnitTestOptions{
AUnitResultsFileName: "aUnitResults.xml",
AUnitConfig: "aunit.yml",
}
assert.Equal(t, expectedmetadataString, metadataString) dir, err := ioutil.TempDir("", "test parse AUnit yaml config2")
assert.Equal(t, expectedoptionsString, optionsString) if err != nil {
assert.Equal(t, expectedobjectSetString, objectSetString) t.Fatal("Failed to create temporary directory")
assert.EqualError(t, err, "Error while parsing AUnit test run object set config. No object set has been provided. Please configure the objects you want to be checked for the respective test run") }
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
yamlBody := `title: My AUnit run
objectset:
packages:
- name: Z_TEST
softwarecomponents:
- name: Z_TEST
- name: /DMO/SWC
`
expectedBodyString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><aunit:run title=\"My AUnit run\" context=\"ABAP Environment Pipeline\" xmlns:aunit=\"http://www.sap.com/adt/api/aunit\"><aunit:options><aunit:measurements type=\"none\"/><aunit:scope ownTests=\"true\" foreignTests=\"true\"/><aunit:riskLevel harmless=\"true\" dangerous=\"true\" critical=\"true\"/><aunit:duration short=\"true\" medium=\"true\" long=\"true\"/></aunit:options><osl:objectSet xsi:type=\"multiPropertySet\" xmlns:osl=\"http://www.sap.com/api/osl\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><osl:package name=\"Z_TEST\"/><osl:softwareComponent name=\"Z_TEST\"/><osl:softwareComponent name=\"/DMO/SWC\"/></osl:objectSet></aunit:run>"
err = ioutil.WriteFile(config.AUnitConfig, []byte(yamlBody), 0644)
if assert.Equal(t, err, nil) {
bodyString, err := buildAUnitRequestBody(config)
assert.Equal(t, nil, err)
assert.Equal(t, expectedBodyString, bodyString)
}
})
t.Run("Config with aunitconfig-yml mps", func(t *testing.T) {
config := abapEnvironmentRunAUnitTestOptions{
AUnitResultsFileName: "aUnitResults.xml",
AUnitConfig: "aunit.yml",
}
dir, err := ioutil.TempDir("", "test parse AUnit yaml config2")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
yamlBody := `title: My AUnit run
objectset:
type: multiPropertySet
multipropertyset:
packages:
- name: Z_TEST
softwarecomponents:
- name: Z_TEST
- name: /DMO/SWC
`
expectedBodyString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><aunit:run title=\"My AUnit run\" context=\"ABAP Environment Pipeline\" xmlns:aunit=\"http://www.sap.com/adt/api/aunit\"><aunit:options><aunit:measurements type=\"none\"/><aunit:scope ownTests=\"true\" foreignTests=\"true\"/><aunit:riskLevel harmless=\"true\" dangerous=\"true\" critical=\"true\"/><aunit:duration short=\"true\" medium=\"true\" long=\"true\"/></aunit:options><osl:objectSet xsi:type=\"multiPropertySet\" xmlns:osl=\"http://www.sap.com/api/osl\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><osl:package name=\"Z_TEST\"/><osl:softwareComponent name=\"Z_TEST\"/><osl:softwareComponent name=\"/DMO/SWC\"/></osl:objectSet></aunit:run>"
err = ioutil.WriteFile(config.AUnitConfig, []byte(yamlBody), 0644)
if assert.Equal(t, err, nil) {
bodyString, err := buildAUnitRequestBody(config)
assert.Equal(t, nil, err)
assert.Equal(t, expectedBodyString, bodyString)
}
})
t.Run("No AUnit config file - expect no panic", func(t *testing.T) {
config := abapEnvironmentRunAUnitTestOptions{
AUnitConfig: "aunit.yml",
}
_, err := buildAUnitRequestBody(config)
assert.Equal(t, "Could not find aunit.yml", err.Error())
})
t.Run("No Repo config file - expect no panic", func(t *testing.T) {
config := abapEnvironmentRunAUnitTestOptions{
Repositories: "repo.yml",
}
_, err := buildAUnitRequestBody(config)
assert.Equal(t, "Could not find repo.yml", err.Error())
}) })
} }
@@ -528,7 +533,7 @@ func TestParseAUnitResult(t *testing.T) {
}() }()
bodyString := `<?xml version="1.0" encoding="utf-8"?><testsuites title="My AUnit run" system="TST" client="100" executedBy="TESTUSER" time="000.000" timestamp="2021-01-01T00:00:00Z" failures="2" errors="2" skipped="0" asserts="0" tests="2"><testsuite name="" tests="2" failures="2" errors="0" skipped="0" asserts="0" package="testpackage" timestamp="2021-01-01T00:00:00ZZ" time="0.000" hostname="test"><testcase classname="test" name="execute" time="0.000" asserts="2"><failure message="testMessage1" type="Assert Failure">Test1</failure><failure message="testMessage2" type="Assert Failure">Test2</failure></testcase></testsuite></testsuites>` bodyString := `<?xml version="1.0" encoding="utf-8"?><testsuites title="My AUnit run" system="TST" client="100" executedBy="TESTUSER" time="000.000" timestamp="2021-01-01T00:00:00Z" failures="2" errors="2" skipped="0" asserts="0" tests="2"><testsuite name="" tests="2" failures="2" errors="0" skipped="0" asserts="0" package="testpackage" timestamp="2021-01-01T00:00:00ZZ" time="0.000" hostname="test"><testcase classname="test" name="execute" time="0.000" asserts="2"><failure message="testMessage1" type="Assert Failure">Test1</failure><failure message="testMessage2" type="Assert Failure">Test2</failure></testcase></testsuite></testsuites>`
body := []byte(bodyString) body := []byte(bodyString)
err = parseAUnitResult(body, "AUnitResults.xml", false) err = persistAUnitResult(body, "AUnitResults.xml", false)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
@@ -547,7 +552,7 @@ func TestParseAUnitResult(t *testing.T) {
}() }()
bodyString := `<?xml version="1.0" encoding="UTF-8"?>` bodyString := `<?xml version="1.0" encoding="UTF-8"?>`
body := []byte(bodyString) body := []byte(bodyString)
err = parseAUnitResult(body, "AUnitResults.xml", false) err = persistAUnitResult(body, "AUnitResults.xml", false)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
}) })
@@ -556,7 +561,7 @@ func TestParseAUnitResult(t *testing.T) {
var bodyString string var bodyString string
body := []byte(bodyString) body := []byte(bodyString)
err := parseAUnitResult(body, "AUnitResults.xml", false) err := persistAUnitResult(body, "AUnitResults.xml", false)
assert.EqualError(t, err, "Parsing AUnit result failed: Body is empty, can't parse empty body") assert.EqualError(t, err, "Parsing AUnit result failed: Body is empty, can't parse empty body")
}) })
} }
@@ -576,7 +581,7 @@ func TestGetResultAUnitRun(t *testing.T) {
Password: "Test", Password: "Test",
URL: "https://api.endpoint.com/Entity/", URL: "https://api.endpoint.com/Entity/",
} }
resp, err := getResultAUnitRun("GET", con, []byte(client.Body), client) resp, err := getAUnitResults("GET", con, []byte(client.Body), client)
defer resp.Body.Close() defer resp.Body.Close()
if assert.Equal(t, nil, err) { if assert.Equal(t, nil, err) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -603,7 +608,7 @@ func TestGetResultAUnitRun(t *testing.T) {
Password: "Test", Password: "Test",
URL: "https://api.endpoint.com/Entity/", URL: "https://api.endpoint.com/Entity/",
} }
resp, err := getResultAUnitRun("GET", con, []byte(client.Body), client) resp, err := getAUnitResults("GET", con, []byte(client.Body), client)
defer resp.Body.Close() defer resp.Body.Close()
if assert.EqualError(t, err, "Getting AUnit run results failed: Test fail") { if assert.EqualError(t, err, "Getting AUnit run results failed: Test fail") {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)

View File

@@ -20,11 +20,11 @@ You can have a look at different pipeline configurations in our [SAP-samples rep
## 1. Prerequisites ## 1. Prerequisites
* Configure your Jenkins Server according to the [documentation](https://sap.github.io/jenkins-library/guidedtour/). * Configure your Jenkins server according to the [documentation](https://sap.github.io/jenkins-library/guidedtour/).
* Create a git repository on a host reachable by the Jenkins server (e.g. GitHub.com). The pipeline will be configured in this repository. Create a GitHub User with read access. * Create a git repository on a host reachable by the Jenkins server (e.g. GitHub.com). The pipeline will be configured in this repository. Create a GitHub user with read access.
* The entitlements for the ABAP environment system are available in the SAP BTP global account and assigned to the subaccount. * The entitlements for the ABAP environment system are available in the SAP BTP global account and assigned to the subaccount.
* A Cloud Foundry Organization & Space with the allocated entitlements are available. * A Cloud Foundry organization & space with the allocated entitlements are available.
* A Cloud Foundry User & Password with the required authorization ("Space Developer") in the Organization and Space are available. User and Password were saved in the Jenkins Credentials Store. * A Cloud Foundry user & password with the required authorization ("Space Developer") in the organization and space are available. User and password were saved in the Jenkins Credentials Store.
## 2. Jenkinsfile ## 2. Jenkinsfile
@@ -46,7 +46,7 @@ An Overview of the releases of the project "Piper" library can be found [here](h
## 3. Configuration for Cloning the repositories ## 3. Configuration for Cloning the repositories
If you have specified the `Clone Repositories` Stage you can make use of a dedicated configuration file containing the repositories to be pulled and the branches to be switched on. The `repositories` flag makes use of such a configuration file and helps executing a Pull, Clone and Checkout of the Branches of the Repositores. Create the file `repositories.yml` with the following structure containing your repositories including the branches for this stage. If you have specified the `Clone Repositories` stage you can make use of a dedicated configuration file containing the repositories to be pulled and the branches to be switched on. The `repositories` flag makes use of such a configuration file and helps executing a `Pull`, `Clone` and `Checkout` of the branches of the repositores. Create the file `repositories.yml` with the following structure containing your repositories including the branches for this stage.
```yml ```yml
repositories: repositories:
@@ -56,11 +56,16 @@ repositories:
branch: 'master' branch: 'master'
``` ```
You can later use the `repositories.yml` file for the `repositories` parameter in the `Clone Repositories` stage used in chapter [7. Technical Pipeline Configuration](#7-technical-pipeline-configuration). You can later use the `repositories.yml` file for the `repositories` parameter in the `Clone Repositories` stage used in chapter [5. Technical Pipeline Configuration](#5-technical-pipeline-configuration).
## 4. Configuration for ATC ## 4. Optional: Configuration for the ABAP Test Cockpit (ATC) and ABAP Unit Tests (AUnit)
Create a file `atcConfig.yml` to store the configuration for the ATC run. In this file, you can specify which packages or software components shall be checked. Please have a look at the step documentation for more details. Here is an example of the configuration: As a default, a configuration for ATC and AUnit is generated out of the `repositories.yml` file. This default configuration checks the listed repositories using the default ATC check variant and with no AUnit restrictions regarding `duration` and `risk level`.
If you want to configure these tools yourself, you can create a tool specific configuration.
### ATC
Create a file `atcConfig.yml` to store the configuration for the ATC run. In this file, you can specify which packages or software components shall be checked. Please have a look at the [step documentation](https://sap.github.io/jenkins-library/steps/abapEnvironmentRunATCCheck/) for more details. You have to pass the filename `atcConfig.yml` to the `atcConfig` parameter in the [5. Technical Pipeline Configuration](#5-technical-pipeline-configuration). Here is an example of the configuration:
```yml ```yml
atcobjects: atcobjects:
@@ -68,7 +73,17 @@ atcobjects:
- name: "/DMO/REPO" - name: "/DMO/REPO"
``` ```
Please have a look at the [step documentation](https://sap.github.io/jenkins-library/steps/abapEnvironmentRunATCCheck/) for more details. ### AUnit
Create a file `aunitConfig.yml` to store the configuration for the AUnit run. In this file, you can specify which packages or software components shall be checked. Please have a look at the [step documentation](https://sap.github.io/jenkins-library/steps/abapEnvironmentRunAUnitTest/) for more details. You have to pass the filename `aunitConfig.yml` to the `aunitConfig` parameter in the [5. Technical Pipeline Configuration](#5-technical-pipeline-configuration). Here is an example of the configuration:
```yml
title: My AUnit run
objectSet:
softwarecomponents:
- name: Z_TEST_SC
- name: Z_TEST_SC2
```
## 5. Technical Pipeline Configuration ## 5. Technical Pipeline Configuration
@@ -95,7 +110,9 @@ stages:
strategy: 'Clone' strategy: 'Clone'
repositories: 'repositories.yml' repositories: 'repositories.yml'
ATC: ATC:
atcConfig: 'atcConfig.yml' active: true
AUnit:
active: true
Post: Post:
cfDeleteServiceKeys: true cfDeleteServiceKeys: true
``` ```
@@ -118,7 +135,11 @@ If the `Clone Repositories` stage is configured, you can specify the `strategy`
Note that you can use the `repositories.yml` file with the `repositories` parameter consistently for all strategies. Note that you can use the `repositories.yml` file with the `repositories` parameter consistently for all strategies.
The values for `cfApiEndpoint`,`cfOrg` and `cfSpace` can be found in the respective overview pages in the SAP BTP cockpit. The Cloud Foundry credentials, saved in the Jenkins credentials store with the ID `cfCredentialsId`, must refer to a user with the required authorizations ("Space Developer") for the Cloud Foundry organization and space. The values for `cfApiEndpoint`,`cfOrg` and `cfSpace` can be found in the respective overview pages in the SAP BTP cockpit. The SAP BTP / Cloud Foundry credentials, saved in the Jenkins credentials store with the ID `cfCredentialsId`, must refer to a user with the required authorizations ("Space Developer") for the SAP BTP / Cloud Foundry organization and space.
### ATC & AUnit
The ATC and AUnit stage will be executed, if the `config.yml` file contains an entry for the respective stage. If you are using the default configuration a placeholder entry `active: true` has to be added in order to activate the stages. If you are using a dedicated configuration file - `atcConfig.yml` and `aunitConfig.yml` - this is not necessary.
## 7. Create a Jenkins Pipeline ## 7. Create a Jenkins Pipeline

View File

@@ -20,7 +20,7 @@ There are no specifc stage parameters.
### Stage Activation ### Stage Activation
This stage will be active, if the stage configuration in the `config.yml` contains entries for this stage.. This stage will be active, if the stage configuration in the `config.yml` contains entries for this stage.
### Configuration Example ### Configuration Example

View File

@@ -82,6 +82,8 @@ To trigger the ATC run an ATC config file `atcconfig.yml` will be needed. Check
### ATC config file example ### ATC config file example
Providing a specifc ATC configuration is optional. If you are using a `repositories.yml` file for the `Clone` stage of the ABAP environment pipeline, a default ATC configuration will be derived if no explicit ATC configuration is available.
The following section contains an example of an `atcconfig.yml` file. The following section contains an example of an `atcconfig.yml` file.
This file must be stored in the same Git folder where the `Jenkinsfile` is stored to run the pipeline. This folder must be taken as a SCM in the Jenkins pipeline to run the pipeline. This file must be stored in the same Git folder where the `Jenkinsfile` is stored to run the pipeline. This folder must be taken as a SCM in the Jenkins pipeline to run the pipeline.

View File

@@ -83,6 +83,8 @@ To trigger the AUnit run an AUnit config file `aUnitConfig.yml` will be needed.
### AUnit config file example ### AUnit config file example
Providing a specifc AUnit configuration is optional. If you are using a `repositories.yml` file for the `Clone` stage of the ABAP environment pipeline, a default AUnit configuration will be derived if no explicit AUnit configuration is available.
The following section contains an example of an `aUnitConfig.yml` file. The following section contains an example of an `aUnitConfig.yml` file.
This file must be stored in the same Git folder where the `Jenkinsfile` is stored to run the pipeline. This repository containing the `Jenkinsfile` must be taken as a SCM in the Jenkins pipeline to run the pipeline. This file must be stored in the same Git folder where the `Jenkinsfile` is stored to run the pipeline. This repository containing the `Jenkinsfile` must be taken as a SCM in the Jenkins pipeline to run the pipeline.
@@ -98,7 +100,7 @@ title: My AUnit run
context: My unit tests context: My unit tests
objectset: objectset:
softwarecomponents: softwarecomponents:
- name: /DMO/SWC - name: /DMO/SWC
``` ```
See below example for an `aUnitConfig.yml` file with the configured options containing the package `Z_TEST_PACKAGE` to be checked: See below example for an `aUnitConfig.yml` file with the configured options containing the package `Z_TEST_PACKAGE` to be checked:
@@ -108,7 +110,7 @@ title: My AUnit run
context: My unit tests context: My unit tests
objectset: objectset:
packages: packages:
- name: Z_TEST_PACKAGE - name: Z_TEST_PACKAGE
``` ```
The following example of an `aUnitConfig.yml` file containing the software component `Z_TESTSC` and shows the available options: The following example of an `aUnitConfig.yml` file containing the software component `Z_TESTSC` and shows the available options:
@@ -131,7 +133,7 @@ options:
long: true long: true
objectset: objectset:
softwarecomponents: softwarecomponents:
- name: Z_TESTSC - name: Z_TESTSC
``` ```
The following example of an `aUnitConfig.yml` file contains all possible properties of the Multi Property Set that can be used. Please take note that this is not the reccommended approach. If you want to check packages or software components please use the two above examples. The usage of the Multi Property Set is only reccommended for ABAP Unit tests that require these rules for the test execution. There is no official documentation on the usage of the Multi Property Set. The following example of an `aUnitConfig.yml` file contains all possible properties of the Multi Property Set that can be used. Please take note that this is not the reccommended approach. If you want to check packages or software components please use the two above examples. The usage of the Multi Property Set is only reccommended for ABAP Unit tests that require these rules for the test execution. There is no official documentation on the usage of the Multi Property Set.

View File

@@ -7,6 +7,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -16,6 +17,7 @@ import (
"github.com/SAP/jenkins-library/pkg/command" "github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http" piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/log"
"github.com/ghodss/yaml"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -119,6 +121,30 @@ func (abaputils *AbapUtils) GetPollIntervall() time.Duration {
return 10 * time.Second return 10 * time.Second
} }
/*
ReadCOnfigFile reads a file from a specific path and returns the json string as []byte
*/
func ReadConfigFile(path string) (file []byte, err error) {
filelocation, err := filepath.Glob(path)
if err != nil {
return nil, err
}
if len(filelocation) == 0 {
return nil, errors.New("Could not find " + path)
}
filename, err := filepath.Abs(filelocation[0])
if err != nil {
return nil, err
}
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var jsonFile []byte
jsonFile, err = yaml.YAMLToJSON(yamlFile)
return jsonFile, err
}
// GetHTTPResponse wraps the SendRequest function of piperhttp // GetHTTPResponse wraps the SendRequest function of piperhttp
func GetHTTPResponse(requestType string, connectionDetails ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) { func GetHTTPResponse(requestType string, connectionDetails ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {

View File

@@ -26,7 +26,15 @@ spec:
- PARAMETERS - PARAMETERS
- STAGES - STAGES
- STEPS - STEPS
mandatory: true mandatory: false
- name: repositories
type: string
description: Specifies a YAML file containing the repositories configuration
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: cfApiEndpoint - name: cfApiEndpoint
type: string type: string
description: Cloud Foundry API endpoint description: Cloud Foundry API endpoint

View File

@@ -26,7 +26,15 @@ spec:
- PARAMETERS - PARAMETERS
- STAGES - STAGES
- STEPS - STEPS
mandatory: true mandatory: false
- name: repositories
type: string
description: Specifies a YAML file containing the repositories configuration
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: cfApiEndpoint - name: cfApiEndpoint
type: string type: string
description: Cloud Foundry API endpoint description: Cloud Foundry API endpoint