diff --git a/reconcilers/sync_test.go b/reconcilers/sync_test.go index d38b823..3a8d7c1 100644 --- a/reconcilers/sync_test.go +++ b/reconcilers/sync_test.go @@ -271,3 +271,64 @@ func TestSyncReconciler(t *testing.T) { return rtc.Metadata["SubReconciler"].(func(*testing.T, reconcilers.Config) reconcilers.SubReconciler[*resources.TestResource])(t, c) }) } + +func TestSyncReconcilerDuck(t *testing.T) { + testNamespace := "test-namespace" + testName := "test-resource" + + scheme := runtime.NewScheme() + // _ = resources.AddToScheme(scheme) + + resource := dies.TestDuckBlank. + APIVersion(resources.GroupVersion.String()). + Kind("TestResource"). + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Namespace(testNamespace) + d.Name(testName) + }). + SpecDie(func(d *dies.TestDuckSpecDie) { + d.AddField("mutation", "false") + }). + StatusDie(func(d *dies.TestResourceStatusDie) { + d.ConditionsDie( + diemetav1.ConditionBlank.Type(apis.ConditionReady).Status(metav1.ConditionUnknown).Reason("Initializing"), + ) + }) + + rts := rtesting.SubReconcilerTests[*resources.TestDuck]{ + "sync no mutation": { + Resource: resource.DieReleasePtr(), + Metadata: map[string]interface{}{ + "SubReconciler": func(t *testing.T, c reconcilers.Config) reconcilers.SubReconciler[*resources.TestDuck] { + return &reconcilers.SyncReconciler[*resources.TestDuck]{ + Sync: func(ctx context.Context, resource *resources.TestDuck) error { + return nil + }, + } + }, + }, + }, + "sync with mutation": { + Resource: resource.DieReleasePtr(), + ExpectResource: resource. + SpecDie(func(d *dies.TestDuckSpecDie) { + d.AddField("mutation", "true") + }). + DieReleasePtr(), + Metadata: map[string]interface{}{ + "SubReconciler": func(t *testing.T, c reconcilers.Config) reconcilers.SubReconciler[*resources.TestDuck] { + return &reconcilers.SyncReconciler[*resources.TestDuck]{ + Sync: func(ctx context.Context, resource *resources.TestDuck) error { + resource.Spec.Fields["mutation"] = "true" + return nil + }, + } + }, + }, + }, + } + + rts.Run(t, scheme, func(t *testing.T, rtc *rtesting.SubReconcilerTestCase[*resources.TestDuck], c reconcilers.Config) reconcilers.SubReconciler[*resources.TestDuck] { + return rtc.Metadata["SubReconciler"].(func(*testing.T, reconcilers.Config) reconcilers.SubReconciler[*resources.TestDuck])(t, c) + }) +} diff --git a/testing/subreconciler.go b/testing/subreconciler.go index 2c4cc15..d8fac64 100644 --- a/testing/subreconciler.go +++ b/testing/subreconciler.go @@ -14,9 +14,11 @@ import ( "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/vmware-labs/reconciler-runtime/duck" "github.com/vmware-labs/reconciler-runtime/internal" "github.com/vmware-labs/reconciler-runtime/reconcilers" rtime "github.com/vmware-labs/reconciler-runtime/time" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -182,12 +184,22 @@ func (tc *SubReconcilerTestCase[T]) Run(t *testing.T, scheme *runtime.Scheme, fa }() } + var givenResource client.Object = tc.Resource + if duck.IsDuck(givenResource, scheme) { + // convert the given resource duck to Unstructured so that it can be created on the fake client + uobj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.Resource) + if err != nil { + t.Fatalf("unable to convert Resource to Unstructured: %s", err) + } + givenResource = &unstructured.Unstructured{Object: uobj} + } + expectConfig := &ExpectConfig{ Name: "default", Scheme: scheme, StatusSubResourceTypes: tc.StatusSubResourceTypes, - GivenObjects: append(tc.GivenObjects, tc.Resource), - APIGivenObjects: append(tc.APIGivenObjects, tc.Resource), + GivenObjects: append(tc.GivenObjects, givenResource), + APIGivenObjects: append(tc.APIGivenObjects, givenResource), WithClientBuilder: tc.WithClientBuilder, WithReactors: tc.WithReactors, GivenTracks: tc.GivenTracks,