diff --git a/internal/controller/snapshot/snapshot_adapter.go b/internal/controller/snapshot/snapshot_adapter.go index 8f8c89d0d..4eeda2f1b 100644 --- a/internal/controller/snapshot/snapshot_adapter.go +++ b/internal/controller/snapshot/snapshot_adapter.go @@ -90,73 +90,125 @@ func scenariosNamesToList(integrationTestScenarios *[]v1beta2.IntegrationTestSce return &result } -// EnsureRerunPipelineRunsExist is responsible for recreating integration test pipelines triggered by users +// EnsureRerunPipelineRunsExist is responsible for recreating integration test pipelineruns triggered by users func (a *Adapter) EnsureRerunPipelineRunsExist() (controller.OperationResult, error) { - - scenarioName, ok := gitops.GetIntegrationTestRunLabelValue(a.snapshot) + runLabelValue, ok := gitops.GetIntegrationTestRunLabelValue(a.snapshot) if !ok { - // no test rerun triggered return controller.ContinueProcessing() } - integrationTestScenario, err := a.loader.GetScenario(a.context, a.client, scenarioName, a.application.Namespace) + integrationTestScenarios, opResult, err := a.getScenariosToRerun(runLabelValue) + if integrationTestScenarios == nil { + return opResult, err + } + + testStatuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(a.snapshot) + if err != nil { + return controller.RequeueWithError(err) + } + + skipScenarioRerunCount, opResult, err := a.handleScenarioReruns(integrationTestScenarios, testStatuses) + if skipScenarioRerunCount == -1 { + return opResult, err + } + + if err = a.cleanupRerunLabelAndUpdateStatus(skipScenarioRerunCount, len(*integrationTestScenarios), runLabelValue, testStatuses); err != nil { + return controller.RequeueWithError(err) + } + + return controller.ContinueProcessing() +} + +// getScenariosToRerun fetches and filters the IntegrationTestScenarios based on runLabelValue. +func (a *Adapter) getScenariosToRerun(runLabelValue string) (*[]v1beta2.IntegrationTestScenario, controller.OperationResult, error) { + if runLabelValue == "all" { + scenarios, err := a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application) + if err != nil { + a.logger.Error(err, "Failed to get IntegrationTestScenarios", "Application.Namespace", a.application.Namespace) + opResult, err := controller.RequeueWithError(err) + return nil, opResult, err + } + filteredScenarios := gitops.FilterIntegrationTestScenariosWithContext(scenarios, a.snapshot) + if filteredScenarios == nil { + a.logger.Info("None of the Scenarios' context are applicable to the Snapshot, nothing to re-run") + } + return filteredScenarios, controller.OperationResult{}, nil + } + + scenario, err := a.loader.GetScenario(a.context, a.client, runLabelValue, a.application.Namespace) if err != nil { if clienterrors.IsNotFound(err) { - a.logger.Error(err, "scenario for integration test re-run not found", "scenario", scenarioName) - // scenario doesn't exist just remove label and continue + a.logger.Error(err, "IntegrationTestScenario not found", "Scenario", runLabelValue) if err = gitops.RemoveIntegrationTestRerunLabel(a.context, a.client, a.snapshot); err != nil { - return controller.RequeueWithError(err) + opResult, err := controller.RequeueWithError(err) + return nil, opResult, err } - return controller.ContinueProcessing() + opResult, _ := controller.ContinueProcessing() + return nil, opResult, nil } - return controller.RequeueWithError(fmt.Errorf("failed to fetch requested scenario %s: %w", scenarioName, err)) + opResult, err := controller.RequeueWithError(fmt.Errorf("failed to fetch scenario %s: %w", runLabelValue, err)) + return nil, opResult, err } + return &[]v1beta2.IntegrationTestScenario{*scenario}, controller.OperationResult{}, nil +} - a.logger.Info("Re-running integration test for scenario", "scenario", scenarioName) - - testStatuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(a.snapshot) - if err != nil { - return controller.RequeueWithError(err) - } +// handleScenarioReruns iterates through scenarios, rerunning tests as needed and updating test statuses. +func (a *Adapter) handleScenarioReruns(scenarios *[]v1beta2.IntegrationTestScenario, testStatuses *intgteststat.SnapshotIntegrationTestStatuses) (int, controller.OperationResult, error) { + skipScenarioRerunCount := 0 + for _, scenario := range *scenarios { + scenario := scenario + status, found := testStatuses.GetScenarioStatus(scenario.Name) + if found && (status.Status == intgteststat.IntegrationTestStatusInProgress || status.Status == intgteststat.IntegrationTestStatusPending) { + a.logger.Info("Skipping re-run for IntegrationTestScenario since it's in 'InProgress' or 'Pending' state", "Scenario", scenario.Name) + skipScenarioRerunCount++ + continue + } - integrationTestScenarioStatus, ok := testStatuses.GetScenarioStatus(integrationTestScenario.Name) - if ok && (integrationTestScenarioStatus.Status == intgteststat.IntegrationTestStatusInProgress || - integrationTestScenarioStatus.Status == intgteststat.IntegrationTestStatusPending) { - a.logger.Info(fmt.Sprintf("Found existing test in %s status, skipping re-run", integrationTestScenarioStatus.Status), - "integrationTestScenario.Name", integrationTestScenario.Name) - if err = gitops.RemoveIntegrationTestRerunLabel(a.context, a.client, a.snapshot); err != nil { - return controller.RequeueWithError(err) + testStatuses.ResetStatus(scenario.Name) + if opResult, err := a.rerunIntegrationPipelinerunForScenario(&scenario, testStatuses); opResult.CancelRequest || err != nil { + a.logger.Error(err, "Failed to create rerun pipelinerun for IntegrationTestScenario", "Scenario", scenario.Name) + return -1, opResult, err } - return controller.ContinueProcessing() } - testStatuses.ResetStatus(scenarioName) + return skipScenarioRerunCount, controller.OperationResult{}, nil +} - pipelineRun, err := a.createIntegrationPipelineRun(a.application, integrationTestScenario, a.snapshot) +// rerunIntegrationPipelinerunForScenario creates a pipelinerun for the given scenario and updates its status. +func (a *Adapter) rerunIntegrationPipelinerunForScenario(scenario *v1beta2.IntegrationTestScenario, testStatuses *intgteststat.SnapshotIntegrationTestStatuses) (controller.OperationResult, error) { + pipelineRun, err := a.createIntegrationPipelineRun(a.application, scenario, a.snapshot) if err != nil { - return a.HandlePipelineCreationError(err, integrationTestScenario, testStatuses) + return a.HandlePipelineCreationError(err, scenario, testStatuses) } - testStatuses.UpdateTestStatusIfChanged( - integrationTestScenario.Name, intgteststat.IntegrationTestStatusInProgress, - fmt.Sprintf("IntegrationTestScenario pipeline '%s' has been created", pipelineRun.Name)) - if err = testStatuses.UpdateTestPipelineRunName(integrationTestScenario.Name, pipelineRun.Name); err != nil { + + testStatuses.UpdateTestStatusIfChanged(scenario.Name, intgteststat.IntegrationTestStatusInProgress, fmt.Sprintf("PipelineRun '%s' created", pipelineRun.Name)) + if err := testStatuses.UpdateTestPipelineRunName(scenario.Name, pipelineRun.Name); err != nil { // it doesn't make sense to restart reconciliation here, it will be eventually updated by integrationpipeline adapter - a.logger.Error(err, "Failed to update pipelinerun name in test status") + a.logger.Error(err, "Failed to update pipeline run name in test status") } + return controller.OperationResult{}, nil +} - if err = gitops.WriteIntegrationTestStatusesIntoSnapshot(a.context, a.snapshot, testStatuses, a.client); err != nil { - return controller.RequeueWithError(err) +// cleanupRerunLabelAndUpdateStatus removes rerun labels and updates snapshot status. +func (a *Adapter) cleanupRerunLabelAndUpdateStatus(skipCount, totalScenarios int, runLabelValue string, testStatuses *intgteststat.SnapshotIntegrationTestStatuses) error { + if err := gitops.RemoveIntegrationTestRerunLabel(a.context, a.client, a.snapshot); err != nil { + return err } - if err = gitops.ResetSnapshotStatusConditions(a.context, a.client, a.snapshot, "Integration test is being rerun for snapshot"); err != nil { - a.logger.Error(err, "Failed to reset snapshot status conditions") - return controller.RequeueWithError(err) + if skipCount == totalScenarios { + a.logger.Info(fmt.Sprintf("%[1]d out of %[1]d requested IntegrationTestScenario(s) are either in 'InProgress' or 'Pending' state, skipping their re-runs", totalScenarios), "Label", runLabelValue) + return nil } - if err = gitops.RemoveIntegrationTestRerunLabel(a.context, a.client, a.snapshot); err != nil { - return controller.RequeueWithError(err) + if err := gitops.WriteIntegrationTestStatusesIntoSnapshot(a.context, a.snapshot, testStatuses, a.client); err != nil { + return err } - return controller.ContinueProcessing() + if err := gitops.ResetSnapshotStatusConditions(a.context, a.client, a.snapshot, "Integration test re-run initiated for Snapshot"); err != nil { + a.logger.Error(err, "Failed to reset snapshot status conditions") + return err + } + + return nil } // EnsureIntegrationPipelineRunsExist is an operation that will ensure that all Integration pipeline runs diff --git a/internal/controller/snapshot/snapshot_adapter_test.go b/internal/controller/snapshot/snapshot_adapter_test.go index eac9522ba..f292da165 100644 --- a/internal/controller/snapshot/snapshot_adapter_test.go +++ b/internal/controller/snapshot/snapshot_adapter_test.go @@ -77,6 +77,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() { hasComSnapshot2 *applicationapiv1alpha1.Snapshot hasComSnapshot3 *applicationapiv1alpha1.Snapshot integrationTestScenario *v1beta2.IntegrationTestScenario + integrationTestScenario1 *v1beta2.IntegrationTestScenario integrationTestScenarioForInvalidSnapshot *v1beta2.IntegrationTestScenario buildPipelineRun1 *tektonv1.PipelineRun ) @@ -145,6 +146,43 @@ var _ = Describe("Snapshot Adapter", Ordered, func() { Expect(k8sClient.Create(ctx, integrationTestScenario)).Should(Succeed()) helpers.SetScenarioIntegrationStatusAsValid(integrationTestScenario, "valid") + integrationTestScenario1 = &v1beta2.IntegrationTestScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-pass-1", + Namespace: "default", + + Labels: map[string]string{ + "test.appstudio.openshift.io/optional": "false", + }, + + Annotations: map[string]string{ + "test.appstudio.openshift.io/kind": "kind", + }, + }, + Spec: v1beta2.IntegrationTestScenarioSpec{ + Application: "application-sample", + ResolverRef: v1beta2.ResolverRef{ + Resolver: "git", + Params: []v1beta2.ResolverParameter{ + { + Name: "url", + Value: "https://github.com/redhat-appstudio/integration-examples.git", + }, + { + Name: "revision", + Value: sourceRepoRef, + }, + { + Name: "pathInRepo", + Value: "pipelineruns/integration_pipelinerun_pass.yaml", + }, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, integrationTestScenario1)).Should(Succeed()) + helpers.SetScenarioIntegrationStatusAsValid(integrationTestScenario1, "valid") + testReleasePlan = &releasev1alpha1.ReleasePlan{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-releaseplan-", @@ -1495,6 +1533,348 @@ var _ = Describe("Snapshot Adapter", Ordered, func() { }) }) + + When("the run label has the value 'all'", func() { + var ( + buf bytes.Buffer + ) + + const ( + fakePLRName string = "pipelinerun-test" + fakeDetails string = "Lorem ipsum sit dolor mit amet" + ) + + BeforeEach(func() { + err := gitops.MarkSnapshotAsPassed(ctx, k8sClient, hasSnapshot, "test passed") + Expect(err).To(Succeed()) + Expect(gitops.HaveAppStudioTestsSucceeded(hasSnapshot)).To(BeTrue()) + + // mock that test for scenario is already in progress by setting it in annotation + statuses, err := intgteststat.NewSnapshotIntegrationTestStatuses("") + Expect(err).To(Succeed()) + statuses.UpdateTestStatusIfChanged(integrationTestScenario.Name, intgteststat.IntegrationTestStatusInProgress, fakeDetails) + Expect(statuses.UpdateTestPipelineRunName(integrationTestScenario.Name, fakePLRName)).To(Succeed()) + Expect(gitops.WriteIntegrationTestStatusesIntoSnapshot(ctx, hasSnapshot, statuses, k8sClient)).Should(Succeed()) + + // add rerun label + // we cannot update it into k8s DB via patch, it would trigger reconciliation in background + // and test wouldn't test anything + hasSnapshot.Labels[gitops.SnapshotIntegrationTestRun] = "all" + + log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)} + adapter = NewAdapter(ctx, hasSnapshot, hasApp, log, loader.NewMockLoader(), k8sClient) + adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ApplicationContextKey, + Resource: hasApp, + }, + { + ContextKey: loader.ComponentContextKey, + Resource: hasComp, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: hasSnapshot, + }, + { + ContextKey: loader.SnapshotComponentsContextKey, + Resource: []applicationapiv1alpha1.Component{*hasComp}, + }, + { + ContextKey: loader.AllIntegrationTestScenariosContextKey, + Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario, *integrationTestScenario1}, + }, + }) + }) + + It("creates Integration PLR for the new ITS and skips for the one that's already InProgress", func() { + statuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + _, ok := statuses.GetScenarioStatus(integrationTestScenario1.Name) + Expect(ok).To(BeFalse()) // no entry for 'integrationTestScenario1' yet, because it wasn't run before + + result, err := adapter.EnsureRerunPipelineRunsExist() + Expect(err).To(Succeed()) + Expect(result.CancelRequest).To(BeFalse()) + + Expect(buf.String()).Should(ContainSubstring(fmt.Sprintf("Skipping re-run for IntegrationTestScenario since it's in 'InProgress' or 'Pending' state Scenario %s", integrationTestScenario.Name))) + + statuses, err = gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + detail, ok := statuses.GetScenarioStatus(integrationTestScenario1.Name) + Expect(ok).To(BeTrue()) // 'integrationTestScenario1' has a status now + Expect(detail).ToNot(BeNil()) + Expect(detail.TestPipelineRunName).ToNot(BeEmpty()) + + m := MatchKeys(IgnoreExtras, Keys{ + gitops.SnapshotIntegrationTestRun: Equal("all"), + }) + Expect(hasSnapshot.GetLabels()).ShouldNot(m, "shouln't have 'run' label after running scenario") + + // We reset Snapshot's status since we created a new Intg PLR for the ITS + Expect(hasSnapshot.Status.Conditions).NotTo(BeNil()) + Expect(gitops.IsSnapshotStatusConditionSet(hasSnapshot, gitops.AppStudioTestSucceededCondition, + metav1.ConditionUnknown, "InProgress")).To(BeTrue()) + + // Verify that the message field of "AppStudioIntegrationStatusCondition" condition mentions the re-run initiation + condition := meta.FindStatusCondition(hasSnapshot.Status.Conditions, gitops.AppStudioIntegrationStatusCondition) + Expect(condition.Message).To(Equal("Integration test re-run initiated for Snapshot")) + }) + }) + + When("the run label has the value 'all' but with a component-type context on an ITS", func() { + var ( + buf bytes.Buffer + ) + + const ( + fakePLRName string = "pipelinerun-test" + fakeDetails string = "Lorem ipsum sit dolor mit amet" + ) + + BeforeEach(func() { + err := gitops.MarkSnapshotAsPassed(ctx, k8sClient, hasSnapshot, "test passed") + Expect(err).To(Succeed()) + Expect(gitops.HaveAppStudioTestsSucceeded(hasSnapshot)).To(BeTrue()) + + // Setting the context of 'integrationTestScenario1' to NOT match the current Component + integrationTestScenario1.Spec.Contexts = append(integrationTestScenario1.Spec.Contexts, v1beta2.TestContext{Name: "component_my-comp", Description: "Single component testing for 'my-comp' specifically"}) + Expect(k8sClient.Update(ctx, integrationTestScenario1)).Should(Succeed()) + + // mock that test for scenario is already in progress by setting it in annotation + statuses, err := intgteststat.NewSnapshotIntegrationTestStatuses("") + Expect(err).To(Succeed()) + statuses.UpdateTestStatusIfChanged(integrationTestScenario.Name, intgteststat.IntegrationTestStatusInProgress, fakeDetails) + Expect(statuses.UpdateTestPipelineRunName(integrationTestScenario.Name, fakePLRName)).To(Succeed()) + Expect(gitops.WriteIntegrationTestStatusesIntoSnapshot(ctx, hasSnapshot, statuses, k8sClient)).Should(Succeed()) + + // add rerun label + // we cannot update it into k8s DB via patch, it would trigger reconciliation in background + // and test wouldn't test anything + hasSnapshot.Labels[gitops.SnapshotIntegrationTestRun] = "all" + + log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)} + adapter = NewAdapter(ctx, hasSnapshot, hasApp, log, loader.NewMockLoader(), k8sClient) + adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ApplicationContextKey, + Resource: hasApp, + }, + { + ContextKey: loader.ComponentContextKey, + Resource: hasComp, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: hasSnapshot, + }, + { + ContextKey: loader.SnapshotComponentsContextKey, + Resource: []applicationapiv1alpha1.Component{*hasComp}, + }, + { + ContextKey: loader.AllIntegrationTestScenariosContextKey, + Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario, *integrationTestScenario1}, + }, + }) + }) + + It("does not create Integration PLR for the new ITS since its context doesn't match with the Snapshot", func() { + statuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + _, ok := statuses.GetScenarioStatus(integrationTestScenario1.Name) + Expect(ok).To(BeFalse()) // no entry for 'integrationTestScenario1' yet, because it wasn't run before + + result, err := adapter.EnsureRerunPipelineRunsExist() + Expect(err).To(Succeed()) + Expect(result.CancelRequest).To(BeFalse()) + + Expect(buf.String()).Should(ContainSubstring(fmt.Sprintf("Skipping re-run for IntegrationTestScenario since it's in 'InProgress' or 'Pending' state Scenario %s", integrationTestScenario.Name))) + Expect(buf.String()).Should(ContainSubstring("1 out of 1 requested IntegrationTestScenario(s) are either in 'InProgress' or 'Pending' state, skipping their re-runs Label all")) + + statuses, err = gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + _, ok = statuses.GetScenarioStatus(integrationTestScenario1.Name) + Expect(ok).To(BeFalse()) // 'integrationTestScenario1' does NOT has a status because it's context doesn't match with the 'hasSnapshot' + + m := MatchKeys(IgnoreExtras, Keys{ + gitops.SnapshotIntegrationTestRun: Equal("all"), + }) + Expect(hasSnapshot.GetLabels()).ShouldNot(m, "shouln't have 'run' label after running scenario") + + // Snapshot status is True because no new Integration PLRS were created + Expect(hasSnapshot.Status.Conditions).NotTo(BeNil()) + Expect(gitops.IsSnapshotStatusConditionSet(hasSnapshot, gitops.AppStudioTestSucceededCondition, + metav1.ConditionTrue, "Passed")).To(BeTrue()) // Because we didn't reset Snapshot's status since 1 ITS has mismatching context, other one is "InProgress" + }) + }) + + When("the run label has the name of an ITS with a component-type context", func() { + var ( + buf bytes.Buffer + ) + + BeforeEach(func() { + err := gitops.MarkSnapshotAsPassed(ctx, k8sClient, hasSnapshot, "test passed") + Expect(err).To(Succeed()) + Expect(gitops.HaveAppStudioTestsSucceeded(hasSnapshot)).To(BeTrue()) + + // Setting the context of 'integrationTestScenario1' to NOT match the current Component + integrationTestScenario1.Spec.Contexts = append(integrationTestScenario1.Spec.Contexts, v1beta2.TestContext{Name: "component_my-comp", Description: "Single component testing for 'my-comp' specifically"}) + Expect(k8sClient.Update(ctx, integrationTestScenario1)).Should(Succeed()) + + // add rerun label + // we cannot update it into k8s DB via patch, it would trigger reconciliation in background + // and test wouldn't test anything + hasSnapshot.Labels[gitops.SnapshotIntegrationTestRun] = integrationTestScenario1.Name + + log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)} + adapter = NewAdapter(ctx, hasSnapshot, hasApp, log, loader.NewMockLoader(), k8sClient) + adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ApplicationContextKey, + Resource: hasApp, + }, + { + ContextKey: loader.ComponentContextKey, + Resource: hasComp, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: hasSnapshot, + }, + { + ContextKey: loader.SnapshotComponentsContextKey, + Resource: []applicationapiv1alpha1.Component{*hasComp}, + }, + { + ContextKey: loader.AllIntegrationTestScenariosContextKey, + Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario, *integrationTestScenario1}, + }, + }) + }) + + It("does creates Integration PLR for the new ITS even though its context doesn't match with the Snapshot", func() { + statuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + _, ok := statuses.GetScenarioStatus(integrationTestScenario1.Name) + Expect(ok).To(BeFalse()) // no entry for 'integrationTestScenario1' yet, because it wasn't run before + + result, err := adapter.EnsureRerunPipelineRunsExist() + Expect(err).To(Succeed()) + Expect(result.CancelRequest).To(BeFalse()) + + statuses, err = gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + detail, ok := statuses.GetScenarioStatus(integrationTestScenario1.Name) + // 'integrationTestScenario1' does have a status because the run was requested specifically with the ITS name. + // When users explicitly request run of a specific ITS, then we ignore the context of that ITS and process it. + Expect(ok).To(BeTrue()) + Expect(detail).ToNot(BeNil()) + Expect(detail.TestPipelineRunName).ToNot(BeEmpty()) + + m := MatchKeys(IgnoreExtras, Keys{ + gitops.SnapshotIntegrationTestRun: Equal(integrationTestScenario1.Name), + }) + Expect(hasSnapshot.GetLabels()).ShouldNot(m, "shouln't have 'run' label after running scenario") + + // We reset Snapshot's status since we created a new Intg PLR for the ITS + Expect(hasSnapshot.Status.Conditions).NotTo(BeNil()) + Expect(gitops.IsSnapshotStatusConditionSet(hasSnapshot, gitops.AppStudioTestSucceededCondition, + metav1.ConditionUnknown, "InProgress")).To(BeTrue()) + + // Verify that the message field of "AppStudioIntegrationStatusCondition" condition mentions the re-run initiation + condition := meta.FindStatusCondition(hasSnapshot.Status.Conditions, gitops.AppStudioIntegrationStatusCondition) + Expect(condition.Message).To(Equal("Integration test re-run initiated for Snapshot")) + }) + }) + + When("the run label has the name of an ITS that was already executed before", func() { + var ( + buf bytes.Buffer + ) + + const ( + fakePLRName string = "pipelinerun-test" + fakeDetails string = "Lorem ipsum sit dolor mit amet" + ) + + BeforeEach(func() { + err := gitops.MarkSnapshotAsPassed(ctx, k8sClient, hasSnapshot, "test passed") + Expect(err).To(Succeed()) + Expect(gitops.HaveAppStudioTestsSucceeded(hasSnapshot)).To(BeTrue()) + + // mock that test for scenario is already in progress by setting it in annotation + statuses, err := intgteststat.NewSnapshotIntegrationTestStatuses("") + Expect(err).To(Succeed()) + statuses.UpdateTestStatusIfChanged(integrationTestScenario.Name, intgteststat.IntegrationTestStatusTestPassed, fakeDetails) + Expect(statuses.UpdateTestPipelineRunName(integrationTestScenario.Name, fakePLRName)).To(Succeed()) + Expect(gitops.WriteIntegrationTestStatusesIntoSnapshot(ctx, hasSnapshot, statuses, k8sClient)).Should(Succeed()) + + // add rerun label + // we cannot update it into k8s DB via patch, it would trigger reconciliation in background + // and test wouldn't test anything + hasSnapshot.Labels[gitops.SnapshotIntegrationTestRun] = integrationTestScenario.Name + + log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)} + adapter = NewAdapter(ctx, hasSnapshot, hasApp, log, loader.NewMockLoader(), k8sClient) + adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ApplicationContextKey, + Resource: hasApp, + }, + { + ContextKey: loader.ComponentContextKey, + Resource: hasComp, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: hasSnapshot, + }, + { + ContextKey: loader.SnapshotComponentsContextKey, + Resource: []applicationapiv1alpha1.Component{*hasComp}, + }, + { + ContextKey: loader.AllIntegrationTestScenariosContextKey, + Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario}, + }, + }) + }) + + It("does create a new Integration PLR for the ITS which was already executed before", func() { + statuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + detail, ok := statuses.GetScenarioStatus(integrationTestScenario.Name) + Expect(ok).To(BeTrue()) // Entry exists for 'integrationTestScenario' because it was run before + Expect(detail).ToNot(BeNil()) + Expect(detail.TestPipelineRunName).To(Equal(fakePLRName)) + + result, err := adapter.EnsureRerunPipelineRunsExist() + Expect(err).To(Succeed()) + Expect(result.CancelRequest).To(BeFalse()) + + Expect(buf.String()).Should(ContainSubstring(fmt.Sprintf("Creating new pipelinerun for integrationTestscenario integrationTestScenario.Name %s", integrationTestScenario.Name))) + + statuses, err = gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(hasSnapshot) + Expect(err).To(Succeed()) + detail, ok = statuses.GetScenarioStatus(integrationTestScenario.Name) + Expect(ok).To(BeTrue()) + Expect(detail).ToNot(BeNil()) + // The name of the PLR is updated to match the latest one + Expect(detail.TestPipelineRunName).ToNot(Equal(fakePLRName)) + + m := MatchKeys(IgnoreExtras, Keys{ + gitops.SnapshotIntegrationTestRun: Equal(integrationTestScenario.Name), + }) + Expect(hasSnapshot.GetLabels()).ShouldNot(m, "shouln't have 'run' label after running scenario") + + // We reset Snapshot's status since we created a new Intg PLR for the ITS + Expect(hasSnapshot.Status.Conditions).NotTo(BeNil()) + Expect(gitops.IsSnapshotStatusConditionSet(hasSnapshot, gitops.AppStudioTestSucceededCondition, + metav1.ConditionUnknown, "InProgress")).To(BeTrue()) + }) + }) }) When("Adapter is created for override snapshot", func() {