Repository: envoyproxy/ai-gateway
Pull request #253 (2025-01-31 17:47:33)

test: adds more coverage of internal/controller
**Commit Message**:

This increases the test coverage of controller
package. The only exception left is the main
controller.go file which requires a relatively large
refactoring.


Patch:
diff --git a/internal/controller/ai_gateway_route.go b/internal/controller/ai_gateway_route.go
index b08ac1c..b76cdbc 100644
--- a/internal/controller/ai_gateway_route.go
+++ b/internal/controller/ai_gateway_route.go
@@ -8,6 +8,7 @@ import (
 	"github.com/go-logr/logr"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -78,13 +79,11 @@ func (c *aiGatewayRouteController) Reconcile(ctx context.Context, req reconcile.
 	}
 
 	if err := c.ensuresExtProcConfigMapExists(ctx, &aiGatewayRoute); err != nil {
-		logger.Error(err, "Failed to reconcile extProc config map")
-		return ctrl.Result{}, err
+		return ctrl.Result{}, fmt.Errorf("failed to ensure extproc configmap exists: %w", err)
 	}
 
 	if err := c.reconcileExtProcExtensionPolicy(ctx, &aiGatewayRoute); err != nil {
-		logger.Error(err, "Failed to reconcile extension policy")
-		return ctrl.Result{}, err
+		return ctrl.Result{}, fmt.Errorf("failed to reconcile extension policy: %w", err)
 	}
 	// Send a copy to the config sink for a full reconciliation on HTTPRoute as well as the extproc config.
 	c.eventChan <- aiGatewayRoute.DeepCopy()
@@ -93,13 +92,14 @@ func (c *aiGatewayRouteController) Reconcile(ctx context.Context, req reconcile.
 
 // reconcileExtProcExtensionPolicy creates or updates the extension policy for the external process.
 // It only changes the target references.
-func (c *aiGatewayRouteController) reconcileExtProcExtensionPolicy(ctx context.Context, aiGatewayRoute *aigv1a1.AIGatewayRoute) error {
+func (c *aiGatewayRouteController) reconcileExtProcExtensionPolicy(ctx context.Context, aiGatewayRoute *aigv1a1.AIGatewayRoute) (err error) {
 	var existingPolicy egv1a1.EnvoyExtensionPolicy
-	if err := c.client.Get(ctx, client.ObjectKey{Name: extProcName(aiGatewayRoute), Namespace: aiGatewayRoute.Namespace}, &existingPolicy); err == nil {
+	if err = c.client.Get(ctx, client.ObjectKey{Name: extProcName(aiGatewayRoute), Namespace: aiGatewayRoute.Namespace}, &existingPolicy); err == nil {
 		existingPolicy.Spec.PolicyTargetReferences.TargetRefs = aiGatewayRoute.Spec.TargetRefs
-		if err := c.client.Update(ctx, &existingPolicy); err != nil {
+		if err = c.client.Update(ctx, &existingPolicy); err != nil {
 			return fmt.Errorf("failed to update extension policy: %w", err)
 		}
+		return
 	} else if client.IgnoreNotFound(err) != nil {
 		return fmt.Errorf("failed to get extension policy: %w", err)
 	}
@@ -130,40 +130,35 @@ func (c *aiGatewayRouteController) reconcileExtProcExtensionPolicy(ctx context.C
 			}},
 		},
 	}
-	if err := ctrlutil.SetControllerReference(aiGatewayRoute, extPolicy, c.client.Scheme()); err != nil {
-		c.logger.Error(err, "failed to set controller reference for envoy extension policy", "namespace", extPolicy.Namespace, "name", extPolicy.Name)
+	if err = ctrlutil.SetControllerReference(aiGatewayRoute, extPolicy, c.client.Scheme()); err != nil {
+		panic(fmt.Errorf("BUG: failed to set controller reference for extension policy: %w", err))
 	}
-	if err := c.client.Create(ctx, extPolicy); client.IgnoreAlreadyExists(err) != nil {
-		return fmt.Errorf("failed to create extension policy: %w", err)
+	if err = c.client.Create(ctx, extPolicy); err != nil {
+		err = fmt.Errorf("failed to create extension policy: %w", err)
 	}
-	return nil
+	return
 }
 
 // ensuresExtProcConfigMapExists ensures that a configmap exists for the external process.
 // This must happen before the external process deployment is created.
-func (c *aiGatewayRouteController) ensuresExtProcConfigMapExists(ctx context.Context, aiGatewayRoute *aigv1a1.AIGatewayRoute) error {
+func (c *aiGatewayRouteController) ensuresExtProcConfigMapExists(ctx context.Context, aiGatewayRoute *aigv1a1.AIGatewayRoute) (err error) {
 	name := extProcName(aiGatewayRoute)
 	// Check if a configmap exists for extproc exists, and if not, create one with the default config.
-	_, err := c.kube.CoreV1().ConfigMaps(aiGatewayRoute.Namespace).Get(ctx, name, metav1.GetOptions{})
-	if err != nil {
-		if client.IgnoreNotFound(err) == nil {
-			configMap := &corev1.ConfigMap{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      name,
-					Namespace: aiGatewayRoute.Namespace,
-				},
-				Data: map[string]string{expProcConfigFileName: filterapi.DefaultConfig},
-			}
-			if err := ctrlutil.SetControllerReference(aiGatewayRoute, configMap, c.client.Scheme()); err != nil {
-				c.logger.Error(err, "failed to set controller reference for service", "namespace", configMap.Namespace, "name", configMap.Name)
-			}
-			_, err = c.kube.CoreV1().ConfigMaps(aiGatewayRoute.Namespace).Create(ctx, configMap, metav1.CreateOptions{})
-			if err != nil {
-				return err
-			}
+	_, err = c.kube.CoreV1().ConfigMaps(aiGatewayRoute.Namespace).Get(ctx, name, metav1.GetOptions{})
+	if apierrors.IsNotFound(err) {
+		configMap := &corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      name,
+				Namespace: aiGatewayRoute.Namespace,
+			},
+			Data: map[string]string{expProcConfigFileName: filterapi.DefaultConfig},
+		}
+		if err := ctrlutil.SetControllerReference(aiGatewayRoute, configMap, c.client.Scheme()); err != nil {
+			panic(fmt.Errorf("BUG: failed to set controller reference for extproc configmap: %w", err))
 		}
+		_, err = c.kube.CoreV1().ConfigMaps(aiGatewayRoute.Namespace).Create(ctx, configMap, metav1.CreateOptions{})
 	}
-	return nil
+	return
 }
 
 func extProcName(route *aigv1a1.AIGatewayRoute) string {
diff --git a/internal/controller/controller.go b/internal/controller/controller.go
index dad1bcd..b0047a9 100644
--- a/internal/controller/controller.go
+++ b/internal/controller/controller.go
@@ -63,7 +63,7 @@ func StartControllers(ctx context.Context, config *rest.Config, logger logr.Logg
 
 	c := mgr.GetClient()
 	indexer := mgr.GetFieldIndexer()
-	if err = applyIndexing(indexer); err != nil {
+	if err = applyIndexing(indexer.IndexField); err != nil {
 		return fmt.Errorf("failed to apply indexing: %w", err)
 	}
 
@@ -130,18 +130,18 @@ const (
 	k8sClientIndexBackendSecurityPolicyToReferencingAIServiceBackend = "BackendSecurityPolicyToReferencingAIServiceBackend"
 )
 
-func applyIndexing(indexer client.FieldIndexer) error {
-	err := indexer.IndexField(context.Background(), &aigv1a1.AIGatewayRoute{},
+func applyIndexing(indexer func(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error) error {
+	err := indexer(context.Background(), &aigv1a1.AIGatewayRoute{},
 		k8sClientIndexBackendToReferencingAIGatewayRoute, aiGatewayRouteIndexFunc)
 	if err != nil {
 		return fmt.Errorf("failed to index field for AIGatewayRoute: %w", err)
 	}
-	err = indexer.IndexField(context.Background(), &aigv1a1.AIServiceBackend{},
+	err = indexer(context.Background(), &aigv1a1.AIServiceBackend{},
 		k8sClientIndexBackendSecurityPolicyToReferencingAIServiceBackend, aiServiceBackendIndexFunc)
 	if err != nil {
 		return fmt.Errorf("failed to index field for AIServiceBackend: %w", err)
 	}
-	err = indexer.IndexField(context.Background(), &aigv1a1.BackendSecurityPolicy{},
+	err = indexer(context.Background(), &aigv1a1.BackendSecurityPolicy{},
 		k8sClientIndexSecretToReferencingBackendSecurityPolicy, backendSecurityPolicyIndexFunc)
 	if err != nil {
 		return fmt.Errorf("failed to index field for BackendSecurityPolicy: %w", err)
diff --git a/internal/controller/sink.go b/internal/controller/sink.go
index 303b46f..3005b94 100644
--- a/internal/controller/sink.go
+++ b/internal/controller/sink.go
@@ -172,8 +172,8 @@ func (c *configSink) syncAIGatewayRoute(aiGatewayRoute *aigv1a1.AIGatewayRoute)
 			},
 			Spec: gwapiv1.HTTPRouteSpec{},
 		}
-		if err := ctrlutil.SetControllerReference(aiGatewayRoute, &httpRoute, c.client.Scheme()); err != nil {
-			c.logger.Error(err, "failed to set controller reference for http route", "namespace", httpRoute.Namespace, "name", httpRoute.Name)
+		if err = ctrlutil.SetControllerReference(aiGatewayRoute, &httpRoute, c.client.Scheme()); err != nil {
+			panic(fmt.Errorf("BUG: failed to set controller reference for HTTPRoute: %w", err))
 		}
 	} else if err != nil {
 		c.logger.Error(err, "failed to get HTTPRoute", "namespace", aiGatewayRoute.Namespace, "name", aiGatewayRoute.Name, "error", err)
@@ -408,15 +408,17 @@ func (c *configSink) newHTTPRoute(dst *gwapiv1.HTTPRoute, aiGatewayRoute *aigv1a
 	}
 
 	// Adds the default route rule with "/" path.
-	rules = append(rules, gwapiv1.HTTPRouteRule{
-		Matches: []gwapiv1.HTTPRouteMatch{
-			{Path: &gwapiv1.HTTPPathMatch{Value: ptr.To("/")}},
-		},
-		BackendRefs: []gwapiv1.HTTPBackendRef{
-			{BackendRef: gwapiv1.BackendRef{BackendObjectReference: backends[0].Spec.BackendRef}},
-		},
-		Filters: rewriteFilters,
-	})
+	if len(rules) > 0 {
+		rules = append(rules, gwapiv1.HTTPRouteRule{
+			Matches: []gwapiv1.HTTPRouteMatch{
+				{Path: &gwapiv1.HTTPPathMatch{Value: ptr.To("/")}},
+			},
+			BackendRefs: []gwapiv1.HTTPBackendRef{
+				{BackendRef: gwapiv1.BackendRef{BackendObjectReference: backends[0].Spec.BackendRef}},
+			},
+			Filters: rewriteFilters,
+		})
+	}
 
 	dst.Spec.Rules = rules
 
@@ -508,7 +510,7 @@ func (c *configSink) syncExtProcDeployment(ctx context.Context, aiGatewayRoute *
 				},
 			}
 			if err := ctrlutil.SetControllerReference(aiGatewayRoute, deployment, c.client.Scheme()); err != nil {
-				c.logger.Error(err, "failed to set controller reference for deployment", "namespace", deployment.Namespace, "name", deployment.Name)
+				panic(fmt.Errorf("BUG: failed to set controller reference for deployment: %w", err))
 			}
 			updatedSpec, err := c.mountBackendSecurityPolicySecrets(&deployment.Spec.Template.Spec, aiGatewayRoute)
 			if err == nil {
@@ -554,7 +556,7 @@ func (c *configSink) syncExtProcDeployment(ctx context.Context, aiGatewayRoute *
 		},
 	}
 	if err = ctrlutil.SetControllerReference(aiGatewayRoute, service, c.client.Scheme()); err != nil {
-		c.logger.Error(err, "failed to set controller reference for service", "namespace", service.Namespace, "name", service.Name)
+		panic(fmt.Errorf("BUG: failed to set controller reference for service: %w", err))
 	}
 	if _, err = c.kube.CoreV1().Services(aiGatewayRoute.Namespace).Create(ctx, service, metav1.CreateOptions{}); client.IgnoreAlreadyExists(err) != nil {
 		return fmt.Errorf("failed to create Service %s.%s: %w", name, aiGatewayRoute.Namespace, err)


Test patch:
diff --git a/.testcoverage.yml b/.testcoverage.yml
index a8bfcaa..a677b76 100644
--- a/.testcoverage.yml
+++ b/.testcoverage.yml
@@ -6,7 +6,7 @@ local-prefix: "github.com/envoyproxy/ai-gateway/"
 threshold:
   file: 70
   # TODO: increase to 90.
-  package: 80
+  package: 81
   # TODO: increase to 95.
   total: 83
 
@@ -18,4 +18,4 @@ exclude:
     - cmd/
     - tests/internal/envtest.go
     # TODO: Remove this exclusion.
-    - internal/controller
+    - internal/controller/controller.go
diff --git a/internal/controller/ai_gateway_route_test.go b/internal/controller/ai_gateway_route_test.go
index 9cc7b6b..ca72353 100644
--- a/internal/controller/ai_gateway_route_test.go
+++ b/internal/controller/ai_gateway_route_test.go
@@ -10,22 +10,61 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 	fake2 "k8s.io/client-go/kubernetes/fake"
 	"k8s.io/utils/ptr"
+	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 
 	aigv1a1 "github.com/envoyproxy/ai-gateway/api/v1alpha1"
 	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
+func TestAIGatewayRouteController_Reconcile(t *testing.T) {
+	ch := make(chan ConfigSinkEvent, 100)
+	cl := fake.NewClientBuilder().WithScheme(scheme).Build()
+	c := NewAIGatewayRouteController(cl, fake2.NewClientset(), ctrl.Log, ch)
+
+	err := cl.Create(context.Background(), &aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: "myroute", Namespace: "default"}})
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "myroute"}})
+	require.NoError(t, err)
+	item, ok := <-ch
+	require.True(t, ok)
+	require.IsType(t, &aigv1a1.AIGatewayRoute{}, item)
+	require.Equal(t, "myroute", item.(*aigv1a1.AIGatewayRoute).Name)
+	require.Equal(t, "default", item.(*aigv1a1.AIGatewayRoute).Namespace)
+
+	// Do it for the second time with a slightly different configuration.
+	current := item.(*aigv1a1.AIGatewayRoute)
+	current.Spec.TargetRefs = []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{
+		{LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{Name: "mytarget"}},
+	}
+	err = cl.Update(context.Background(), current)
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "myroute"}})
+	require.NoError(t, err)
+	item, ok = <-ch
+	require.True(t, ok)
+	require.IsType(t, &aigv1a1.AIGatewayRoute{}, item)
+	r := item.(*aigv1a1.AIGatewayRoute)
+	require.Equal(t, "myroute", r.Name)
+	require.Equal(t, "default", r.Namespace)
+	require.Len(t, r.Spec.TargetRefs, 1)
+	require.Equal(t, "mytarget", string(r.Spec.TargetRefs[0].Name))
+
+	// Test the case where the AIGatewayRoute is being deleted.
+	err = cl.Delete(context.Background(), &aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: "myroute", Namespace: "default"}})
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "myroute"}})
+	require.NoError(t, err)
+}
+
 func Test_extProcName(t *testing.T) {
-	actual := extProcName(&aigv1a1.AIGatewayRoute{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "myroute",
-		},
-	})
+	actual := extProcName(&aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: "myroute"}})
 	require.Equal(t, "ai-eg-route-extproc-myroute", actual)
 }
 
diff --git a/internal/controller/ai_service_backend_test.go b/internal/controller/ai_service_backend_test.go
index 06cf088..1806e68 100644
--- a/internal/controller/ai_service_backend_test.go
+++ b/internal/controller/ai_service_backend_test.go
@@ -33,6 +33,12 @@ func TestAIServiceBackendController_Reconcile(t *testing.T) {
 	require.IsType(t, &aigv1a1.AIServiceBackend{}, item)
 	require.Equal(t, "mybackend", item.(*aigv1a1.AIServiceBackend).Name)
 	require.Equal(t, "default", item.(*aigv1a1.AIServiceBackend).Namespace)
+
+	// Test the case where the AIServiceBackend is being deleted.
+	err = cl.Delete(context.Background(), &aigv1a1.AIServiceBackend{ObjectMeta: metav1.ObjectMeta{Name: "mybackend", Namespace: "default"}})
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "mybackend"}})
+	require.NoError(t, err)
 }
 
 func Test_AiServiceBackendIndexFunc(t *testing.T) {
diff --git a/internal/controller/backend_security_policy_test.go b/internal/controller/backend_security_policy_test.go
index c47f0f4..0f8d861 100644
--- a/internal/controller/backend_security_policy_test.go
+++ b/internal/controller/backend_security_policy_test.go
@@ -31,4 +31,10 @@ func TestBackendSecurityController_Reconcile(t *testing.T) {
 	require.IsType(t, &aigv1a1.BackendSecurityPolicy{}, item)
 	require.Equal(t, backendSecurityPolicyName, item.(*aigv1a1.BackendSecurityPolicy).Name)
 	require.Equal(t, namespace, item.(*aigv1a1.BackendSecurityPolicy).Namespace)
+
+	// Test the case where the BackendSecurityPolicy is being deleted.
+	err = cl.Delete(context.Background(), &aigv1a1.BackendSecurityPolicy{ObjectMeta: metav1.ObjectMeta{Name: backendSecurityPolicyName, Namespace: namespace}})
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: namespace, Name: backendSecurityPolicyName}})
+	require.NoError(t, err)
 }
diff --git a/internal/controller/secret_test.go b/internal/controller/secret_test.go
index 0a606fe..086dd6d 100644
--- a/internal/controller/secret_test.go
+++ b/internal/controller/secret_test.go
@@ -33,4 +33,14 @@ func TestSecretController_Reconcile(t *testing.T) {
 	item, ok := <-ch
 	require.True(t, ok)
 	require.Equal(t, ConfigSinkEventSecretUpdate{Namespace: "default", Name: "mysecret"}, item)
+
+	// Test the case where the Secret is being deleted.
+	err = cl.Delete(context.Background(), &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{Name: "mysecret", Namespace: "default"},
+	})
+	require.NoError(t, err)
+	_, err = c.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{
+		Namespace: "default", Name: "mysecret",
+	}})
+	require.NoError(t, err)
 }
diff --git a/internal/controller/sink_test.go b/internal/controller/sink_test.go
index 9d319b0..7addc24 100644
--- a/internal/controller/sink_test.go
+++ b/internal/controller/sink_test.go
@@ -28,17 +28,37 @@ import (
 	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
-func TestConfigSink_init(t *testing.T) {
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+func requireNewFakeClientWithIndexes(t *testing.T) client.Client {
+	builder := fake.NewClientBuilder().WithScheme(scheme)
+	err := applyIndexing(func(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
+		builder = builder.WithIndex(obj, field, extractValue)
+		return nil
+	})
+	require.NoError(t, err)
+	return builder.Build()
+}
+
+func TestConfigSink_handleEvent(t *testing.T) {
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent)
 	s := newConfigSink(fakeClient, kube, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
 	require.NotNil(t, s)
+
+	_, err := kube.CoreV1().ConfigMaps("ns").Create(context.Background(), &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{Name: "ai-eg-route-extproc-apple", Namespace: "ns"},
+	}, metav1.CreateOptions{})
+	require.NoError(t, err)
+
+	s.handleEvent(ConfigSinkEventSecretUpdate{Namespace: "ns", Name: "some-secret"})
+	s.handleEvent(&aigv1a1.AIServiceBackend{ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns"}})
+	s.handleEvent(&aigv1a1.BackendSecurityPolicy{ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns"}})
+	s.handleEvent(&aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns"}})
 }
 
 func TestConfigSink_syncAIGatewayRoute(t *testing.T) {
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent, 10)
@@ -109,31 +129,51 @@ func TestConfigSink_syncAIGatewayRoute(t *testing.T) {
 
 func TestConfigSink_syncAIServiceBackend(t *testing.T) {
 	eventChan := make(chan ConfigSinkEvent)
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
+	// Create the AI Gateway Route that references the backend.
+	route := &aigv1a1.AIGatewayRoute{
+		ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "ns1"},
+		Spec: aigv1a1.AIGatewayRouteSpec{
+			Rules: []aigv1a1.AIGatewayRouteRule{
+				{
+					BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{{Name: "apple", Weight: 1}},
+				},
+			},
+		},
+	}
+	require.NoError(t, fakeClient.Create(context.Background(), route, &client.CreateOptions{}))
+
 	s := newConfigSink(fakeClient, nil, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
-	s.syncAIServiceBackend(&aigv1a1.AIServiceBackend{ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns1"}})
+	s.syncAIServiceBackend(&aigv1a1.AIServiceBackend{
+		ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns1"},
+		Spec: aigv1a1.AIServiceBackendSpec{
+			BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend", Namespace: ptr.To[gwapiv1.Namespace]("ns1")},
+		},
+	})
 }
 
 func TestConfigSink_syncBackendSecurityPolicy(t *testing.T) {
 	eventChan := make(chan ConfigSinkEvent)
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 
 	backend := aigv1a1.AIServiceBackend{
 		ObjectMeta: metav1.ObjectMeta{Name: "tomato", Namespace: "ns"},
 		Spec: aigv1a1.AIServiceBackendSpec{
 			BackendRef:               gwapiv1.BackendObjectReference{Name: "some-backend", Namespace: ptr.To[gwapiv1.Namespace]("ns")},
-			BackendSecurityPolicyRef: &gwapiv1.LocalObjectReference{Name: "new-backend-security-policy"},
+			BackendSecurityPolicyRef: &gwapiv1.LocalObjectReference{Name: "apple"},
 		},
 	}
 	require.NoError(t, fakeClient.Create(context.Background(), &backend, &client.CreateOptions{}))
 
 	s := newConfigSink(fakeClient, nil, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
-	s.syncBackendSecurityPolicy(&aigv1a1.BackendSecurityPolicy{ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns"}})
+	s.syncBackendSecurityPolicy(&aigv1a1.BackendSecurityPolicy{
+		ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: "ns"},
+	})
 }
 
 func Test_newHTTPRoute(t *testing.T) {
 	eventChan := make(chan ConfigSinkEvent)
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	s := newConfigSink(fakeClient, nil, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
 	httpRoute := &gwapiv1.HTTPRoute{
 		ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "ns1"},
@@ -142,6 +182,13 @@ func Test_newHTTPRoute(t *testing.T) {
 	aiGatewayRoute := &aigv1a1.AIGatewayRoute{
 		ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "ns1"},
 		Spec: aigv1a1.AIGatewayRouteSpec{
+			TargetRefs: []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{
+				{
+					LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{
+						Name: "gtw", Kind: "Gateway", Group: "gateway.networking.k8s.io",
+					},
+				},
+			},
 			Rules: []aigv1a1.AIGatewayRouteRule{
 				{
 					BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{{Name: "apple", Weight: 100}},
@@ -239,7 +286,7 @@ func Test_newHTTPRoute(t *testing.T) {
 }
 
 func Test_updateExtProcConfigMap(t *testing.T) {
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent)
@@ -435,7 +482,7 @@ func Test_updateExtProcConfigMap(t *testing.T) {
 }
 
 func TestConfigSink_SyncExtprocDeployment(t *testing.T) {
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent)
@@ -599,25 +646,21 @@ func TestConfigSink_SyncExtprocDeployment(t *testing.T) {
 
 func TestConfigSink_MountBackendSecurityPolicySecrets(t *testing.T) {
 	// Create simple case
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent)
 	s := newConfigSink(fakeClient, kube, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
-	err := s.init(context.Background())
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	err := s.init(ctx)
 	require.NoError(t, err)
 	require.NoError(t, fakeClient.Create(context.Background(), &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy"}}))
 
 	for _, secret := range []*corev1.Secret{
-		{
-			ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-1"},
-		},
-		{
-			ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-2"},
-		},
-		{
-			ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-3"},
-		},
+		{ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-1"}},
+		{ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-2"}},
+		{ObjectMeta: metav1.ObjectMeta{Name: "some-secret-policy-3"}},
 	} {
 		require.NoError(t, fakeClient.Create(context.Background(), secret, &client.CreateOptions{}))
 	}
@@ -786,7 +829,7 @@ func Test_backendSecurityPolicyVolumeName(t *testing.T) {
 }
 
 func Test_annotateExtProcPods(t *testing.T) {
-	fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+	fakeClient := requireNewFakeClientWithIndexes(t)
 	kube := fake2.NewClientset()
 
 	eventChan := make(chan ConfigSinkEvent)
@@ -820,3 +863,18 @@ func Test_annotateExtProcPods(t *testing.T) {
 		require.Equal(t, uuid, pod.Annotations[extProcConfigAnnotationKey])
 	}
 }
+
+func Test_syncSecret(t *testing.T) {
+	fakeClient := requireNewFakeClientWithIndexes(t)
+	kube := fake2.NewClientset()
+
+	eventChan := make(chan ConfigSinkEvent)
+	s := newConfigSink(fakeClient, kube, logr.Discard(), eventChan, "defaultExtProcImage", "debug")
+
+	_, err := kube.CoreV1().Secrets("ns").Create(context.Background(), &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{Name: "some-secret", Namespace: "ns"},
+		Data:       map[string][]byte{"key": []byte("value")},
+	}, metav1.CreateOptions{})
+	require.NoError(t, err)
+	s.syncSecret("ns", "some-secret")
+}