github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/integration-service/status-reporting-to-pullrequest.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/devfile/library/v2/pkg/util"
    10  	"github.com/google/go-github/v44/github"
    11  	"github.com/redhat-appstudio/e2e-tests/pkg/clients/has"
    12  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    13  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    14  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    15  
    16  	integrationv1beta1 "github.com/konflux-ci/integration-service/api/v1beta1"
    17  	. "github.com/onsi/ginkgo/v2"
    18  	. "github.com/onsi/gomega"
    19  	appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1"
    20  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    21  )
    22  
    23  var _ = framework.IntegrationServiceSuiteDescribe("Status Reporting of Integration tests", Label("integration-service", "HACBS", "status-reporting"), func() {
    24  	defer GinkgoRecover()
    25  
    26  	var f *framework.Framework
    27  	var err error
    28  
    29  	var prNumber int
    30  	var timeout, interval time.Duration
    31  	var prHeadSha string
    32  	var snapshot *appstudioApi.Snapshot
    33  	var component *appstudioApi.Component
    34  	var pipelineRun, testPipelinerun *pipeline.PipelineRun
    35  	var integrationTestScenarioPass, integrationTestScenarioFail *integrationv1beta1.IntegrationTestScenario
    36  	var applicationName, componentName, componentBaseBranchName, pacBranchName, testNamespace string
    37  
    38  	AfterEach(framework.ReportFailure(&f))
    39  
    40  	Describe("with status reporting of Integration tests in CheckRuns", Ordered, func() {
    41  		BeforeAll(func() {
    42  			if os.Getenv(constants.SKIP_PAC_TESTS_ENV) == "true" {
    43  				Skip("Skipping this test due to configuration issue with Spray proxy")
    44  			}
    45  
    46  			f, err = framework.NewFramework(utils.GetGeneratedNamespace("stat-rep"))
    47  			Expect(err).NotTo(HaveOccurred())
    48  			testNamespace = f.UserNamespace
    49  
    50  			if utils.IsPrivateHostname(f.OpenshiftConsoleHost) {
    51  				Skip("Using private cluster (not reachable from Github), skipping...")
    52  			}
    53  
    54  			applicationName = createApp(*f, testNamespace)
    55  
    56  			integrationTestScenarioPass, err = f.AsKubeAdmin.IntegrationController.CreateIntegrationTestScenario("", applicationName, testNamespace, gitURL, revision, pathInRepoPass)
    57  			Expect(err).ShouldNot(HaveOccurred())
    58  
    59  			integrationTestScenarioFail, err = f.AsKubeAdmin.IntegrationController.CreateIntegrationTestScenario("", applicationName, testNamespace, gitURL, revision, pathInRepoFail)
    60  			Expect(err).ShouldNot(HaveOccurred())
    61  
    62  			componentName = fmt.Sprintf("%s-%s", "test-component-pac", util.GenerateRandomString(6))
    63  			pacBranchName = constants.PaCPullRequestBranchPrefix + componentName
    64  			componentBaseBranchName = fmt.Sprintf("base-%s", util.GenerateRandomString(6))
    65  
    66  			err = f.AsKubeAdmin.CommonController.Github.CreateRef(componentRepoNameForStatusReporting, componentDefaultBranch, componentRevision, componentBaseBranchName)
    67  			Expect(err).ShouldNot(HaveOccurred())
    68  		})
    69  
    70  		AfterAll(func() {
    71  			if !CurrentSpecReport().Failed() {
    72  				cleanup(*f, testNamespace, applicationName, componentName)
    73  			}
    74  
    75  			// Delete new branches created by PaC and a testing branch used as a component's base branch
    76  			err = f.AsKubeAdmin.CommonController.Github.DeleteRef(componentRepoNameForStatusReporting, pacBranchName)
    77  			if err != nil {
    78  				Expect(err.Error()).To(ContainSubstring(referenceDoesntExist))
    79  			}
    80  			err = f.AsKubeAdmin.CommonController.Github.DeleteRef(componentRepoNameForStatusReporting, componentBaseBranchName)
    81  			if err != nil {
    82  				Expect(err.Error()).To(ContainSubstring(referenceDoesntExist))
    83  			}
    84  		})
    85  
    86  		When("a new Component with specified custom branch is created", Label("custom-branch"), func() {
    87  			BeforeAll(func() {
    88  				componentObj := appstudioApi.ComponentSpec{
    89  					ComponentName: componentName,
    90  					Application:   applicationName,
    91  					Source: appstudioApi.ComponentSource{
    92  						ComponentSourceUnion: appstudioApi.ComponentSourceUnion{
    93  							GitSource: &appstudioApi.GitSource{
    94  								URL:      componentGitSourceURLForStatusReporting,
    95  								Revision: componentBaseBranchName,
    96  							},
    97  						},
    98  					},
    99  				}
   100  				// Create a component with Git Source URL, a specified git branch
   101  				component, err = f.AsKubeAdmin.HasController.CreateComponent(componentObj, testNamespace, "", "", applicationName, false, utils.MergeMaps(constants.ComponentPaCRequestAnnotation, constants.ImageControllerAnnotationRequestPublicRepo))
   102  				Expect(err).ShouldNot(HaveOccurred())
   103  			})
   104  			It("triggers a Build PipelineRun", func() {
   105  				timeout = time.Second * 600
   106  				interval = time.Second * 1
   107  				Eventually(func() error {
   108  					pipelineRun, err = f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "")
   109  					if err != nil {
   110  						GinkgoWriter.Printf("Build PipelineRun has not been created yet for the component %s/%s\n", testNamespace, componentName)
   111  						return err
   112  					}
   113  					if !pipelineRun.HasStarted() {
   114  						return fmt.Errorf("build pipelinerun %s/%s hasn't started yet", pipelineRun.GetNamespace(), pipelineRun.GetName())
   115  					}
   116  					return nil
   117  				}, timeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for the build PipelineRun to start for the component %s/%s", testNamespace, componentName))
   118  			})
   119  			It("does not contain an annotation with a Snapshot Name", func() {
   120  				Expect(pipelineRun.Annotations[snapshotAnnotation]).To(Equal(""))
   121  			})
   122  			It("should lead to build PipelineRun finishing successfully", func() {
   123  				Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component,
   124  					"", f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, pipelineRun)).To(Succeed())
   125  			})
   126  			It("should have a related PaC init PR created", func() {
   127  				timeout = time.Second * 300
   128  				interval = time.Second * 1
   129  
   130  				Eventually(func() bool {
   131  					prs, err := f.AsKubeAdmin.CommonController.Github.ListPullRequests(componentRepoNameForStatusReporting)
   132  					Expect(err).ShouldNot(HaveOccurred())
   133  
   134  					for _, pr := range prs {
   135  						if pr.Head.GetRef() == pacBranchName {
   136  							prNumber = pr.GetNumber()
   137  							prHeadSha = pr.Head.GetSHA()
   138  							return true
   139  						}
   140  					}
   141  					return false
   142  				}, timeout, interval).Should(BeTrue(), fmt.Sprintf("timed out when waiting for init PaC PR (branch name '%s') to be created in %s repository", pacBranchName, componentRepoNameForStatusReporting))
   143  
   144  				// in case the first pipelineRun attempt has failed and was retried, we need to update the value of pipelineRun variable
   145  				pipelineRun, err = f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, prHeadSha)
   146  				Expect(err).ShouldNot(HaveOccurred())
   147  			})
   148  
   149  			It("eventually leads to the build PipelineRun's status reported at Checks tab", func() {
   150  				expectedCheckRunName := fmt.Sprintf("%s-%s", componentName, "on-pull-request")
   151  				validateCheckRun(*f, expectedCheckRunName, checkrunConclusionSuccess, componentRepoNameForStatusReporting, prHeadSha, prNumber)
   152  			})
   153  		})
   154  
   155  		When("the PaC build pipelineRun run succeeded", func() {
   156  			It("checks if the BuildPipelineRun have the annotation of chains signed", func() {
   157  				Expect(f.AsKubeDeveloper.IntegrationController.WaitForBuildPipelineRunToGetAnnotated(testNamespace, applicationName, componentName, chainsSignedAnnotation)).To(Succeed())
   158  			})
   159  
   160  			It("checks if the Snapshot is created", func() {
   161  				snapshot, err = f.AsKubeDeveloper.IntegrationController.WaitForSnapshotToGetCreated("", "", componentName, testNamespace)
   162  				Expect(err).ToNot(HaveOccurred())
   163  			})
   164  
   165  			It("checks if the Build PipelineRun got annotated with Snapshot name", func() {
   166  				Expect(f.AsKubeDeveloper.IntegrationController.WaitForBuildPipelineRunToGetAnnotated(testNamespace, applicationName, componentName, snapshotAnnotation)).To(Succeed())
   167  			})
   168  		})
   169  
   170  		When("the Snapshot was created", func() {
   171  			It("should find both the related Integration PipelineRuns", func() {
   172  				testPipelinerun, err = f.AsKubeDeveloper.IntegrationController.WaitForIntegrationPipelineToGetStarted(integrationTestScenarioPass.Name, snapshot.Name, testNamespace)
   173  				Expect(err).ToNot(HaveOccurred())
   174  				Expect(testPipelinerun.Labels[snapshotAnnotation]).To(ContainSubstring(snapshot.Name))
   175  				Expect(testPipelinerun.Labels[scenarioAnnotation]).To(ContainSubstring(integrationTestScenarioPass.Name))
   176  
   177  				testPipelinerun, err = f.AsKubeDeveloper.IntegrationController.WaitForIntegrationPipelineToGetStarted(integrationTestScenarioFail.Name, snapshot.Name, testNamespace)
   178  				Expect(err).ToNot(HaveOccurred())
   179  				Expect(testPipelinerun.Labels[snapshotAnnotation]).To(ContainSubstring(snapshot.Name))
   180  				Expect(testPipelinerun.Labels[scenarioAnnotation]).To(ContainSubstring(integrationTestScenarioFail.Name))
   181  			})
   182  		})
   183  
   184  		When("Integration PipelineRuns are created", func() {
   185  			It("should eventually complete successfully", func() {
   186  				Expect(f.AsKubeAdmin.IntegrationController.WaitForIntegrationPipelineToBeFinished(integrationTestScenarioPass, snapshot, testNamespace)).To(Succeed(), fmt.Sprintf("Error when waiting for an integration pipelinerun for snapshot %s/%s to finish", testNamespace, snapshot.GetName()))
   187  				Expect(f.AsKubeAdmin.IntegrationController.WaitForIntegrationPipelineToBeFinished(integrationTestScenarioFail, snapshot, testNamespace)).To(Succeed(), fmt.Sprintf("Error when waiting for an integration pipelinerun for snapshot %s/%s to finish", testNamespace, snapshot.GetName()))
   188  			})
   189  		})
   190  
   191  		When("Integration PipelineRuns completes successfully", func() {
   192  			It("should lead to Snapshot CR being marked as failed", FlakeAttempts(3), func() {
   193  				// Snapshot marked as Failed because one of its Integration test failed (as expected)
   194  				Eventually(func() bool {
   195  					snapshot, err = f.AsKubeAdmin.IntegrationController.GetSnapshot("", pipelineRun.Name, "", testNamespace)
   196  					return err == nil && !f.AsKubeAdmin.CommonController.HaveTestsSucceeded(snapshot)
   197  				}, time.Minute*3, time.Second*5).Should(BeTrue(), fmt.Sprintf("Timed out waiting for Snapshot to be marked as failed %s/%s", snapshot.GetNamespace(), snapshot.GetName()))
   198  			})
   199  			It("eventually leads to the status reported at Checks tab for the successful Integration PipelineRun", func() {
   200  				validateCheckRun(*f, integrationTestScenarioPass.Name, checkrunConclusionSuccess, componentRepoNameForStatusReporting, prHeadSha, prNumber)
   201  			})
   202  			It("eventually leads to the status reported at Checks tab for the failed Integration PipelineRun", func() {
   203  				validateCheckRun(*f, integrationTestScenarioFail.Name, checkrunConclusionFailure, componentRepoNameForStatusReporting, prHeadSha, prNumber)
   204  			})
   205  		})
   206  	})
   207  })
   208  
   209  // validateCheckRun fetches a specific CheckRun within a given repo
   210  // by matching the CheckRun's name with the given checkRunName, and
   211  // then validates that it got completed and its conclusion is as expected
   212  func validateCheckRun(f framework.Framework, checkRunName, conclusion, repoName, prHeadSha string, prNumber int) {
   213  	var checkRun *github.CheckRun
   214  	var timeout, interval time.Duration
   215  	var err error
   216  
   217  	timeout = time.Minute * 10
   218  	interval = time.Second * 2
   219  
   220  	Eventually(func() *github.CheckRun {
   221  		checkRuns, err := f.AsKubeAdmin.CommonController.Github.ListCheckRuns(repoName, prHeadSha)
   222  		Expect(err).ShouldNot(HaveOccurred())
   223  		for _, cr := range checkRuns {
   224  			if strings.Contains(cr.GetName(), checkRunName) {
   225  				checkRun = cr
   226  				return cr
   227  			}
   228  		}
   229  		return nil
   230  	}, timeout, interval).ShouldNot(BeNil(), fmt.Sprintf("timed out when waiting for the PaC CheckRun, with `Name` field containing the substring %s, to appear in the PR #%d of the Component repo %s", checkRunName, prNumber, repoName))
   231  
   232  	Eventually(func() string {
   233  		checkRun, err = f.AsKubeAdmin.CommonController.Github.GetCheckRun(repoName, checkRun.GetID())
   234  		Expect(err).ShouldNot(HaveOccurred())
   235  		return checkRun.GetStatus()
   236  	}, timeout, interval).Should(Equal(checkrunStatusCompleted), fmt.Sprintf("timed out when waiting for the PaC Check suite status to be 'completed' in the Component repo %s in PR #%d", repoName, prNumber))
   237  	Expect(checkRun.GetConclusion()).To(Equal(conclusion), fmt.Sprintf("the PR %d in %s repo doesn't contain the expected conclusion (%s) of the CheckRun", prNumber, repoName, conclusion))
   238  }