@@ -17,6 +17,7 @@ limitations under the License.
1717package workapplier
1818
1919import (
20+ "context"
2021 "fmt"
2122 "log"
2223 "os"
@@ -32,8 +33,10 @@ import (
3233 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3334 "k8s.io/apimachinery/pkg/runtime"
3435 "k8s.io/apimachinery/pkg/runtime/schema"
36+ "k8s.io/apimachinery/pkg/types"
3537 "k8s.io/client-go/kubernetes/scheme"
3638 "k8s.io/utils/ptr"
39+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
3740
3841 fleetv1beta1 "github.com/kubefleet-dev/kubefleet/apis/placement/v1beta1"
3942)
@@ -208,7 +211,8 @@ var (
208211)
209212
210213var (
211- ignoreFieldTypeMetaInNamespace = cmpopts .IgnoreFields (corev1.Namespace {}, "TypeMeta" )
214+ ignoreFieldTypeMetaInNamespace = cmpopts .IgnoreFields (corev1.Namespace {}, "TypeMeta" )
215+ ignoreFieldObjectMetaresourceVersion = cmpopts .IgnoreFields (metav1.ObjectMeta {}, "ResourceVersion" )
212216
213217 lessFuncAppliedResourceMeta = func (i , j fleetv1beta1.AppliedResourceMeta ) bool {
214218 iStr := fmt .Sprintf ("%s/%s/%s/%s/%s" , i .Group , i .Version , i .Kind , i .Namespace , i .Name )
@@ -262,6 +266,14 @@ func TestMain(m *testing.M) {
262266 os .Exit (m .Run ())
263267}
264268
269+ func fakeClientScheme (t * testing.T ) * runtime.Scheme {
270+ scheme := runtime .NewScheme ()
271+ if err := fleetv1beta1 .AddToScheme (scheme ); err != nil {
272+ t .Fatalf ("Failed to add placement v1beta1 scheme: %v" , err )
273+ }
274+ return scheme
275+ }
276+
265277func initializeVariables () {
266278 var err error
267279
@@ -332,3 +344,197 @@ func TestPrepareManifestProcessingBundles(t *testing.T) {
332344 t .Errorf ("prepareManifestProcessingBundles() mismatches (-got +want):\n %s" , diff )
333345 }
334346}
347+
348+ // TestEnsureAppliedWork tests the ensureAppliedWork method.
349+ func TestEnsureAppliedWork (t * testing.T ) {
350+ ctx := context .Background ()
351+
352+ fakeUID := types .UID ("foo" )
353+ testCases := []struct {
354+ name string
355+ work * fleetv1beta1.Work
356+ appliedWork * fleetv1beta1.AppliedWork
357+ wantWork * fleetv1beta1.Work
358+ wantAppliedWork * fleetv1beta1.AppliedWork
359+ }{
360+ {
361+ name : "with work cleanup finalizer present, but no corresponding AppliedWork exists" ,
362+ work : & fleetv1beta1.Work {
363+ ObjectMeta : metav1.ObjectMeta {
364+ Name : workName ,
365+ Namespace : memberReservedNSName1 ,
366+ Finalizers : []string {
367+ fleetv1beta1 .WorkFinalizer ,
368+ },
369+ },
370+ },
371+ wantWork : & fleetv1beta1.Work {
372+ ObjectMeta : metav1.ObjectMeta {
373+ Name : workName ,
374+ Namespace : memberReservedNSName1 ,
375+ Finalizers : []string {
376+ fleetv1beta1 .WorkFinalizer ,
377+ },
378+ },
379+ },
380+ wantAppliedWork : & fleetv1beta1.AppliedWork {
381+ ObjectMeta : metav1.ObjectMeta {
382+ Name : workName ,
383+ },
384+ Spec : fleetv1beta1.AppliedWorkSpec {
385+ WorkName : workName ,
386+ WorkNamespace : memberReservedNSName1 ,
387+ },
388+ },
389+ },
390+ {
391+ name : "with work cleanup finalizer present, and corresponding AppliedWork exists" ,
392+ work : & fleetv1beta1.Work {
393+ ObjectMeta : metav1.ObjectMeta {
394+ Name : workName ,
395+ Namespace : memberReservedNSName1 ,
396+ Finalizers : []string {
397+ fleetv1beta1 .WorkFinalizer ,
398+ },
399+ },
400+ },
401+ appliedWork : & fleetv1beta1.AppliedWork {
402+ ObjectMeta : metav1.ObjectMeta {
403+ Name : workName ,
404+ // Add the UID field to track if the method returns the existing object.
405+ UID : fakeUID ,
406+ },
407+ Spec : fleetv1beta1.AppliedWorkSpec {
408+ WorkName : workName ,
409+ WorkNamespace : memberReservedNSName1 ,
410+ },
411+ },
412+ wantWork : & fleetv1beta1.Work {
413+ ObjectMeta : metav1.ObjectMeta {
414+ Name : workName ,
415+ Namespace : memberReservedNSName1 ,
416+ Finalizers : []string {
417+ fleetv1beta1 .WorkFinalizer ,
418+ },
419+ },
420+ },
421+ wantAppliedWork : & fleetv1beta1.AppliedWork {
422+ ObjectMeta : metav1.ObjectMeta {
423+ Name : workName ,
424+ UID : fakeUID ,
425+ },
426+ Spec : fleetv1beta1.AppliedWorkSpec {
427+ WorkName : workName ,
428+ WorkNamespace : memberReservedNSName1 ,
429+ },
430+ },
431+ },
432+ {
433+ name : "without work cleanup finalizer, but corresponding AppliedWork exists" ,
434+ work : & fleetv1beta1.Work {
435+ ObjectMeta : metav1.ObjectMeta {
436+ Name : workName ,
437+ Namespace : memberReservedNSName1 ,
438+ },
439+ },
440+ appliedWork : & fleetv1beta1.AppliedWork {
441+ ObjectMeta : metav1.ObjectMeta {
442+ Name : workName ,
443+ // Add the UID field to track if the method returns the existing object.
444+ UID : fakeUID ,
445+ },
446+ Spec : fleetv1beta1.AppliedWorkSpec {
447+ WorkName : workName ,
448+ WorkNamespace : memberReservedNSName1 ,
449+ },
450+ },
451+ wantWork : & fleetv1beta1.Work {
452+ ObjectMeta : metav1.ObjectMeta {
453+ Name : workName ,
454+ Namespace : memberReservedNSName1 ,
455+ Finalizers : []string {
456+ fleetv1beta1 .WorkFinalizer ,
457+ },
458+ },
459+ },
460+ wantAppliedWork : & fleetv1beta1.AppliedWork {
461+ ObjectMeta : metav1.ObjectMeta {
462+ Name : workName ,
463+ UID : fakeUID ,
464+ },
465+ Spec : fleetv1beta1.AppliedWorkSpec {
466+ WorkName : workName ,
467+ WorkNamespace : memberReservedNSName1 ,
468+ },
469+ },
470+ },
471+ {
472+ name : "without work cleanup finalizer, and no corresponding AppliedWork exists" ,
473+ work : & fleetv1beta1.Work {
474+ ObjectMeta : metav1.ObjectMeta {
475+ Name : workName ,
476+ Namespace : memberReservedNSName1 ,
477+ },
478+ },
479+ wantWork : & fleetv1beta1.Work {
480+ ObjectMeta : metav1.ObjectMeta {
481+ Name : workName ,
482+ Namespace : memberReservedNSName1 ,
483+ Finalizers : []string {
484+ fleetv1beta1 .WorkFinalizer ,
485+ },
486+ },
487+ },
488+ wantAppliedWork : & fleetv1beta1.AppliedWork {
489+ ObjectMeta : metav1.ObjectMeta {
490+ Name : workName ,
491+ },
492+ Spec : fleetv1beta1.AppliedWorkSpec {
493+ WorkName : workName ,
494+ WorkNamespace : memberReservedNSName1 ,
495+ },
496+ },
497+ },
498+ }
499+
500+ for _ , tc := range testCases {
501+ t .Run (tc .name , func (t * testing.T ) {
502+ hubClientScheme := fakeClientScheme (t )
503+ fakeHubClient := fake .NewClientBuilder ().
504+ WithScheme (hubClientScheme ).
505+ WithObjects (tc .work ).
506+ Build ()
507+
508+ memberClientScheme := fakeClientScheme (t )
509+ fakeMemberClientBuilder := fake .NewClientBuilder ().WithScheme (memberClientScheme )
510+ if tc .appliedWork != nil {
511+ fakeMemberClientBuilder = fakeMemberClientBuilder .WithObjects (tc .appliedWork )
512+ }
513+ fakeMemberClient := fakeMemberClientBuilder .Build ()
514+
515+ r := & Reconciler {
516+ hubClient : fakeHubClient ,
517+ spokeClient : fakeMemberClient ,
518+ }
519+
520+ gotAppliedWork , err := r .ensureAppliedWork (ctx , tc .work )
521+ if err != nil {
522+ t .Fatalf ("ensureAppliedWork() = %v, want no error" , err )
523+ }
524+
525+ // Verify the Work object.
526+ gotWork := & fleetv1beta1.Work {}
527+ if err := fakeHubClient .Get (ctx , types.NamespacedName {Name : tc .work .Name , Namespace : tc .work .Namespace }, gotWork ); err != nil {
528+ t .Fatalf ("failed to get Work object from fake hub client: %v" , err )
529+ }
530+ if diff := cmp .Diff (gotWork , tc .wantWork , ignoreFieldObjectMetaresourceVersion ); diff != "" {
531+ t .Errorf ("Work objects diff (-got +want):\n %s" , diff )
532+ }
533+
534+ // Verify the AppliedWork object.
535+ if diff := cmp .Diff (gotAppliedWork , tc .wantAppliedWork , ignoreFieldObjectMetaresourceVersion ); diff != "" {
536+ t .Errorf ("AppliedWork objects diff (-got +want):\n %s" , diff )
537+ }
538+ })
539+ }
540+ }
0 commit comments