dev/testsreporter/testsreporter.go (166 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package testsreporter
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"github.com/elastic/integrations/dev/codeowners"
)
type CheckOptions struct {
Serverless bool
ServerlessProject string
LogsDB bool
StackVersion string
Subscription string
BuildURL string
CodeownersPath string
MaxPreviousLinks int
MaxTestsReported int
DryRun bool
Verbose bool
}
func Check(ctx context.Context, resultsPath string, options CheckOptions) error {
if options.CodeownersPath == "" {
// set default value for the GitHub CODEOWNERS file
options.CodeownersPath = codeowners.DefaultCodeownersPath
}
if options.DryRun {
fmt.Println("DRY_RUN mode enabled")
}
fmt.Println("path: ", resultsPath)
packageErrors, err := errorsFromTests(resultsPath, options)
if err != nil {
return err
}
ghCli := newGhCli(githubOptions{
DryRun: options.DryRun,
})
aReporter := newReporter(reporterOptions{
GhCli: ghCli,
MaxPreviousLinks: options.MaxPreviousLinks,
Verbose: options.Verbose,
})
if len(packageErrors) > options.MaxTestsReported {
fmt.Printf("Skip creating GitHub issues, hit the maximum number (%d) of tests to be reported. Total failing tests: %d.\n", options.MaxTestsReported, len(packageErrors))
packages, err := packagesFromTests(resultsPath)
if err != nil {
return fmt.Errorf("failed to get packages from results files: %w", err)
}
bError, err := newBuildError(buildErrorOptions{
Serverless: options.Serverless,
ServerlessProject: options.ServerlessProject,
LogsDB: options.LogsDB,
StackVersion: options.StackVersion,
Subscription: options.Subscription,
BuildURL: options.BuildURL,
Packages: packages,
})
if err != nil {
return fmt.Errorf("failed to create the build information error: %w", err)
}
ghIssue, err := createInitialIssue(bError, options.MaxPreviousLinks)
if err != nil {
return fmt.Errorf("failed to create initial issue: %w", err)
}
if err := aReporter.Report(ctx, ghIssue, bError); err != nil {
return err
}
return nil
}
var multiErr error
for _, pError := range packageErrors {
ghIssue, err := createInitialIssue(pError, options.MaxPreviousLinks)
if err != nil {
return fmt.Errorf("failed to create initial issue: %w", err)
}
if err := aReporter.Report(ctx, ghIssue, pError); err != nil {
multiErr = errors.Join(multiErr, err)
}
}
return multiErr
}
func errorsFromTests(resultsPath string, options CheckOptions) ([]*packageError, error) {
var packageErrors []*packageError
err := filepath.Walk(resultsPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) != ".xml" {
return nil
}
if info.IsDir() {
return nil
}
cases, err := testFailures(path)
if err != nil {
return err
}
for _, c := range cases {
packageError, err := newPackageError(packageErrorOptions{
Serverless: options.Serverless,
ServerlessProject: options.ServerlessProject,
LogsDB: options.LogsDB,
StackVersion: options.StackVersion,
Subscription: options.Subscription,
BuildURL: options.BuildURL,
TestCase: c,
CodeownersPath: options.CodeownersPath,
})
if err != nil {
return fmt.Errorf("failed to create package error: %w", err)
}
packageErrors = append(packageErrors, packageError)
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to look for errors: %w", err)
}
return packageErrors, nil
}
// packagesFromTests returns the sorted packages failing given the results file
func packagesFromTests(resultsPath string) ([]string, error) {
packages := []string{}
err := filepath.Walk(resultsPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) != ".xml" {
return nil
}
if info.IsDir() {
return nil
}
cases, err := testFailures(path)
if err != nil {
return err
}
if len(cases) > 0 {
name := cases[0].PackageName()
packages = append(packages, name)
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to look for errors: %w", err)
}
sort.Strings(packages)
return packages, nil
}
func createInitialIssue(resultError failureObserver, maxPreviousLinks int) (*githubIssue, error) {
r := resultsFormatter{
result: resultError,
maxPreviousLinks: maxPreviousLinks,
}
description, err := r.Description()
if err != nil {
return nil, fmt.Errorf("failed to render initial description: %w", err)
}
issue := newGithubIssue(githubIssueOptions{
Title: r.Title(),
Description: description,
Labels: []string{"flaky-test", "automation"},
Repository: "elastic/integrations",
})
return issue, nil
}