1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-02-15 14:04:04 +02:00

AUnit step (#3075)

* Add AUnit step

* Add Groovy File

* Update metadata

* Add groovy test config

* Add Unit tests

* Add Documentation

* Docu fixes

* change docu

* change docu

* change logging level & remove sleep

* change docu

* change docu

* Update documentation/docs/steps/abapEnvironmentRunAUnitTest.md

Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com>
This commit is contained in:
Dominik Lendle 2021-09-20 11:14:13 +02:00 committed by GitHub
parent cacdb2b867
commit 09f5c0a524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1565 additions and 0 deletions

View File

@ -0,0 +1,566 @@
package cmd
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"path/filepath"
"reflect"
"time"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
)
type abapEnvironmentRunAUnitTestUtils interface {
command.ExecRunner
FileExists(filename string) (bool, error)
// Add more methods here, or embed additional interfaces, or remove/replace as required.
// The abapEnvironmentRunAUnitTestUtils interface should be descriptive of your runtime dependencies,
// i.e. include everything you need to be able to mock in tests.
// Unit tests shall be executable in parallel (not depend on global state), and don't (re-)test dependencies.
}
type abapEnvironmentRunAUnitTestUtilsBundle struct {
*command.Command
*piperutils.Files
// Embed more structs as necessary to implement methods or interfaces you add to abapEnvironmentRunAUnitTestUtils.
// Structs embedded in this way must each have a unique set of methods attached.
// If there is no struct which implements the method you need, attach the method to
// abapEnvironmentRunAUnitTestUtilsBundle and forward to the implementation of the dependency.
}
func newAbapEnvironmentRunAUnitTestUtils() abapEnvironmentRunAUnitTestUtils {
utils := abapEnvironmentRunAUnitTestUtilsBundle{
Command: &command.Command{},
Files: &piperutils.Files{},
}
// Reroute command output to logging framework
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func abapEnvironmentRunAUnitTest(config abapEnvironmentRunAUnitTestOptions, telemetryData *telemetry.CustomData) {
// for command execution use Command
c := command.Command{}
// reroute command output to logging framework
c.Stdout(log.Writer())
c.Stderr(log.Writer())
var autils = abaputils.AbapUtils{
Exec: &c,
}
client := piperhttp.Client{}
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
err := runAbapEnvironmentRunAUnitTest(&config, telemetryData, &autils, &client)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runAbapEnvironmentRunAUnitTest(config *abapEnvironmentRunAUnitTestOptions, telemetryData *telemetry.CustomData, com abaputils.Communication, client piperhttp.Sender) error {
var details abaputils.ConnectionDetailsHTTP
subOptions := convertAUnitOptions(config)
details, err := com.GetAbapCommunicationArrangementInfo(subOptions, "")
var resp *http.Response
cookieJar, _ := cookiejar.New(nil)
//Fetch Xcrsf-Token
if err == nil {
credentialsOptions := piperhttp.ClientOptions{
Username: details.User,
Password: details.Password,
CookieJar: cookieJar,
}
client.SetOptions(credentialsOptions)
details.XCsrfToken, err = fetchAUnitXcsrfToken("GET", details, nil, client)
}
if err == nil {
resp, err = triggerAUnitrun(*config, details, client)
}
if err == nil {
err = handleAUnitResults(resp, details, client, config.AUnitResultsFileName)
}
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
log.Entry().Info("AUnit test 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")
return nil
}
func triggerAUnitrun(config abapEnvironmentRunAUnitTestOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (*http.Response, error) {
var aUnitConfigYamlFile []byte
abapEndpoint := details.URL
filelocation, err := filepath.Glob(config.AUnitConfig)
//Parse YAML AUnit run configuration as body for AUnit run trigger
if err == nil {
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
var resp *http.Response
var bodyString = `<?xml version="1.0" encoding="UTF-8"?>` + metadataString + optionsString + objectSetString + `</aunit:run>`
var body = []byte(bodyString)
if err == nil {
log.Entry().Debugf("Request Body: %s", bodyString)
details.URL = abapEndpoint + "/sap/bc/adt/api/abapunit/runs"
resp, err = runAUnit("POST", details, body, client)
}
if err != nil {
return resp, fmt.Errorf("Triggering AUnit test run failed: %w", err)
}
return resp, nil
}
func convertAUnitOptions(options *abapEnvironmentRunAUnitTestOptions) abaputils.AbapEnvironmentOptions {
subOptions := abaputils.AbapEnvironmentOptions{}
subOptions.CfAPIEndpoint = options.CfAPIEndpoint
subOptions.CfServiceInstance = options.CfServiceInstance
subOptions.CfServiceKeyName = options.CfServiceKeyName
subOptions.CfOrg = options.CfOrg
subOptions.CfSpace = options.CfSpace
subOptions.Host = options.Host
subOptions.Password = options.Password
subOptions.Username = options.Username
return subOptions
}
func handleAUnitResults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, aunitResultFileName string) error {
var err error
var abapEndpoint string
abapEndpoint = details.URL
location := resp.Header.Get("Location")
details.URL = abapEndpoint + location
location, err = pollAUnitRun(details, nil, client)
if err == nil {
details.URL = abapEndpoint + location
resp, err = getResultAUnitRun("GET", details, nil, client)
}
//Parse response
var body []byte
if err == nil {
body, err = ioutil.ReadAll(resp.Body)
}
if err == nil {
defer resp.Body.Close()
err = parseAUnitResult(body, aunitResultFileName)
}
if err != nil {
return fmt.Errorf("Handling AUnit result failed: %w", err)
}
return nil
}
func buildAUnitTestBody(AUnitConfig AUnitConfig) (metadataString string, optionsString string, objectSetString string, err error) {
//Checks before building the XML body
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")
} else if AUnitConfig.Context == "" {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No context for the AUnit run has been provided. Please configure an appropriate context for the respective test run")
} else if reflect.DeepEqual(AUnitOptions{}, AUnitConfig.Options) {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No options have been provided. Please configure the options for the respective test run")
} else 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")
} else if len(AUnitConfig.ObjectSet) == 0 {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run object set config. No object set has been provided. Please configure the set of objects you want to be checked for the respective test run")
}
//Build metadata string
metadataString += `<aunit:run title="` + AUnitConfig.Title + `" context="` + AUnitConfig.Context + `" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
//Build Options
optionsString += buildAUnitOptionsString(AUnitConfig)
//Build Object Set
objectSetString += buildAUnitObjectSetString(AUnitConfig)
return metadataString, optionsString, objectSetString, nil
}
func runAUnit(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP endpoint: ", details.URL).Info("Triggering AUnit run")
header := make(map[string][]string)
header["X-Csrf-Token"] = []string{details.XCsrfToken}
header["Content-Type"] = []string{"application/vnd.sap.adt.api.abapunit.run.v1+xml; charset=utf-8;"}
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
if err != nil {
return req, fmt.Errorf("Triggering AUnit run failed: %w", err)
}
defer req.Body.Close()
return req, err
}
func buildAUnitOptionsString(AUnitConfig AUnitConfig) (optionsString string) {
optionsString += `<aunit:options>`
if AUnitConfig.Options.Measurements != "" {
optionsString += `<aunit:measurements type="` + AUnitConfig.Options.Measurements + `"/>`
}
optionsString += `<aunit:scope`
if AUnitConfig.Options.Scope.OwnTests != nil {
optionsString += ` ownTests="` + fmt.Sprintf("%v", *AUnitConfig.Options.Scope.OwnTests) + `"`
}
if AUnitConfig.Options.Scope.ForeignTests != nil {
optionsString += ` foreignTests="` + fmt.Sprintf("%v", *AUnitConfig.Options.Scope.ForeignTests) + `"`
}
optionsString += `/><aunit:riskLevel`
if AUnitConfig.Options.RiskLevel.Harmless != nil {
optionsString += ` harmless="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Harmless) + `"`
}
if AUnitConfig.Options.RiskLevel.Dangerous != nil {
optionsString += ` dangerous="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Dangerous) + `"`
}
if AUnitConfig.Options.RiskLevel.Critical != nil {
optionsString += ` critical="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Critical) + `"`
}
optionsString += `/><aunit:duration`
if AUnitConfig.Options.Duration.Short != nil {
optionsString += ` short="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Short) + `"`
}
if AUnitConfig.Options.Duration.Medium != nil {
optionsString += ` medium="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Medium) + `"`
}
if AUnitConfig.Options.Duration.Long != nil {
optionsString += ` long="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Long) + `"`
}
optionsString += `/></aunit:options>`
return optionsString
}
func buildAUnitObjectSetString(AUnitConfig AUnitConfig) (objectSetString string) {
//Build ObjectSets {
for _, s := range AUnitConfig.ObjectSet {
//Write object set
objectSetString += `<osl:objectSet xsi:type="` + s.Type + `" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">`
for _, t := range s.Set {
objectSetString += `<osl:set xsi:type="` + t.Type + `">`
for _, packageSet := range t.PackageSet {
objectSetString += `<osl:package name="` + packageSet.Name + `" includeSubpackages="` + fmt.Sprintf("%v", *packageSet.IncludeSubpackages) + `"/>`
}
for _, flatObjectSet := range t.FlatObjectSet {
objectSetString += `<osl:package name="` + flatObjectSet.Name + `" includeSubpackages="` + fmt.Sprintf("%v", *&flatObjectSet.Type) + `"/>`
}
objectSetString += `</osl:set>`
}
objectSetString += `</osl:objectSet>`
}
return objectSetString
}
func fetchAUnitXcsrfToken(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Debug("Fetching Xcrsf-Token")
details.URL += "/sap/bc/adt/api/abapunit/runs/00000000000000000000000000000000"
details.XCsrfToken = "fetch"
header := make(map[string][]string)
header["X-Csrf-Token"] = []string{details.XCsrfToken}
header["Accept"] = []string{"application/vnd.sap.adt.api.abapunit.run-status.v1+xml"}
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
if err != nil {
return "", fmt.Errorf("Fetching Xcsrf-Token failed: %w", err)
}
defer req.Body.Close()
token := req.Header.Get("X-Csrf-Token")
return token, err
}
func pollAUnitRun(details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
log.Entry().WithField("ABAP endpoint", details.URL).Info("Polling AUnit run status")
for {
resp, err := getHTTPResponseAUnitRun("GET", details, nil, client)
if err != nil {
return "", fmt.Errorf("Getting HTTP response failed: %w", err)
}
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Reading response body failed: %w", err)
}
x := new(AUnitRun)
xml.Unmarshal(bodyText, &x)
log.Entry().Infof("Current polling status: %s", x.Progress.Status)
if x.Progress.Status == "Not Created" {
return "", err
}
if x.Progress.Status == "Completed" || x.Progress.Status == "FINISHED" {
return x.Link.Href, err
}
if x.Progress.Status == "" {
return "", fmt.Errorf("Could not get any response from AUnit poll: %w", errors.New("Status from AUnit run is empty. Either it's not an ABAP system or AUnit run hasn't started"))
}
time.Sleep(10 * time.Second)
}
}
func getHTTPResponseAUnitRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Polling AUnit run status")
header := make(map[string][]string)
header["Accept"] = []string{"application/vnd.sap.adt.api.abapunit.run-status.v1+xml"}
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
if err != nil {
return req, fmt.Errorf("Getting AUnit run status failed: %w", err)
}
return req, err
}
func getResultAUnitRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Getting AUnit results")
header := make(map[string][]string)
header["x-csrf-token"] = []string{details.XCsrfToken}
header["Accept"] = []string{"application/vnd.sap.adt.api.junit.run-result.v1+xml"}
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
if err != nil {
return req, fmt.Errorf("Getting AUnit run results failed: %w", err)
}
return req, err
}
func parseAUnitResult(body []byte, aunitResultFileName string) (err error) {
if len(body) == 0 {
return fmt.Errorf("Parsing AUnit result failed: %w", errors.New("Body is empty, can't parse empty body"))
}
responseBody := string(body)
log.Entry().Debugf("Response body: %s", responseBody)
//Optional checks before writing the Results
parsedXML := new(AUnitResult)
xml.Unmarshal([]byte(body), &parsedXML)
if len(parsedXML.Testsuite.Testcase) == 0 {
log.Entry().Info("There were no AUnit findings from this run")
return nil
}
//Write Results
err = ioutil.WriteFile(aunitResultFileName, body, 0644)
if err == nil {
log.Entry().Infof("Writing %s file was successful. Please find the results from the respective AUnit run in the %s file or in below logs", aunitResultFileName, aunitResultFileName)
//Logging of findings
log.Entry().Infof(`Here are the results for the AUnit test run '%s' executed by User %s on System %s in Client %s at %s. The AUnit run took %s seconds and contains %s tests with %s failures, %s errors, %s skipped and %s assert findings`, parsedXML.Title, parsedXML.System, parsedXML.ExecutedBy, parsedXML.Client, parsedXML.Timestamp, parsedXML.Time, parsedXML.Tests, parsedXML.Failures, parsedXML.Errors, parsedXML.Skipped, parsedXML.Asserts)
for _, s := range parsedXML.Testsuite.Testcase {
//Log Info for testcase
for _, failure := range s.Failure {
log.Entry().Debugf("%s, %s: %s found by %s", failure.Type, failure.Message, failure.Message, s.Classname)
}
for _, skipped := range s.Skipped {
log.Entry().Debugf("The following test has been skipped: %s: %s", skipped.Message, skipped.Text)
}
}
//Persist findings afterwards
var reports []piperutils.Path
reports = append(reports, piperutils.Path{Target: aunitResultFileName, Name: "AUnit Results", Mandatory: true})
piperutils.PersistReportsAndLinks("abapEnvironmentRunAUnitTest", "", reports, nil)
}
if err != nil {
return fmt.Errorf("Writing results failed: %w", err)
}
return nil
}
//
// Object Set Structure
//
//AUnitConfig object for parsing yaml config of software components and packages
type AUnitConfig struct {
Title string `json:"title,omitempty"`
Context string `json:"context,omitempty"`
Options AUnitOptions `json:"options,omitempty"`
ObjectSet []ObjectSet `json:"objectset,omitempty"`
}
//AUnitOptions in form of packages and software components to be checked
type AUnitOptions struct {
Measurements string `json:"measurements,omitempty"`
Scope Scope `json:"scope,omitempty"`
RiskLevel RiskLevel `json:"risklevel,omitempty"`
Duration Duration `json:"duration,omitempty"`
}
//Scope in form of packages and software components to be checked
type Scope struct {
OwnTests *bool `json:"owntests,omitempty"`
ForeignTests *bool `json:"foreigntests,omitempty"`
}
//RiskLevel in form of packages and software components to be checked
type RiskLevel struct {
Harmless *bool `json:"harmless,omitempty"`
Dangerous *bool `json:"dangerous,omitempty"`
Critical *bool `json:"critical,omitempty"`
}
//Duration in form of packages and software components to be checked
type Duration struct {
Short *bool `json:"short,omitempty"`
Medium *bool `json:"medium,omitempty"`
Long *bool `json:"long,omitempty"`
}
//ObjectSet in form of packages and software components to be checked
type ObjectSet struct {
Type string `json:"type,omitempty"`
Set []Set `json:"set,omitempty"`
}
//Set in form of packages and software components to be checked
type Set struct {
//Set []Set `json:"set,omitempty"`
Type string `json:"type,omitempty"`
PackageSet []AUnitPackage `json:"package,omitempty"`
FlatObjectSet []AUnitObject `json:"object,omitempty"`
/*FlatSet []FlatObjectSet `json:"flatobjectset,omitempty"`
ObjectTypeSet []ObjectTypeSet `json:"objecttypeset,omitempty"`
ComponentSet []ComponentSet `json:"componentset,omitempty"`
TransportSet []TransportSet `json:"transportset,omitempty"`*/
}
//AUnitPackage in form of packages and software components to be checked
type AUnitPackage struct {
Name string `json:"name,omitempty"`
IncludeSubpackages *bool `json:"includesubpackages,omitempty"`
}
//AUnitObject in form of packages and software components to be checked
type AUnitObject struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
}
//
// AUnit Run Structure
//
//AUnitRun Object for parsing XML
type AUnitRun struct {
XMLName xml.Name `xml:"run"`
Title string `xml:"title,attr"`
Context string `xml:"context,attr"`
Progress Progress `xml:"progress"`
ExecutedBy ExecutedBy `xml:"executedBy"`
Time Time `xml:"time"`
Link AUnitLink `xml:"link"`
}
//Progress of AUnit run
type Progress struct {
Status string `xml:"status,attr"`
Percentage string `xml:"percentage,attr"`
}
//ExecutedBy User
type ExecutedBy struct {
User string `xml:"user,attr"`
}
//Time run was started and finished
type Time struct {
Started string `xml:"started,attr"`
Ended string `xml:"ended,attr"`
}
//AUnitLink containing result locations
type AUnitLink struct {
Href string `xml:"href,attr"`
Rel string `xml:"rel,attr"`
Type string `xml:"type,attr"`
}
//
// AUnit Result Structure
//
type AUnitResult struct {
XMLName xml.Name `xml:"testsuites"`
Title string `xml:"title,attr"`
System string `xml:"system,attr"`
Client string `xml:"client,attr"`
ExecutedBy string `xml:"executedBy,attr"`
Time string `xml:"time,attr"`
Timestamp string `xml:"timestamp,attr"`
Failures string `xml:"failures,attr"`
Errors string `xml:"errors,attr"`
Skipped string `xml:"skipped,attr"`
Asserts string `xml:"asserts,attr"`
Tests string `xml:"tests,attr"`
Testsuite struct {
Tests string `xml:"tests,attr"`
Asserts string `xml:"asserts,attr"`
Skipped string `xml:"skipped,attr"`
Errors string `xml:"errors,attr"`
Failures string `xml:"failures,attr"`
Timestamp string `xml:"timestamp,attr"`
Time string `xml:"time,attr"`
Hostname string `xml:"hostname,attr"`
Package string `xml:"package,attr"`
Name string `xml:"name,attr"`
Testcase []struct {
Asserts string `xml:"asserts,attr"`
Time string `xml:"time,attr"`
Name string `xml:"name,attr"`
Classname string `xml:"classname,attr"`
Error []struct {
Text string `xml:",chardata"`
Type string `xml:"type,attr"`
Message string `xml:"message,attr"`
} `xml:"error"`
Failure []struct {
Text string `xml:",chardata"`
Type string `xml:"type,attr"`
Message string `xml:"message,attr"`
} `xml:"failure"`
Skipped []struct {
Text string `xml:",chardata"`
Message string `xml:"message,attr"`
} `xml:"skipped"`
} `xml:"testcase"`
} `xml:"testsuite"`
}

View File

@ -0,0 +1,254 @@
// 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/splunk"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/spf13/cobra"
)
type abapEnvironmentRunAUnitTestOptions struct {
AUnitConfig string `json:"aUnitConfig,omitempty"`
CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"`
CfOrg string `json:"cfOrg,omitempty"`
CfServiceInstance string `json:"cfServiceInstance,omitempty"`
CfServiceKeyName string `json:"cfServiceKeyName,omitempty"`
CfSpace string `json:"cfSpace,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Host string `json:"host,omitempty"`
AUnitResultsFileName string `json:"aUnitResultsFileName,omitempty"`
}
// AbapEnvironmentRunAUnitTestCommand Runs an AUnit Test
func AbapEnvironmentRunAUnitTestCommand() *cobra.Command {
const STEP_NAME = "abapEnvironmentRunAUnitTest"
metadata := abapEnvironmentRunAUnitTestMetadata()
var stepConfig abapEnvironmentRunAUnitTestOptions
var startTime time.Time
var logCollector *log.CollectorHook
var createAbapEnvironmentRunAUnitTestCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Runs an AUnit Test",
Long: `This step is for triggering an [AUnit](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/cdd19e3a5c49458291ec65d8d86e2b9a.html) test run on an SAP BTP ABAP Environment system.
Please provide either of the following options:
* The host and credentials of the BTP ABAP Environment system itself. The credentials must be configured for the Communication Scenario SAP_COM_0735.
* The Cloud Foundry parameters (API endpoint, organization, space), credentials, the service instance for the ABAP service and the service key for the Communication Scenario SAP_COM_0735.
* Only provide one of those options with the respective credentials. If all values are provided, the direct communication (via host) has priority.
Regardless of the option you chose, please make sure to provide the object set containing the objects that you want to be checked analog to the examples listed on this page.`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens)
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
}
log.RegisterSecret(stepConfig.Username)
log.RegisterSecret(stepConfig.Password)
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID}
log.RegisterHook(logCollector)
}
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)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Send(&telemetryData, logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.Dsn,
GeneralConfig.HookConfig.SplunkConfig.Token,
GeneralConfig.HookConfig.SplunkConfig.Index,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
}
abapEnvironmentRunAUnitTest(stepConfig, &telemetryData)
telemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addAbapEnvironmentRunAUnitTestFlags(createAbapEnvironmentRunAUnitTestCmd, &stepConfig)
return createAbapEnvironmentRunAUnitTestCmd
}
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.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.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Parameter of ServiceInstance Name to delete Cloud Foundry Service")
cmd.Flags().StringVar(&stepConfig.CfServiceKeyName, "cfServiceKeyName", os.Getenv("PIPER_cfServiceKeyName"), "Parameter of Cloud Foundry Service Key to be created")
cmd.Flags().StringVar(&stepConfig.CfSpace, "cfSpace", os.Getenv("PIPER_cfSpace"), "Cloud Foundry Space")
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0735")
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0735")
cmd.Flags().StringVar(&stepConfig.Host, "host", os.Getenv("PIPER_host"), "Specifies the host address of the SAP BTP ABAP Environment system")
cmd.Flags().StringVar(&stepConfig.AUnitResultsFileName, "aUnitResultsFileName", `AUnitResults.xml`, "Specifies output file name for the results from the AUnit run.")
cmd.MarkFlagRequired("aUnitConfig")
cmd.MarkFlagRequired("username")
cmd.MarkFlagRequired("password")
}
// retrieve step metadata
func abapEnvironmentRunAUnitTestMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "abapEnvironmentRunAUnitTest",
Aliases: []config.Alias{},
Description: "Runs an AUnit Test",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Secrets: []config.StepSecrets{
{Name: "abapCredentialsId", Description: "Jenkins credentials ID containing user and password to authenticate to the BTP ABAP Environment system or the Cloud Foundry API", Type: "jenkins", Aliases: []config.Alias{{Name: "cfCredentialsId", Deprecated: false}}},
},
Parameters: []config.StepParameters{
{
Name: "aUnitConfig",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_aUnitConfig"),
},
{
Name: "cfApiEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/apiEndpoint"}},
Default: os.Getenv("PIPER_cfApiEndpoint"),
},
{
Name: "cfOrg",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/org"}},
Default: os.Getenv("PIPER_cfOrg"),
},
{
Name: "cfServiceInstance",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/serviceInstance"}},
Default: os.Getenv("PIPER_cfServiceInstance"),
},
{
Name: "cfServiceKeyName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/serviceKey"}, {Name: "cloudFoundry/serviceKeyName"}, {Name: "cfServiceKey"}},
Default: os.Getenv("PIPER_cfServiceKeyName"),
},
{
Name: "cfSpace",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/space"}},
Default: os.Getenv("PIPER_cfSpace"),
},
{
Name: "username",
ResourceRef: []config.ResourceReference{
{
Name: "abapCredentialsId",
Param: "username",
Type: "secret",
},
},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_username"),
},
{
Name: "password",
ResourceRef: []config.ResourceReference{
{
Name: "abapCredentialsId",
Param: "password",
Type: "secret",
},
},
Scope: []string{"PARAMETERS", "STAGES", "STEPS2"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_password"),
},
{
Name: "host",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_host"),
},
{
Name: "aUnitResultsFileName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: `AUnitResults.xml`,
},
},
},
Containers: []config.Container{
{Name: "cf", Image: "ppiper/cf-cli:7"},
},
},
}
return theMetaData
}

View File

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

View File

@ -0,0 +1,437 @@
package cmd
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
type abapEnvironmentRunAUnitTestMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func newAbapEnvironmentRunAUnitTestTestsUtils() abapEnvironmentRunAUnitTestMockUtils {
utils := abapEnvironmentRunAUnitTestMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
return utils
}
func TestBuildAUnitTestBody(t *testing.T) {
t.Parallel()
t.Run("Test AUnit test run body with no data", func(t *testing.T) {
t.Parallel()
expectedmetadataString := ""
expectedoptionsString := ""
expectedobjectSetString := ""
var err error
var 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", func(t *testing.T) {
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>`
expectedobjectSetString := `<osl:objectSet xsi:type="testSet" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><osl:set xsi:type="testSet"><osl:package name="TestPackage" includeSubpackages="false"/><osl:package name="TestObject" includeSubpackages="CLAS"/></osl:set></osl:objectSet>`
var err error
var config AUnitConfig
config = AUnitConfig{
Title: "Test Title",
Context: "Test Context",
Options: AUnitOptions{
Measurements: "none",
Scope: Scope{
OwnTests: new(bool),
ForeignTests: new(bool),
},
RiskLevel: RiskLevel{
Harmless: new(bool),
Dangerous: new(bool),
Critical: new(bool),
},
Duration: Duration{
Short: new(bool),
Medium: new(bool),
Long: new(bool),
},
},
ObjectSet: []ObjectSet{{
Type: "testSet",
Set: []Set{{
Type: "testSet",
PackageSet: []AUnitPackage{{
Name: "TestPackage",
IncludeSubpackages: new(bool),
}},
FlatObjectSet: []AUnitObject{{
Name: "TestObject",
Type: "CLAS",
}},
}},
}},
}
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, nil, err)
//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 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 fail: no Context", func(t *testing.T) {
t.Parallel()
expectedmetadataString := ""
expectedoptionsString := ""
expectedobjectSetString := ""
var err error
var config AUnitConfig
config = AUnitConfig{Title: "Test"}
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 context for the AUnit run has been provided. Please configure an appropriate context for the respective test run")
})
t.Run("Test AUnit test run body with example yaml config fail: no Options", func(t *testing.T) {
t.Parallel()
expectedmetadataString := ""
expectedoptionsString := ""
expectedobjectSetString := ""
var err error
var config AUnitConfig
config = AUnitConfig{Title: "Test", Context: "Test"}
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 options have been provided. Please configure the options for the respective test run")
})
t.Run("Test AUnit test run body with example yaml config fail: no ObjectSet", func(t *testing.T) {
t.Parallel()
expectedmetadataString := ""
expectedoptionsString := ""
expectedobjectSetString := ""
var err error
var config AUnitConfig
config = AUnitConfig{Title: "Test", Context: "Test", Options: AUnitOptions{Measurements: "Test"}}
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 object set config. No object set has been provided. Please configure the set of objects you want to be checked for the respective test run")
})
}
func TestParseAUnitResult(t *testing.T) {
t.Parallel()
t.Run("succes case: test parsing example XML result", func(t *testing.T) {
t.Parallel()
dir, err := ioutil.TempDir("", "test get result AUnit test run")
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)
}()
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)
err = parseAUnitResult(body, "AUnitResults.xml")
assert.Equal(t, nil, err)
})
t.Run("succes case: test parsing empty AUnit run XML result", func(t *testing.T) {
t.Parallel()
dir, err := ioutil.TempDir("", "test get result AUnit test run")
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)
}()
bodyString := `<?xml version="1.0" encoding="UTF-8"?>`
body := []byte(bodyString)
err = parseAUnitResult(body, "AUnitResults.xml")
assert.Equal(t, nil, err)
})
t.Run("failure case: parsing empty xml", func(t *testing.T) {
t.Parallel()
var bodyString string
body := []byte(bodyString)
err := parseAUnitResult(body, "AUnitResults.xml")
assert.EqualError(t, err, "Parsing AUnit result failed: Body is empty, can't parse empty body")
})
}
func TestGetResultAUnitRun(t *testing.T) {
t.Parallel()
t.Run("Get HTTP Response from AUnit test run Test", func(t *testing.T) {
t.Parallel()
client := &abaputils.ClientMock{
Body: `AUnit test result body`,
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
resp, err := getResultAUnitRun("GET", con, []byte(client.Body), client)
defer resp.Body.Close()
if assert.Equal(t, nil, err) {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
newStr := buf.String()
assert.Equal(t, "AUnit test result body", newStr)
assert.Equal(t, int64(0), resp.ContentLength)
assert.Equal(t, []string([]string(nil)), resp.Header["X-Crsf-Token"])
}
})
t.Run("Get HTTP Response from AUnit test run Test Failure", func(t *testing.T) {
t.Parallel()
client := &abaputils.ClientMock{
Body: `AUnit test result body`,
BodyList: []string{},
StatusCode: 400,
Error: fmt.Errorf("%w", errors.New("Test fail")),
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
resp, err := getResultAUnitRun("GET", con, []byte(client.Body), client)
defer resp.Body.Close()
if assert.EqualError(t, err, "Getting AUnit run results failed: Test fail") {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
newStr := buf.String()
assert.Equal(t, "AUnit test result body", newStr)
assert.Equal(t, int64(0), resp.ContentLength)
assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, []string([]string(nil)), resp.Header["X-Crsf-Token"])
}
})
}
func TestRunAbapEnvironmentRunAUnitTest(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
t.Parallel()
/*
t.Parallel()
// init
config := abapEnvironmentRunAUnitTestOptions{}
utils := newAbapEnvironmentRunAUnitTestTestsUtils()
utils.AddFile("file.txt", []byte("dummy content"))
// test
err := runAbapEnvironmentRunAUnitTest(&config, nil, nil, utils)
// assert
assert.NoError(t, err)
*/
})
t.Run("FetchXcsrfToken Test", func(t *testing.T) {
t.Parallel()
tokenExpected := "myToken"
client := &abaputils.ClientMock{
Body: `Xcsrf Token test`,
Token: tokenExpected,
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
token, error := fetchAUnitXcsrfToken("GET", con, []byte(client.Body), client)
if assert.Equal(t, nil, error) {
assert.Equal(t, tokenExpected, token)
}
})
t.Run("failure case: fetch token", func(t *testing.T) {
t.Parallel()
tokenExpected := ""
client := &abaputils.ClientMock{
Body: `Xcsrf Token test`,
Token: "",
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
token, error := fetchAUnitXcsrfToken("GET", con, []byte(client.Body), client)
if assert.Equal(t, nil, error) {
assert.Equal(t, tokenExpected, token)
}
})
t.Run("AUnit test run Poll Test", func(t *testing.T) {
t.Parallel()
tokenExpected := "myToken"
client := &abaputils.ClientMock{
Body: `<?xml version="1.0" encoding="utf-8"?><aunit:run xmlns:aunit="http://www.sap.com/adt/api/aunit"><aunit:progress status="FINISHED"/><aunit:time/><atom:link href="/sap/bc/adt/api/abapunit/results/test" rel="http://www.sap.com/adt/relations/api/abapunit/run-result" type="application/vnd.sap.adt.api.junit.run-result.v1+xml" title="JUnit Run Result" xmlns:atom="http://www.w3.org/2005/Atom"/></aunit:run>`,
Token: tokenExpected,
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
resp, err := pollAUnitRun(con, []byte(client.Body), client)
if assert.Equal(t, nil, err) {
assert.Equal(t, "/sap/bc/adt/api/abapunit/results/test", resp)
}
})
t.Run("AUnit test run Poll Test Fail", func(t *testing.T) {
t.Parallel()
tokenExpected := "myToken"
client := &abaputils.ClientMock{
Body: `<?xml version="1.0" encoding="utf-8"?><aunit:run xmlns:aunit="http://www.sap.com/adt/api/aunit"><aunit:progress status="Not Created"/><aunit:time/><atom:link href="/sap/bc/adt/api/abapunit/results/test" rel="http://www.sap.com/adt/relations/api/abapunit/run-result" type="application/vnd.sap.adt.api.junit.run-result.v1+xml" title="JUnit Run Result" xmlns:atom="http://www.w3.org/2005/Atom"/></aunit:run>`,
Token: tokenExpected,
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
resp, err := pollAUnitRun(con, []byte(client.Body), client)
if assert.Equal(t, nil, err) {
assert.Equal(t, "", resp)
}
})
t.Run("Get HTTP Response from AUnit test run Test", func(t *testing.T) {
t.Parallel()
client := &abaputils.ClientMock{
Body: `HTTP response test`,
}
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
}
fmt.Println("Body:" + string([]byte(client.Body)))
resp, err := getHTTPResponseAUnitRun("GET", con, []byte(client.Body), client)
defer resp.Body.Close()
if assert.Equal(t, nil, err) {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
newStr := buf.String()
assert.Equal(t, "HTTP response test", newStr)
assert.Equal(t, int64(0), resp.ContentLength)
assert.Equal(t, []string([]string(nil)), resp.Header["X-Crsf-Token"])
}
})
}

View File

@ -21,6 +21,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"abapEnvironmentCreateSystem": abapEnvironmentCreateSystemMetadata(),
"abapEnvironmentPullGitRepo": abapEnvironmentPullGitRepoMetadata(),
"abapEnvironmentRunATCCheck": abapEnvironmentRunATCCheckMetadata(),
"abapEnvironmentRunAUnitTest": abapEnvironmentRunAUnitTestMetadata(),
"batsExecuteTests": batsExecuteTestsMetadata(),
"checkChangeInDevelopment": checkChangeInDevelopmentMetadata(),
"checkmarxExecuteScan": checkmarxExecuteScanMetadata(),

View File

@ -163,6 +163,7 @@ func Execute() {
rootCmd.AddCommand(WritePipelineEnv())
rootCmd.AddCommand(ReadPipelineEnv())
rootCmd.AddCommand(InfluxWriteDataCommand())
rootCmd.AddCommand(AbapEnvironmentRunAUnitTestCommand())
rootCmd.AddCommand(CheckStepActiveCommand())
addRootFlags(rootCmd)

View File

@ -0,0 +1,144 @@
# ${docGenStepName}
## ${docGenDescription}
## Prerequisites
* A SAP BTP, ABAP environment system is available. On this system, a [Communication User](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/0377adea0401467f939827242c1f4014.html), a [Communication System](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/1bfe32ae08074b7186e375ab425fb114.html) and a [Communication Arrangement](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/a0771f6765f54e1c8193ad8582a32edb.html) is setup for the Communication Scenario “SAP BTP, ABAP Environment - Software Component Test Integration (SAP_COM_0735)“. This can be done manually through the respective applications on the SAP BTP, ABAP environment system or through creating a Service Key for the system on Cloud Foundry with the parameters {“scenario_id”: “SAP_COM_0735", “type”: “basic”}. In a pipeline, you can do this with the step [cloudFoundryCreateServiceKey](https://sap.github.io/jenkins-library/steps/cloudFoundryCreateServiceKey/).
* You can either provide the ABAP endpoint configuration to directly trigger an AUnit run on the ABAP system or optionally provide the Cloud Foundry parameters with your credentials to read a Service Key of a SAP BTP, ABAP environment system in Cloud Foundry that contains all the details of the ABAP endpoint to trigger an AUnit run.
* Regardless if you chose an ABAP endpoint directly or reading a Cloud Foundry Service Key, you have to provide the object set containing the objects you want to be checked in an AUnit run in a .yml or .yaml file. This file must be stored in the same folder as the Jenkinsfile defining the pipeline.
* Make sure that the objects contained in the object set are present in the configured system in order to run the check. Please make sure that you have created or pulled the respective software components and/or packages including the test classes and objects in the SAP BTP, ABAP environment system, that should be checked.
## ${docGenParameters}
## ${docGenConfiguration}
## ${docJenkinsPluginDependencies}
## Examples
### AUnit test run via direct ABAP endpoint configuration in Jenkinsfile
This example triggers an AUnit test run directly on the ABAP endpoint.
In order to trigger the AUnit test run you have to pass the username and password for authentication to the ABAP endpoint via parameters as well as the ABAP endpoint/host. You can store the credentials in Jenkins and use the abapCredentialsId parameter to authenticate to the ABAP endpoint/host.
This must be configured as following:
```groovy
abapEnvironmentRunAUnitTest(
abapCredentialsId: 'abapCredentialsId',
host: 'https://myABAPendpoint.com',
aUnitConfig: 'aUnitConfig.yml',
script: this,
)
```
To trigger the AUnit test run an AUnit config file `aUnitConfig.yml` will be needed. Check the section 'AUnit config file example' for more information.
### AUnit test run via Cloud Foundry Service Key example in Jenkinsfile
The following example triggers an AUnit test run via reading the Service Key of an ABAP instance in Cloud Foundry.
You can store the credentials in Jenkins and use the cfCredentialsId parameter to authenticate to Cloud Foundry.
The username and password to authenticate to ABAP system will then be read from the Cloud Foundry Service Key that is bound to the ABAP instance.
This can be done accordingly:
```groovy
abapEnvironmentRunAUnitTest(
cfApiEndpoint : 'https://test.server.com',
cfOrg : 'cfOrg',
cfSpace: 'cfSpace',
cfServiceInstance: 'myServiceInstance',
cfServiceKeyName: 'myServiceKey',
abapCredentialsId: 'cfCredentialsId',
aUnitConfig: 'aUnitConfig.yml',
script: this,
)
```
To trigger the AUnit test run an AUnit config file `aUnitConfig.yml` will be needed. Check the section 'AUnit config file example' for more information.
### AUnit test run via direct ABAP endpoint configuration in Jenkinsfile
This example triggers an AUnit run directly on the ABAP endpoint.
In order to trigger the AUnit run you have to pass the username and password for authentication to the ABAP endpoint via parameters as well as the ABAP endpoint/host. You can store the credentials in Jenkins and use the abapCredentialsId parameter to authenticate to the ABAP endpoint/host.
This must be configured as following:
```groovy
abapEnvironmentRunAUnitTest(
abapCredentialsId: 'abapCredentialsId',
host: 'https://myABAPendpoint.com',
aUnitConfig: 'aUnitConfig.yml',
script: this,
)
```
To trigger the AUnit run an AUnit config file `aUnitConfig.yml` will be needed. Check section 'AUnit config file example' for more information.
### AUnit config file example
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.
You can specify an object set containing the objects that should be checked. These can be for example packages, classes or transport requests containing test classes that can be executed. This must be in the same format as below example for a `aUnitConfig.yml` file.
Note that if you specify a package inside a packageSet to be checked for each package that has to be checked you can configure if you want the subpackages to be included in checks or not.
See below example for an `aUnitConfig.yml` file containing a package to be checked:
```yaml
title: My AUnit run
context: My unit tests
options:
measurements: none
scope:
owntests: true
foreigntests: true
riskLevel:
harmless: true
dangerous: true
critical: true
duration:
short: true
medium: true
long: true
objectset:
- type: unionSet
set:
- type: packageSet
package:
- name: my_package
includesubpackages: false
```
The following example of an `aUnitConfig.yml` file containing one class and one interface to be checked:
```yaml
title: My AUnit run
context: My unit tests
options:
measurements: none
scope:
owntests: true
foreigntests: true
riskLevel:
harmless: true
dangerous: true
critical: true
duration:
short: true
medium: true
long: true
objectset:
- type: unionSet
set:
- type: flatObjectSet
object:
- name: my_class
type: CLAS
- name: my_interface
type: INTF
```

View File

@ -0,0 +1,133 @@
metadata:
name: abapEnvironmentRunAUnitTest
description: Runs an AUnit Test
longDescription: |
This step is for triggering an [AUnit](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/cdd19e3a5c49458291ec65d8d86e2b9a.html) test run on an SAP BTP ABAP Environment system.
Please provide either of the following options:
* The host and credentials of the BTP ABAP Environment system itself. The credentials must be configured for the Communication Scenario SAP_COM_0735.
* The Cloud Foundry parameters (API endpoint, organization, space), credentials, the service instance for the ABAP service and the service key for the Communication Scenario SAP_COM_0735.
* Only provide one of those options with the respective credentials. If all values are provided, the direct communication (via host) has priority.
Regardless of the option you chose, please make sure to provide the object set containing the objects that you want to be checked analog to the examples listed on this page.
spec:
inputs:
secrets:
- name: abapCredentialsId
aliases:
- name: cfCredentialsId
description: Jenkins credentials ID containing user and password to authenticate to the BTP ABAP Environment system or the Cloud Foundry API
type: jenkins
params:
- name: aUnitConfig
type: string
description: Path to a YAML configuration file for the object set to be checked during the AUnit test run
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
- name: cfApiEndpoint
type: string
description: Cloud Foundry API endpoint
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
aliases:
- name: cloudFoundry/apiEndpoint
- name: cfOrg
type: string
description: Cloud Foundry org
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
aliases:
- name: cloudFoundry/org
- name: cfServiceInstance
type: string
description: Parameter of ServiceInstance Name to delete Cloud Foundry Service
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
aliases:
- name: cloudFoundry/serviceInstance
- name: cfServiceKeyName
type: string
description: Parameter of Cloud Foundry Service Key to be created
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
aliases:
- name: cloudFoundry/serviceKey
- name: cloudFoundry/serviceKeyName
- name: cfServiceKey
- name: cfSpace
type: string
description: Cloud Foundry Space
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
aliases:
- name: cloudFoundry/space
- name: username
type: string
description: User for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0735
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
secret: true
resourceRef:
- name: abapCredentialsId
type: secret
param: username
- name: password
type: string
description: Password for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0735
scope:
- PARAMETERS
- STAGES
- STEPS2
mandatory: true
secret: true
resourceRef:
- name: abapCredentialsId
type: secret
param: password
- name: host
type: string
description: Specifies the host address of the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: false
- name: aUnitResultsFileName
type: string
description: Specifies output file name for the results from the AUnit run.
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: false
default: "AUnitResults.xml"
containers:
- name: cf
image: ppiper/cf-cli:7

View File

@ -120,6 +120,7 @@ public class CommonStepsTest extends BasePiperTest{
'abapEnvironmentPullGitRepo', //implementing new golang pattern without fields
'abapEnvironmentPipeline', // special step (infrastructure)
'abapEnvironmentRunATCCheck', //implementing new golang pattern without fields
'abapEnvironmentRunAUnitTest', //implementing new golang pattern without fields
'abapEnvironmentCreateSystem', //implementing new golang pattern without fields
'artifactPrepareVersion',
'cloudFoundryCreateService', //implementing new golang pattern without fields

View File

@ -0,0 +1,11 @@
import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/abapEnvironmentRunAUnitTest.yaml'
void call(Map parameters = [:]) {
List credentials = [
[type: 'usernamePassword', id: 'abapCredentialsId', env: ['PIPER_username', 'PIPER_password']]
]
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials, true, false, true)
}