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  }