diff --git a/internal/engine/apply.go b/internal/engine/apply.go index 16ef5f3..6f84612 100644 --- a/internal/engine/apply.go +++ b/internal/engine/apply.go @@ -58,17 +58,22 @@ func NewRecordingApplier(inner Applier, sink *[]ChildID) *RecordingApplier { return &RecordingApplier{inner: inner, applied: sink} } -// Apply records obj's final identity, then delegates to inner and returns its -// result. The name is already final at this point (QualifyingApplier renames +// Apply delegates to inner and then records obj's final identity and returns its result. +// The name is already final at this point (QualifyingApplier renames // before delegating to its inner), so recording before delegating is correct. func (r *RecordingApplier) Apply(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + observed, err := r.inner.Apply(ctx, obj) + if err != nil { + return observed, err + } + *r.applied = append(*r.applied, ChildID{ - GVK: obj.GroupVersionKind(), - Namespace: obj.GetNamespace(), - Name: obj.GetName(), + GVK: observed.GroupVersionKind(), + Namespace: observed.GetNamespace(), + Name: observed.GetName(), }) - return r.inner.Apply(ctx, obj) + return observed, nil } // SSAApplier applies objects into one workspace via server-side apply using a diff --git a/internal/engine/apply_test.go b/internal/engine/apply_test.go index 9afbd39..fc6c7e6 100644 --- a/internal/engine/apply_test.go +++ b/internal/engine/apply_test.go @@ -160,6 +160,37 @@ func TestRecordingApplier_RecordsFinalIdentity(t *testing.T) { } } +func TestRecordingApplier_RecordsServerObservedIdentity(t *testing.T) { + inner := &fakeApplier{mutate: func(o *unstructured.Unstructured) { + o.SetNamespace("default") // server-side namespace defaulting + }} + var sink []ChildID + rec := NewRecordingApplier(inner, &sink) + + desired := &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "v1", "kind": "ConfigMap", + "metadata": map[string]any{"name": "cfg"}, // no namespace set + }} + observed, err := rec.Apply(context.Background(), desired) + if err != nil { + t.Fatalf("apply: %v", err) + } + if len(sink) != 1 { + t.Fatalf("recorded %d ChildIDs, want 1", len(sink)) + } + // keep-set entry must match what pruneChildren's List returns: the server object. + want := ChildID{ + GVK: observed.GroupVersionKind(), + Namespace: observed.GetNamespace(), + Name: observed.GetName(), + } + if sink[0] != want { + t.Fatalf("recorded ChildID = %+v, want server-observed %+v; "+ + "prune keep-set built from desired identity will not match the listed child", + sink[0], want) + } +} + func TestOwnerRefApplier_StampsInstanceOwner(t *testing.T) { inner := &fakeApplier{} owner := &unstructured.Unstructured{Object: map[string]any{