quorate/services/project-delivery/controller/recovery_test.go

87 lines
3.6 KiB
Go
Raw Normal View History

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)
}
})
}
}