package main import ( "context" "errors" "reflect" "slices" "testing" "time" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" dynamicfake "k8s.io/client-go/dynamic/fake" k8stesting "k8s.io/client-go/testing" ) func TestRecordRelease(t *testing.T) { ctx := context.Background() now := time.Date(2026, 10, 2, 0, 0, 0, 0, time.UTC) object := pointDelivery("0123456789abcdef0123456789abcdef01234567") c := testController(t, object, &mutableRefs{}, &now) d, err := decodeProjectDelivery(object) if err != nil { t.Fatal(err) } if err := c.ensureWorkflow(ctx, &d, "accepted", "nuthatch-delivery", "build", nil, nil); err != nil { t.Fatal(err) } workflow, err := c.client.Resource(workflowGVR).Namespace("delivery").Get(ctx, "accepted", metav1.GetOptions{}) if err != nil { t.Fatal(err) } gc := "workflows.argoproj.io/artifact-gc" workflow.SetFinalizers([]string{recordKey, gc}) if _, err := c.client.Resource(workflowGVR).Namespace("delivery").Update(ctx, workflow, metav1.UpdateOptions{}); err != nil { t.Fatal(err) } d.Status.Points = []attemptStatus{{WorkflowName: "accepted", Phase: "Pending"}} client := c.client.(*dynamicfake.FakeDynamicClient) fail := true client.PrependReactor("update", "projectdeliveries", func(action k8stesting.Action) (bool, runtime.Object, error) { if fail && action.GetSubresource() == "status" { return true, nil, apierrors.NewConflict(projectDeliveryGVR.GroupResource(), "point", errors.New("synthetic conflict")) } return false, nil, nil }) if err := c.updateStatus(ctx, object, &d); !apierrors.IsConflict(err) { t.Fatalf("status conflict = %v", err) } workflow, err = c.client.Resource(workflowGVR).Namespace("delivery").Get(ctx, "accepted", metav1.GetOptions{}) if err != nil { t.Fatal(err) } if !slices.Contains(workflow.GetFinalizers(), recordKey) { t.Fatal("checkpoint finalizer was removed before status persistence") } fail = false if err := c.updateStatus(ctx, object, &d); err != nil { t.Fatal(err) } workflow, err = c.client.Resource(workflowGVR).Namespace("delivery").Get(ctx, "accepted", metav1.GetOptions{}) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(workflow.GetFinalizers(), []string{gc}) || workflow.GetLabels()[recordKey] != "" { t.Fatalf("remaining checkpoint: finalizers=%v labels=%v", workflow.GetFinalizers(), workflow.GetLabels()) } } func TestRecordOrphans(t *testing.T) { for _, state := range []string{"missing", "deleting", "replaced"} { t.Run(state, func(t *testing.T) { ctx := context.Background() now := time.Date(2026, 10, 2, 0, 0, 0, 0, time.UTC) object := pointDelivery("0123456789abcdef0123456789abcdef01234567") c := testController(t, object, &mutableRefs{}, &now) d, err := decodeProjectDelivery(object) if err != nil { t.Fatal(err) } if err := c.ensureWorkflow(ctx, &d, "accepted", "nuthatch-delivery", "build", nil, nil); err != nil { t.Fatal(err) } resource := c.client.Resource(projectDeliveryGVR).Namespace("delivery") if state == "missing" { if err := resource.Delete(ctx, "point", metav1.DeleteOptions{}); err != nil { t.Fatal(err) } } else { if state == "deleting" { object.SetDeletionTimestamp(&metav1.Time{Time: now}) } else { object.SetUID("replacement") } if _, err := resource.Update(ctx, object, metav1.UpdateOptions{}); err != nil { t.Fatal(err) } } if err := c.releaseOrphans(ctx); err != nil { t.Fatal(err) } workflow, err := c.client.Resource(workflowGVR).Namespace("delivery").Get(ctx, "accepted", metav1.GetOptions{}) if err != nil { t.Fatal(err) } if slices.Contains(workflow.GetFinalizers(), recordKey) { t.Fatal("deleted owner retained a checkpoint finalizer") } }) } }