github.com/kubeshop/testkube@v1.17.23/contrib/executor/postman/pkg/runner/newman/newman.go (about)

     1  package newman
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/kubeshop/testkube/pkg/api/v1/testkube"
    14  	"github.com/kubeshop/testkube/pkg/envs"
    15  	"github.com/kubeshop/testkube/pkg/executor"
    16  	"github.com/kubeshop/testkube/pkg/executor/agent"
    17  	"github.com/kubeshop/testkube/pkg/executor/content"
    18  	"github.com/kubeshop/testkube/pkg/executor/env"
    19  	"github.com/kubeshop/testkube/pkg/executor/output"
    20  	"github.com/kubeshop/testkube/pkg/executor/runner"
    21  	"github.com/kubeshop/testkube/pkg/executor/scraper"
    22  	"github.com/kubeshop/testkube/pkg/executor/scraper/factory"
    23  	"github.com/kubeshop/testkube/pkg/tmp"
    24  	"github.com/kubeshop/testkube/pkg/ui"
    25  )
    26  
    27  func NewNewmanRunner(ctx context.Context, params envs.Params) (*NewmanRunner, error) {
    28  	output.PrintLog(fmt.Sprintf("%s Preparing test runner", ui.IconTruck))
    29  
    30  	var err error
    31  	r := &NewmanRunner{
    32  		Params: params,
    33  	}
    34  
    35  	r.Scraper, err = factory.TryGetScrapper(ctx, params)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	return r, nil
    41  }
    42  
    43  // NewmanRunner struct for newman based runner
    44  type NewmanRunner struct {
    45  	Params  envs.Params
    46  	Scraper scraper.Scraper
    47  }
    48  
    49  var _ runner.Runner = &NewmanRunner{}
    50  
    51  // Run runs particular test content on top of newman binary
    52  func (r *NewmanRunner) Run(ctx context.Context, execution testkube.Execution) (result testkube.ExecutionResult, err error) {
    53  	if r.Scraper != nil {
    54  		defer r.Scraper.Close()
    55  	}
    56  
    57  	output.PrintLog(fmt.Sprintf("%s Preparing for test run", ui.IconTruck))
    58  
    59  	path, workingDir, err := content.GetPathAndWorkingDir(execution.Content, r.Params.DataDir)
    60  	if err != nil {
    61  		output.PrintLogf("%s Failed to resolve absolute directory for %s, using the path directly", ui.IconWarning, r.Params.DataDir)
    62  	}
    63  
    64  	fileInfo, err := os.Stat(path)
    65  	if err != nil {
    66  		return result, err
    67  	}
    68  
    69  	if fileInfo.IsDir() {
    70  		scriptName := execution.Args[len(execution.Args)-1]
    71  		if workingDir != "" {
    72  			path = ""
    73  			if execution.Content != nil && execution.Content.Repository != nil {
    74  				scriptName = filepath.Join(execution.Content.Repository.Path, scriptName)
    75  			}
    76  		}
    77  
    78  		execution.Args = execution.Args[:len(execution.Args)-1]
    79  		output.PrintLogf("%s It is a directory test - trying to find file from the last executor argument %s in directory %s", ui.IconWorld, scriptName, path)
    80  
    81  		// sanity checking for test script
    82  		scriptFile := filepath.Join(path, workingDir, scriptName)
    83  		fileInfo, errFile := os.Stat(scriptFile)
    84  		if errors.Is(errFile, os.ErrNotExist) || fileInfo.IsDir() {
    85  			output.PrintLogf("%s Could not find file %s in the directory, error: %s", ui.IconCross, scriptName, errFile)
    86  			return *result.Err(errors.Errorf("could not find file %s in the directory: %v", scriptName, errFile)), nil
    87  		}
    88  		path = scriptFile
    89  	}
    90  
    91  	envManager := env.NewManagerWithVars(execution.Variables)
    92  	envManager.GetReferenceVars(envManager.Variables)
    93  
    94  	variablesFileContent := execution.VariablesFile
    95  	if execution.IsVariablesFileUploaded {
    96  		b, err := os.ReadFile(filepath.Join(content.UploadsFolder, execution.VariablesFile))
    97  		if err != nil {
    98  			return result, fmt.Errorf("could not read uploaded variables file: %w", err)
    99  		}
   100  		variablesFileContent = string(b)
   101  	}
   102  
   103  	// write params to tmp file
   104  	envReader, err := NewEnvFileReader(envManager.Variables, variablesFileContent, envManager.GetSecretEnvs())
   105  	if err != nil {
   106  		return result, err
   107  	}
   108  	envpath, err := tmp.ReaderToTmpfile(envReader)
   109  	if err != nil {
   110  		return result, err
   111  	}
   112  
   113  	tmpName := tmp.Name() + ".json"
   114  	args := execution.Args
   115  	hasJunit := false
   116  	hasReport := false
   117  	for i := range args {
   118  		if args[i] == "<envFile>" {
   119  			args[i] = envpath
   120  		}
   121  
   122  		if args[i] == "<reportFile>" {
   123  			args[i] = tmpName
   124  			hasReport = true
   125  		}
   126  
   127  		if args[i] == "<runPath>" {
   128  			args[i] = path
   129  		}
   130  
   131  		if args[i] == "--reporter-json-export" {
   132  			hasJunit = true
   133  		}
   134  
   135  		args[i] = os.ExpandEnv(args[i])
   136  	}
   137  
   138  	runPath := ""
   139  	if workingDir != "" {
   140  		runPath = workingDir
   141  	}
   142  	// we'll get error here in case of failed test too so we treat this as
   143  	// starter test execution with failed status
   144  	command, args := executor.MergeCommandAndArgs(execution.Command, args)
   145  	output.PrintLogf("%s Test run command %s %s", ui.IconRocket, command, strings.Join(envManager.ObfuscateStringSlice(args), " "))
   146  	out, err := executor.Run(runPath, command, envManager, args...)
   147  
   148  	out = envManager.ObfuscateSecrets(out)
   149  
   150  	var nerr error
   151  	if hasJunit && hasReport {
   152  		var newmanResult NewmanExecutionResult
   153  		// try to get json result even if process returned error (could be invalid test)
   154  		newmanResult, nerr = r.GetNewmanResult(tmpName, out)
   155  		if nerr != nil {
   156  			output.PrintLog(fmt.Sprintf("%s Could not get Newman result: %s", ui.IconCross, nerr.Error()))
   157  		} else {
   158  			output.PrintLog(fmt.Sprintf("%s Got Newman result successfully", ui.IconCheckMark))
   159  		}
   160  		// convert newman result to OpenAPI struct
   161  		result = MapMetadataToResult(newmanResult)
   162  		output.PrintLog(fmt.Sprintf("%s Mapped Newman result successfully", ui.IconCheckMark))
   163  	} else {
   164  		result = makeSuccessExecution(out)
   165  	}
   166  
   167  	var rerr error
   168  	if execution.PostRunScript != "" && execution.ExecutePostRunScriptBeforeScraping {
   169  		output.PrintLog(fmt.Sprintf("%s Running post run script...", ui.IconCheckMark))
   170  
   171  		if rerr = agent.RunScript(execution.PostRunScript, r.Params.WorkingDir); rerr != nil {
   172  			output.PrintLogf("%s Failed to execute post run script %s", ui.IconWarning, rerr)
   173  		}
   174  	}
   175  
   176  	// scrape artifacts first even if there are errors above
   177  	if r.Params.ScrapperEnabled && execution.ArtifactRequest != nil && len(execution.ArtifactRequest.Dirs) != 0 {
   178  		output.PrintLogf("Scraping directories: %v with masks: %v", execution.ArtifactRequest.Dirs, execution.ArtifactRequest.Masks)
   179  
   180  		if err := r.Scraper.Scrape(ctx, execution.ArtifactRequest.Dirs, execution.ArtifactRequest.Masks, execution); err != nil {
   181  			return *result.WithErrors(err), nil
   182  		}
   183  	}
   184  
   185  	// catch errors if any
   186  	if err != nil {
   187  		return *result.Err(err), nil
   188  	}
   189  
   190  	if nerr != nil {
   191  		return *result.Err(nerr), nil
   192  	}
   193  
   194  	if rerr != nil {
   195  		return *result.Err(rerr), nil
   196  	}
   197  
   198  	return result, nil
   199  }
   200  
   201  func (r *NewmanRunner) GetNewmanResult(tmpName string, out []byte) (newmanResult NewmanExecutionResult, err error) {
   202  	newmanResult.Output = string(out)
   203  
   204  	// parse JSON output of newman test
   205  	bytes, err := os.ReadFile(tmpName)
   206  	if err != nil {
   207  		return newmanResult, err
   208  	}
   209  
   210  	err = json.Unmarshal(bytes, &newmanResult.Metadata)
   211  	if err != nil {
   212  		return newmanResult, err
   213  	}
   214  
   215  	return
   216  }
   217  
   218  // GetType returns runner type
   219  func (r NewmanRunner) GetType() runner.Type {
   220  	return runner.TypeMain
   221  }