package main import ( "context" "slices" "testing" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestRecoveryOrder(t *testing.T) { for _, restored := range []bool{false, true} { t.Run(map[bool]string{false: "missing-time", true: "known-history"}[restored], func(t *testing.T) { ctx := context.Background() now := time.Date(2026, 10, 2, 0, 0, 0, 0, time.UTC) instance := testController(t, continuousDelivery(), &mutableRefs{}, &now) delivery, err := decodeProjectDelivery(continuousDelivery()) if err != nil { t.Fatal(err) } oldTime, newTime := now.Add(-2*time.Hour), now.Add(-time.Hour) oldRev := "0123456789abcdef0123456789abcdef01234567" newRev := "89abcdef0123456789abcdef0123456789abcdef" for _, entry := range []struct { name, revision, phase string created time.Time }{ {"old", oldRev, "Failed", oldTime}, {"new", newRev, "Succeeded", newTime}, } { input := map[string]string{"source-ref": "refs/heads/main", "source-revision": entry.revision} if err := instance.ensureWorkflow(ctx, &delivery, entry.name, delivery.Spec.WorkflowTemplateRef.Name, "build", input, nil); err != nil { t.Fatal(err) } resource := instance.client.Resource(workflowGVR).Namespace("delivery") workflow, err := resource.Get(ctx, entry.name, metav1.GetOptions{}) if err != nil { t.Fatal(err) } workflow.SetCreationTimestamp(metav1.NewTime(entry.created)) if _, err := resource.Update(ctx, workflow, metav1.UpdateOptions{}); err != nil { t.Fatal(err) } setNamedWorkflowStatus(t, instance, entry.name, entry.phase, nil) } delivery.Status.Continuous = &continuousStatus{ Attempt: &attemptStatus{WorkflowName: "new", Ref: "refs/heads/main", Revision: newRev, SourceIdentity: "tree-new", Phase: "Succeeded"}, Delivered: &deliveredSource{Revision: newRev, Identity: "tree-new"}, } if restored { delivery.Status.Continuous.Attempt = &attemptStatus{WorkflowName: "old", Ref: "refs/heads/main", Revision: oldRev, Phase: "Failed", CreatedAt: &oldTime, History: &attemptHistory{Workflows: []string{"old", "new"}, Identities: []string{"tree-new"}}, } } if _, err := instance.recoverBuilds(ctx, &delivery); err != nil { t.Fatal(err) } current := delivery.Status.Continuous.Attempt if current.WorkflowName != "new" || current.Phase != "Succeeded" { t.Fatalf("latest successful attempt regressed to %s (%s)", current.WorkflowName, current.Phase) } if current.CreatedAt == nil || !current.CreatedAt.Equal(newTime) { t.Fatal("captured creation time missing") } if current.SourceIdentity != "tree-new" || !slices.Contains(current.History.Workflows, "old") { t.Fatal("captured identity or prior history lost") } if !slices.Contains(current.History.Workflows, "new") || len(current.History.Workflows) != 2 || !slices.Equal(current.History.Identities, []string{"tree-new"}) { t.Fatal("recovery changed the retained attempt set") } if otherAttemptActive(&delivery, "") { t.Fatal("terminal history recovery occupied an execution slot") } if delivered := delivery.Status.Continuous.Delivered; delivered.Revision != newRev || delivered.Identity != "tree-new" { t.Fatal("terminal history recovery changed the delivered source") } if attempt, _ := retryTarget(&delivery, "new"); attempt == nil { t.Fatal("latest owned terminal attempt is no longer retriable") } if changed, err := instance.recoverBuilds(ctx, &delivery); err != nil || changed { t.Fatalf("repeated recovery changed history: %t %v", changed, err) } }) } }