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 }