github.com/kubeshop/testkube@v1.17.23/pkg/mapper/cdevents/mapper.go (about) 1 package cdevents 2 3 import ( 4 "errors" 5 "fmt" 6 "path/filepath" 7 "strings" 8 9 cdevents "github.com/cdevents/sdk-go/pkg/api" 10 11 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 12 ) 13 14 // MapTestkubeEventToCDEvent maps OpenAPI spec Event to CDEvent CDEventReader 15 func MapTestkubeEventToCDEvent(tkEvent testkube.Event, clusterID, defaultNamespace, dashboardURI string) (cdevents.CDEventReader, error) { 16 if tkEvent.Type_ == nil { 17 return nil, errors.New("empty event type") 18 } 19 20 switch *tkEvent.Type_ { 21 case *testkube.EventStartTest: 22 return MapTestkubeEventStartTestToCDEvent(tkEvent, clusterID, defaultNamespace, dashboardURI) 23 case *testkube.EventEndTestAborted, *testkube.EventEndTestFailed, *testkube.EventEndTestTimeout, *testkube.EventEndTestSuccess: 24 return MapTestkubeEventFinishTestToCDEvent(tkEvent, clusterID, defaultNamespace, dashboardURI) 25 case *testkube.EventStartTestSuite: 26 return MapTestkubeEventStartTestSuiteToCDEvent(tkEvent, clusterID, dashboardURI) 27 case *testkube.EventEndTestSuiteAborted, *testkube.EventEndTestSuiteFailed, *testkube.EventEndTestSuiteTimeout, *testkube.EventEndTestSuiteSuccess: 28 return MapTestkubeEventFinishTestSuiteToCDEvent(tkEvent, clusterID, dashboardURI) 29 } 30 31 return nil, fmt.Errorf("not supported event type %s", tkEvent.Type_) 32 } 33 34 // MapTestkubeEventQueuedTestToCDEvent maps OpenAPI spec Queued Test Event to CDEvent CDEventReader 35 func MapTestkubeEventQueuedTestToCDEvent(event testkube.Event, clusterID, defaultNamespace, dashboardURI string) (cdevents.CDEventReader, error) { 36 // Create the base event 37 ev, err := cdevents.NewTestCaseRunQueuedEvent() 38 if err != nil { 39 return nil, err 40 } 41 42 if event.TestExecution != nil { 43 ev.SetSubjectId(event.TestExecution.Id) 44 } 45 46 ev.SetSubjectSource(clusterID) 47 ev.SetSource(clusterID) 48 if event.TestExecution != nil { 49 ev.SetSubjectTestCase(&cdevents.TestCaseRunQueuedSubjectContentTestCase{ 50 Id: event.TestExecution.TestName, 51 Type: MapTestkubeTestTypeToCDEventTestCaseType(event.TestExecution.TestType), 52 Uri: fmt.Sprintf("%s/tests/%s", dashboardURI, event.TestExecution.TestName), 53 }) 54 55 namespace := event.TestExecution.TestNamespace 56 if namespace == "" { 57 namespace = defaultNamespace 58 } 59 60 ev.SetSubjectEnvironment(&cdevents.Reference{ 61 Id: namespace, 62 Source: clusterID, 63 }) 64 65 if event.TestExecution.RunningContext != nil { 66 ev.SetSubjectTrigger(&cdevents.TestCaseRunQueuedSubjectContentTrigger{ 67 Type: MapTestkubeRunningContextTypeToCDEventTiggerType(event.TestExecution.RunningContext.Type_), 68 }) 69 } 70 71 if event.TestExecution.TestSuiteName != "" { 72 ev.SetSubjectTestSuiteRun(&cdevents.Reference{ 73 Id: event.TestExecution.TestSuiteName, 74 Source: clusterID, 75 }) 76 } 77 } 78 79 return ev, nil 80 } 81 82 // MapTestkubeEventStartTestToCDEvent maps OpenAPI spec Start Test Event to CDEvent CDEventReader 83 func MapTestkubeEventStartTestToCDEvent(event testkube.Event, clusterID, defaultNamespace, dashboardURI string) (cdevents.CDEventReader, error) { 84 // Create the base event 85 ev, err := cdevents.NewTestCaseRunStartedEvent() 86 if err != nil { 87 return nil, err 88 } 89 90 if event.TestExecution != nil { 91 ev.SetSubjectId(event.TestExecution.Id) 92 } 93 94 ev.SetSubjectSource(clusterID) 95 ev.SetSource(clusterID) 96 if event.TestExecution != nil { 97 ev.SetSubjectTestCase(&cdevents.TestCaseRunStartedSubjectContentTestCase{ 98 Id: event.TestExecution.TestName, 99 Type: MapTestkubeTestTypeToCDEventTestCaseType(event.TestExecution.TestType), 100 Uri: fmt.Sprintf("%s/tests/%s", dashboardURI, event.TestExecution.TestName), 101 }) 102 103 namespace := event.TestExecution.TestNamespace 104 if namespace == "" { 105 namespace = defaultNamespace 106 } 107 108 ev.SetSubjectEnvironment(&cdevents.Reference{ 109 Id: namespace, 110 Source: clusterID, 111 }) 112 113 if event.TestExecution.RunningContext != nil { 114 ev.SetSubjectTrigger(&cdevents.TestCaseRunStartedSubjectContentTrigger{ 115 Type: MapTestkubeRunningContextTypeToCDEventTiggerType(event.TestExecution.RunningContext.Type_), 116 }) 117 } 118 119 if event.TestExecution.TestSuiteName != "" { 120 ev.SetSubjectTestSuiteRun(&cdevents.Reference{ 121 Id: event.TestExecution.TestSuiteName, 122 Source: clusterID, 123 }) 124 } 125 } 126 127 return ev, nil 128 } 129 130 // MapTestkubeEventFinishTestToCDEvent maps OpenAPI spec Failed, Aborted, Timeout, Success Test Event to CDEvent CDEventReader 131 func MapTestkubeEventFinishTestToCDEvent(event testkube.Event, clusterID, defaultNamespace, dashboardURI string) (cdevents.CDEventReader, error) { 132 // Create the base event 133 ev, err := cdevents.NewTestCaseRunFinishedEvent() 134 if err != nil { 135 return nil, err 136 } 137 138 if event.TestExecution != nil { 139 ev.SetSubjectId(event.TestExecution.Id) 140 } 141 142 ev.SetSubjectSource(clusterID) 143 ev.SetSource(clusterID) 144 if event.TestExecution != nil { 145 ev.SetSubjectTestCase(&cdevents.TestCaseRunFinishedSubjectContentTestCase{ 146 Id: event.TestExecution.TestName, 147 Type: MapTestkubeTestTypeToCDEventTestCaseType(event.TestExecution.TestType), 148 Uri: fmt.Sprintf("%s/tests/%s", dashboardURI, event.TestExecution.TestName), 149 }) 150 151 namespace := event.TestExecution.TestNamespace 152 if namespace == "" { 153 namespace = defaultNamespace 154 } 155 156 ev.SetSubjectEnvironment(&cdevents.Reference{ 157 Id: namespace, 158 Source: clusterID, 159 }) 160 161 if event.TestExecution.IsAborted() || event.TestExecution.IsTimeout() { 162 ev.SetSubjectOutcome("cancel") 163 if event.TestExecution.ExecutionResult != nil { 164 ev.SetSubjectReason(event.TestExecution.ExecutionResult.ErrorMessage) 165 } 166 } 167 168 if event.TestExecution.IsFailed() { 169 ev.SetSubjectOutcome("fail") 170 if event.TestExecution.ExecutionResult != nil { 171 ev.SetSubjectReason(event.TestExecution.ExecutionResult.ErrorMessage) 172 } 173 } 174 175 if event.TestExecution.IsPassed() { 176 ev.SetSubjectOutcome("pass") 177 } 178 179 if event.TestExecution.TestSuiteName != "" { 180 ev.SetSubjectTestSuiteRun(&cdevents.Reference{ 181 Id: event.TestExecution.TestSuiteName, 182 Source: clusterID, 183 }) 184 } 185 } 186 187 return ev, nil 188 } 189 190 // MapTestkubeArtifactToCDEvent maps OpenAPI spec Artifact to CDEvent CDEventReader 191 func MapTestkubeArtifactToCDEvent(execution *testkube.Execution, clusterID, path, format, dashboardURI string) (cdevents.CDEventReader, error) { 192 // Create the base event 193 ev, err := cdevents.NewTestOutputPublishedEvent() 194 if err != nil { 195 return nil, err 196 } 197 198 ev.SetSubjectId(filepath.Join(execution.Name, path)) 199 ev.SetSubjectSource(clusterID) 200 ev.SetSource(clusterID) 201 ev.SetSubjectTestCaseRun(&cdevents.Reference{ 202 Id: execution.Id, 203 Source: clusterID, 204 }) 205 206 ev.SetSubjectFormat(format) 207 ev.SetSubjectOutputType(MapMimeTypeToCDEventOutputType(format)) 208 ev.SetSubjectUri(fmt.Sprintf("%s/tests/executions/%s/execution/%s", dashboardURI, execution.TestName, execution.Id)) 209 210 return ev, nil 211 } 212 213 // MapTestkubeLogToCDEvent maps OpenAPI spec log to CDEvent CDEventReader 214 func MapTestkubeLogToCDEvent(event testkube.Event, clusterID, dashboardURI string) (cdevents.CDEventReader, error) { 215 // Create the base event 216 ev, err := cdevents.NewTestOutputPublishedEvent() 217 if err != nil { 218 return nil, err 219 } 220 221 if event.TestExecution != nil { 222 ev.SetSubjectId(event.TestExecution.Id + "-log") 223 } 224 225 ev.SetSubjectSource(clusterID) 226 ev.SetSource(clusterID) 227 228 if event.TestExecution != nil { 229 ev.SetSubjectTestCaseRun(&cdevents.Reference{ 230 Id: event.TestExecution.Id, 231 Source: clusterID, 232 }) 233 } 234 235 ev.SetSubjectFormat("text/x-uri") 236 ev.SetSubjectOutputType("log") 237 if event.TestExecution != nil { 238 ev.SetSubjectUri(fmt.Sprintf("%s/tests/%s/executions/%s/log-output", dashboardURI, 239 event.TestExecution.TestName, event.TestExecution.Id)) 240 } 241 242 return ev, nil 243 } 244 245 // MapTestkubeEventQueuedTestSuiteToCDEvent maps OpenAPI spec Queued Test Suite Event to CDEvent CDEventReader 246 func MapTestkubeEventQueuedTestSuiteToCDEvent(event testkube.Event, clusterID, dashboardURI string) (cdevents.CDEventReader, error) { 247 // Create the base event 248 ev, err := cdevents.NewTestSuiteRunQueuedEvent() 249 if err != nil { 250 return nil, err 251 } 252 253 if event.TestSuiteExecution != nil { 254 ev.SetSubjectId(event.TestSuiteExecution.Id) 255 } 256 257 ev.SetSubjectSource(clusterID) 258 ev.SetSource(clusterID) 259 if event.TestSuiteExecution != nil { 260 if event.TestSuiteExecution.TestSuite != nil { 261 ev.SetSubjectTestSuite(&cdevents.TestSuiteRunQueuedSubjectContentTestSuite{ 262 Id: event.TestSuiteExecution.TestSuite.Name, 263 Url: fmt.Sprintf("%s/test-suites/executions/%s", dashboardURI, event.TestSuiteExecution.TestSuite.Name), 264 }) 265 266 ev.SetSubjectEnvironment(&cdevents.Reference{ 267 Id: event.TestSuiteExecution.TestSuite.Namespace, 268 Source: clusterID, 269 }) 270 } 271 272 if event.TestSuiteExecution.RunningContext != nil { 273 ev.SetSubjectTrigger(&cdevents.TestSuiteRunQueuedSubjectContentTrigger{ 274 Type: MapTestkubeRunningContextTypeToCDEventTiggerType(event.TestSuiteExecution.RunningContext.Type_), 275 }) 276 } 277 } 278 279 return ev, nil 280 } 281 282 // MapTestkubeEventStartTestSuiteToCDEvent maps OpenAPI spec Start Test Suite Event to CDEvent CDEventReader 283 func MapTestkubeEventStartTestSuiteToCDEvent(event testkube.Event, clusterID, dashboardURI string) (cdevents.CDEventReader, error) { 284 // Create the base event 285 ev, err := cdevents.NewTestSuiteRunStartedEvent() 286 if err != nil { 287 return nil, err 288 } 289 290 if event.TestSuiteExecution != nil { 291 ev.SetSubjectId(event.TestSuiteExecution.Id) 292 } 293 294 ev.SetSubjectSource(clusterID) 295 ev.SetSource(clusterID) 296 if event.TestSuiteExecution != nil { 297 if event.TestSuiteExecution.TestSuite != nil { 298 ev.SetSubjectTestSuite(&cdevents.TestSuiteRunStartedSubjectContentTestSuite{ 299 Id: event.TestSuiteExecution.TestSuite.Name, 300 Uri: fmt.Sprintf("%s/test-suites/%s", dashboardURI, event.TestSuiteExecution.TestSuite.Name), 301 }) 302 303 ev.SetSubjectEnvironment(&cdevents.Reference{ 304 Id: event.TestSuiteExecution.TestSuite.Namespace, 305 Source: clusterID, 306 }) 307 } 308 309 if event.TestSuiteExecution.RunningContext != nil { 310 ev.SetSubjectTrigger(&cdevents.TestSuiteRunStartedSubjectContentTrigger{ 311 Type: MapTestkubeRunningContextTypeToCDEventTiggerType(event.TestSuiteExecution.RunningContext.Type_), 312 }) 313 } 314 } 315 316 return ev, nil 317 } 318 319 // MapTestkubeEventFinishTestSuiteToCDEvent maps OpenAPI spec Failed, Aborted, Timeout, Success Test Event to CDEvent CDEventReader 320 func MapTestkubeEventFinishTestSuiteToCDEvent(event testkube.Event, clusterID, dashboardURI string) (cdevents.CDEventReader, error) { 321 // Create the base event 322 ev, err := cdevents.NewTestSuiteRunFinishedEvent() 323 if err != nil { 324 return nil, err 325 } 326 327 if event.TestSuiteExecution != nil { 328 ev.SetSubjectId(event.TestSuiteExecution.Id) 329 } 330 331 ev.SetSubjectSource(clusterID) 332 ev.SetSource(clusterID) 333 if event.TestSuiteExecution != nil { 334 if event.TestSuiteExecution.TestSuite != nil { 335 ev.SetSubjectTestSuite(&cdevents.TestSuiteRunFinishedSubjectContentTestSuite{ 336 Id: event.TestSuiteExecution.TestSuite.Name, 337 Uri: fmt.Sprintf("%s/test-suites/%s", dashboardURI, event.TestSuiteExecution.TestSuite.Name), 338 }) 339 340 ev.SetSubjectEnvironment(&cdevents.Reference{ 341 Id: event.TestSuiteExecution.TestSuite.Namespace, 342 Source: clusterID, 343 }) 344 } 345 346 if event.TestSuiteExecution.IsAborted() || event.TestSuiteExecution.IsTimeout() { 347 ev.SetSubjectOutcome("cancel") 348 var errs []string 349 for _, result := range event.TestSuiteExecution.StepResults { 350 if result.Execution != nil && result.Execution.ExecutionResult != nil { 351 errs = append(errs, result.Execution.ExecutionResult.ErrorMessage) 352 } 353 } 354 355 for _, batch := range event.TestSuiteExecution.ExecuteStepResults { 356 for _, result := range batch.Execute { 357 if result.Execution != nil && result.Execution.ExecutionResult != nil { 358 errs = append(errs, result.Execution.ExecutionResult.ErrorMessage) 359 } 360 } 361 } 362 363 ev.SetSubjectReason(strings.Join(errs, ",")) 364 } 365 366 if event.TestSuiteExecution.IsFailed() { 367 ev.SetSubjectOutcome("fail") 368 var errs []string 369 for _, result := range event.TestSuiteExecution.StepResults { 370 if result.Execution != nil && result.Execution.ExecutionResult != nil { 371 errs = append(errs, result.Execution.ExecutionResult.ErrorMessage) 372 } 373 } 374 375 for _, batch := range event.TestSuiteExecution.ExecuteStepResults { 376 for _, result := range batch.Execute { 377 if result.Execution != nil && result.Execution.ExecutionResult != nil { 378 errs = append(errs, result.Execution.ExecutionResult.ErrorMessage) 379 } 380 } 381 } 382 383 ev.SetSubjectReason(strings.Join(errs, ",")) 384 } 385 386 if event.TestSuiteExecution.IsPassed() { 387 ev.SetSubjectOutcome("pass") 388 } 389 } 390 391 return ev, nil 392 } 393 394 // MapTestkubeRunningContextTypeToCDEventTiggerType maps OpenAPI spec Running Context Type to CDEvent Trigger Type 395 func MapTestkubeRunningContextTypeToCDEventTiggerType(contextType string) string { 396 switch testkube.RunningContextType(contextType) { 397 case testkube.RunningContextTypeUserCLI, testkube.RunningContextTypeUserUI: 398 return "manual" 399 case testkube.RunningContextTypeTestTrigger, testkube.RunningContextTypeTestSuite: 400 return "event" 401 case testkube.RunningContextTypeScheduler: 402 return "schedule" 403 } 404 405 return "other" 406 } 407 408 // MapTestkubeTestTypeToCDEventTestCaseType maps OpenAPI spec Test Type to CDEvent Test Case Type 409 func MapTestkubeTestTypeToCDEventTestCaseType(testType string) string { 410 var types = map[string]string{ 411 "artillery/": "performance", 412 "curl/": "functional", 413 "cypress/": "functional", 414 "ginkgo/": "unit", 415 "gradle/": "integration", 416 "jmeter/": "performance", 417 "k6/": "performance", 418 "kubepug/": "compliance", 419 "maven/": "integration", 420 "playwright/": "functional", 421 "postman/": "functional", 422 "soapui/": "functional", 423 "zap/": "security", 424 } 425 426 for key, value := range types { 427 if strings.Contains(testType, key) { 428 return value 429 } 430 } 431 432 return "other" 433 } 434 435 // MapMimeTypeToCDEventOutputType maps mime type to CDEvent Output Type 436 func MapMimeTypeToCDEventOutputType(mimeType string) string { 437 if strings.Contains(mimeType, "video/") || strings.Contains(mimeType, "audio/") { 438 return "video" 439 } 440 441 if strings.Contains(mimeType, "image/") { 442 return "image" 443 } 444 445 if strings.Contains(mimeType, "text/") { 446 return "report" 447 } 448 449 var types = map[string]string{ 450 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "report", 451 "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "report", 452 "application/vnd.openxmlformats-officedocument.presentationml.presentation": "report", 453 "application/vnd.oasis.opendocument.text": "report", 454 "application/vnd.oasis.opendocument.spreadsheet": "report", 455 "application/vnd.oasis.opendocument.presentation": "report", 456 "application/pdf": "report", 457 "application/vnd.ms-excel": "report", 458 "application/vnd.ms-powerpoint": "report", 459 "application/msword": "report", 460 "application/json": "log", 461 } 462 463 for key, value := range types { 464 if mimeType == key { 465 return value 466 } 467 } 468 469 return "other" 470 }