This is an automated email from the ASF dual-hosted git repository.

jimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-seata-k8s.git


The following commit(s) were added to refs/heads/master by this push:
     new 6813165  optimize: add some UT for controllers (#48)
6813165 is described below

commit 6813165ae69cff0e78c3b8216457396b81958a2e
Author: jimin <[email protected]>
AuthorDate: Sat Jan 10 11:15:02 2026 +0800

    optimize: add some UT for controllers (#48)
---
 controllers/seataserver_controller_test.go | 691 +++++++++++++++++++++++++++++
 pkg/finalizer/cleanup_handler_test.go      | 338 ++++++++++++++
 pkg/seata/fetchers_test.go                 | 429 ++++++++++++++++++
 pkg/seata/generators_test.go               | 342 ++++++++++++++
 pkg/seata/synchronizers_test.go            | 264 +++++++++++
 pkg/utils/utils_test.go                    | 360 +++++++++++++++
 6 files changed, 2424 insertions(+)

diff --git a/controllers/seataserver_controller_test.go 
b/controllers/seataserver_controller_test.go
new file mode 100644
index 0000000..dbe4097
--- /dev/null
+++ b/controllers/seataserver_controller_test.go
@@ -0,0 +1,691 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import (
+       "context"
+       "testing"
+       "time"
+
+       "github.com/go-logr/logr"
+       appsv1 "k8s.io/api/apps/v1"
+       apiv1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/resource"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/types"
+       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"
+
+       seatav1alpha1 "github.com/apache/seata-k8s/api/v1alpha1"
+)
+
+func TestSeataServerReconciler_Reconcile(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ContainerSpec: seatav1alpha1.ContainerSpec{
+                               ContainerName: "seata-server",
+                               Image:         "apache/seata-server:latest",
+                               Resources: apiv1.ResourceRequirements{
+                                       Requests: apiv1.ResourceList{
+                                               apiv1.ResourceCPU:    
resource.MustParse("500m"),
+                                               apiv1.ResourceMemory: 
resource.MustParse("1Gi"),
+                                       },
+                               },
+                       },
+                       ServiceName: "seata-cluster",
+                       Replicas:    3,
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+                       Persistence: seatav1alpha1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1alpha1.VolumeReclaimPolicyRetain,
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               WithStatusSubresource(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       req := ctrl.Request{
+               NamespacedName: types.NamespacedName{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       result, err := reconciler.Reconcile(context.Background(), req)
+       if err != nil {
+               t.Errorf("Reconcile failed: %v", err)
+       }
+
+       if result.Requeue {
+               t.Log("Result requested requeue, which is expected for 
unsynchronized servers")
+       }
+}
+
+func TestSeataServerReconciler_Reconcile_NotFound(t *testing.T) {
+       scheme := createTestScheme()
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       req := ctrl.Request{
+               NamespacedName: types.NamespacedName{
+                       Name:      "nonexistent",
+                       Namespace: "default",
+               },
+       }
+
+       result, err := reconciler.Reconcile(context.Background(), req)
+       if err != nil {
+               t.Errorf("Reconcile should not return error for not found: %v", 
err)
+       }
+
+       if result.Requeue {
+               t.Error("Result should not request requeue for not found")
+       }
+}
+
+func TestSeataServerReconciler_ReconcileHeadlessService(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ServiceName: "seata-cluster",
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileHeadlessService(context.Background(), 
seataServer)
+       if err != nil {
+               t.Errorf("reconcileHeadlessService failed: %v", err)
+       }
+
+       // Verify service was created
+       svc := &apiv1.Service{}
+       err = fakeClient.Get(context.Background(), types.NamespacedName{
+               Name:      "seata-cluster",
+               Namespace: "default",
+       }, svc)
+
+       if err != nil {
+               t.Errorf("Failed to get created service: %v", err)
+       }
+
+       if svc.Spec.ClusterIP != "None" {
+               t.Errorf("Expected headless service (ClusterIP=None), got 
ClusterIP=%s", svc.Spec.ClusterIP)
+       }
+}
+
+func TestSeataServerReconciler_ReconcileStatefulSet(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ContainerSpec: seatav1alpha1.ContainerSpec{
+                               ContainerName: "seata-server",
+                               Image:         "apache/seata-server:latest",
+                       },
+                       ServiceName: "seata-cluster",
+                       Replicas:    3,
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+                       Persistence: seatav1alpha1.Persistence{
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               WithStatusSubresource(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileStatefulSet(context.Background(), 
seataServer)
+       if err != nil {
+               t.Errorf("reconcileStatefulSet failed: %v", err)
+       }
+
+       // Verify StatefulSet was created
+       sts := &appsv1.StatefulSet{}
+       err = fakeClient.Get(context.Background(), types.NamespacedName{
+               Name:      "test-seata",
+               Namespace: "default",
+       }, sts)
+
+       if err != nil {
+               t.Errorf("Failed to get created StatefulSet: %v", err)
+       }
+
+       if *sts.Spec.Replicas != 3 {
+               t.Errorf("Expected 3 replicas, got %d", *sts.Spec.Replicas)
+       }
+}
+
+func TestSeataServerReconciler_UpdateStatefulSet(t *testing.T) {
+       scheme := createTestScheme()
+       replicas3 := int32(3)
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ContainerSpec: seatav1alpha1.ContainerSpec{
+                               ContainerName: "seata-server",
+                               Image:         "apache/seata-server:v2",
+                       },
+                       ServiceName: "seata-cluster",
+                       Replicas:    3,
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+                       Persistence: seatav1alpha1.Persistence{
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       existingSts := &appsv1.StatefulSet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas3,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {
+                                                       Name:  "seata-server",
+                                                       Image: 
"apache/seata-server:v1",
+                                               },
+                                       },
+                               },
+                       },
+               },
+               Status: appsv1.StatefulSetStatus{
+                       Replicas:      3,
+                       ReadyReplicas: 3,
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, existingSts).
+               WithStatusSubresource(seataServer, existingSts).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileStatefulSet(context.Background(), 
seataServer)
+       if err != nil {
+               t.Errorf("reconcileStatefulSet failed during update: %v", err)
+       }
+}
+
+func TestSeataServerReconciler_ReconcileFinalizers_Delete(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:              "test-seata",
+                       Namespace:         "default",
+                       UID:               types.UID("test-uid-123"),
+                       DeletionTimestamp: &metav1.Time{Time: time.Now()},
+                       Finalizers:        []string{"cleanUpSeataPVC"},
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas: 1,
+                       Persistence: seatav1alpha1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1alpha1.VolumeReclaimPolicyDelete,
+                       },
+               },
+       }
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc-0",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileFinalizers(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("reconcileFinalizers failed: %v", err)
+       }
+}
+
+func TestSeataServerReconciler_ReconcileFinalizers_Retain(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas: 1,
+                       Persistence: seatav1alpha1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1alpha1.VolumeReclaimPolicyRetain,
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileFinalizers(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("reconcileFinalizers with Retain policy failed: %v", 
err)
+       }
+}
+
+func TestSeataServerReconciler_CleanupOrphanPVCs(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas: 2,
+               },
+               Status: seatav1alpha1.SeataServerStatus{
+                       ReadyReplicas: 2,
+               },
+       }
+
+       // PVC 0 and 1 are in use, PVC 2 is orphan
+       pvc0 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "seata-store-test-seata-0",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       pvc1 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "seata-store-test-seata-1",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       pvc2 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "seata-store-test-seata-2",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc0, pvc1, pvc2).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.cleanupOrphanPVCs(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("cleanupOrphanPVCs failed: %v", err)
+       }
+}
+
+func TestSeataServerReconciler_GetPVCList(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas: 2,
+               },
+       }
+
+       pvc0 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc-0",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       pvc1 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc-1",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc0, pvc1).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       pvcList, err := reconciler.getPVCList(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("getPVCList failed: %v", err)
+       }
+
+       if len(pvcList.Items) != 2 {
+               t.Errorf("Expected 2 PVCs, got %d", len(pvcList.Items))
+       }
+}
+
+func TestSeataServerReconciler_GetPVCCount(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+       }
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       count, err := reconciler.getPVCCount(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("getPVCCount failed: %v", err)
+       }
+
+       if count != 1 {
+               t.Errorf("Expected PVC count 1, got %d", count)
+       }
+}
+
+func TestSeataServerReconciler_RecordError(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Status: seatav1alpha1.SeataServerStatus{
+                       Errors: []seatav1alpha1.SeataServerError{},
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               WithStatusSubresource(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.recordError(
+               context.Background(),
+               types.NamespacedName{Name: "test-seata", Namespace: "default"},
+               seatav1alpha1.ErrorTypeK8s_SeataServer,
+               "Test error message",
+               reconcile.TerminalError(nil),
+       )
+
+       if err != nil {
+               t.Errorf("recordError failed: %v", err)
+       }
+}
+
+func TestSeataServerReconciler_ReconcileClientObject(t *testing.T) {
+       scheme := createTestScheme()
+
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ServiceName: "test-service",
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+               },
+       }
+
+       svc := &apiv1.Service{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-service",
+                       Namespace: "default",
+               },
+               Spec: apiv1.ServiceSpec{
+                       Ports: []apiv1.ServicePort{
+                               {Name: "service-port", Port: 8091},
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               Build()
+
+       reconciler := &SeataServerReconciler{
+               Client: fakeClient,
+               Scheme: scheme,
+               Log:    logr.Discard(),
+       }
+
+       err := reconciler.reconcileClientObject(
+               context.Background(),
+               seataServer,
+               svc,
+               func() client.Object { return &apiv1.Service{} },
+               func(found, desired client.Object) {},
+               seatav1alpha1.ErrorTypeK8s_HeadlessService,
+               "Service",
+       )
+
+       if err != nil {
+               t.Errorf("reconcileClientObject failed: %v", err)
+       }
+
+       // Verify service was created
+       createdSvc := &apiv1.Service{}
+       err = fakeClient.Get(context.Background(), types.NamespacedName{
+               Name:      "test-service",
+               Namespace: "default",
+       }, createdSvc)
+
+       if err != nil {
+               t.Errorf("Failed to get created service: %v", err)
+       }
+}
+
+func createTestScheme() *runtime.Scheme {
+       scheme := runtime.NewScheme()
+       _ = apiv1.AddToScheme(scheme)
+       _ = appsv1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+       return scheme
+}
+
diff --git a/pkg/finalizer/cleanup_handler_test.go 
b/pkg/finalizer/cleanup_handler_test.go
new file mode 100644
index 0000000..b6c04a0
--- /dev/null
+++ b/pkg/finalizer/cleanup_handler_test.go
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package finalizer
+
+import (
+       "context"
+       "testing"
+
+       "github.com/go-logr/logr"
+       apiv1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/resource"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/types"
+       "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+       seatav1 "github.com/apache/seata-k8s/api/v1"
+       seatav1alpha1 "github.com/apache/seata-k8s/api/v1alpha1"
+)
+
+func TestNewCleanupHandler(t *testing.T) {
+       scheme := createCleanupTestScheme()
+       fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+       logger := logr.Discard()
+
+       handler := NewCleanupHandler(fakeClient, logger)
+
+       if handler == nil {
+               t.Fatal("NewCleanupHandler should not return nil")
+       }
+       if handler.Client == nil {
+               t.Error("CleanupHandler.Client should not be nil")
+       }
+}
+
+func TestHandleCleanup_V1(t *testing.T) {
+       scheme := createCleanupTestScheme()
+       
+       seataServer := &seatav1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1.SeataServerSpec{
+                       Persistence: seatav1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1.VolumeReclaimPolicyDelete,
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.HandleCleanup(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("HandleCleanup failed: %v", err)
+       }
+}
+
+func TestHandleCleanup_V1Alpha1(t *testing.T) {
+       scheme := createCleanupTestScheme()
+       
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Persistence: seatav1alpha1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1alpha1.VolumeReclaimPolicyDelete,
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer, pvc).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.HandleCleanup(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("HandleCleanup failed: %v", err)
+       }
+}
+
+func TestHandleCleanup_UnsupportedType(t *testing.T) {
+       scheme := createCleanupTestScheme()
+       fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       // Pass an unsupported type
+       err := handler.HandleCleanup(context.Background(), "unsupported")
+       if err == nil {
+               t.Error("HandleCleanup should return error for unsupported 
type")
+       }
+}
+
+func TestHandleCleanup_V1_RetainPolicy(t *testing.T) {
+       scheme := createCleanupTestScheme()
+       
+       seataServer := &seatav1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+               },
+               Spec: seatav1.SeataServerSpec{
+                       Persistence: seatav1.Persistence{
+                               VolumeReclaimPolicy: 
seatav1.VolumeReclaimPolicyRetain,
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(seataServer).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.HandleCleanup(context.Background(), seataServer)
+       if err != nil {
+               t.Errorf("HandleCleanup with Retain policy failed: %v", err)
+       }
+}
+
+func TestCleanupAllPVCs(t *testing.T) {
+       scheme := createCleanupTestScheme()
+
+       pvc1 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc-1",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       pvc2 := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc-2",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(pvc1, pvc2).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.cleanupAllPVCs(context.Background(), "test-seata", 
"default", types.UID("test-uid-123"))
+       if err != nil {
+               t.Errorf("cleanupAllPVCs failed: %v", err)
+       }
+}
+
+func TestGetPVCList(t *testing.T) {
+       scheme := createCleanupTestScheme()
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                               "uid": "test-uid-123",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(pvc).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       pvcList, err := handler.getPVCList(context.Background(), "test-seata", 
"default", types.UID("test-uid-123"))
+       if err != nil {
+               t.Errorf("getPVCList failed: %v", err)
+       }
+
+       if len(pvcList.Items) != 1 {
+               t.Errorf("Expected 1 PVC, got %d", len(pvcList.Items))
+       }
+}
+
+func TestDeletePVC(t *testing.T) {
+       scheme := createCleanupTestScheme()
+
+       pvc := &apiv1.PersistentVolumeClaim{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-pvc",
+                       Namespace: "default",
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(pvc).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.deletePVC(context.Background(), pvc)
+       if err != nil {
+               t.Errorf("deletePVC failed: %v", err)
+       }
+}
+
+func TestCleanupConfigMaps(t *testing.T) {
+       scheme := createCleanupTestScheme()
+
+       cm := &apiv1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-cm",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(cm).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.cleanupConfigMaps(context.Background(), "test-seata", 
"default")
+       if err != nil {
+               t.Errorf("cleanupConfigMaps failed: %v", err)
+       }
+}
+
+func TestCleanupSecrets(t *testing.T) {
+       scheme := createCleanupTestScheme()
+
+       secret := &apiv1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-secret",
+                       Namespace: "default",
+                       Labels: map[string]string{
+                               "app": "test-seata",
+                       },
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(secret).
+               Build()
+
+       handler := NewCleanupHandler(fakeClient, logr.Discard())
+
+       err := handler.cleanupSecrets(context.Background(), "test-seata", 
"default")
+       if err != nil {
+               t.Errorf("cleanupSecrets failed: %v", err)
+       }
+}
+
+func createCleanupTestScheme() *runtime.Scheme {
+       scheme := runtime.NewScheme()
+       _ = apiv1.AddToScheme(scheme)
+       _ = seatav1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+       return scheme
+}
+
diff --git a/pkg/seata/fetchers_test.go b/pkg/seata/fetchers_test.go
new file mode 100644
index 0000000..fbec08e
--- /dev/null
+++ b/pkg/seata/fetchers_test.go
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package seata
+
+import (
+       "context"
+       "testing"
+
+       seatav1alpha1 "github.com/apache/seata-k8s/api/v1alpha1"
+       v1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func TestFetchEnvVar_DirectValue(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+       
+       fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
+       
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       envVar := v1.EnvVar{
+               Name:  "TEST_VAR",
+               Value: "test-value",
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "test-value" {
+               t.Errorf("Expected 'test-value', got '%s'", result)
+       }
+}
+
+func TestFetchEnvVar_FromConfigMap(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       configMap := &v1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-cm",
+                       Namespace: "default",
+               },
+               Data: map[string]string{
+                       "username": "admin",
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(configMap).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-cm",
+                               },
+                               Key: "username",
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "admin" {
+               t.Errorf("Expected 'admin', got '%s'", result)
+       }
+}
+
+func TestFetchEnvVar_FromConfigMap_KeyNotFound(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       configMap := &v1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-cm",
+                       Namespace: "default",
+               },
+               Data: map[string]string{
+                       "username": "admin",
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(configMap).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-cm",
+                               },
+                               Key: "nonexistent",
+                       },
+               },
+       }
+
+       _, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err == nil {
+               t.Error("Expected error for non-existent key, got nil")
+       }
+}
+
+func TestFetchEnvVar_FromConfigMap_Optional(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       configMap := &v1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-cm",
+                       Namespace: "default",
+               },
+               Data: map[string]string{
+                       "username": "admin",
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(configMap).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       optional := true
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-cm",
+                               },
+                               Key:      "nonexistent",
+                               Optional: &optional,
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "" {
+               t.Errorf("Expected empty string for optional missing key, got 
'%s'", result)
+       }
+}
+
+func TestFetchEnvVar_FromSecret(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       secret := &v1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-secret",
+                       Namespace: "default",
+               },
+               Data: map[string][]byte{
+                       "password": []byte("secret123"),
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(secret).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       SecretKeyRef: &v1.SecretKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-secret",
+                               },
+                               Key: "password",
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "secret123" {
+               t.Errorf("Expected 'secret123', got '%s'", result)
+       }
+}
+
+func TestFetchEnvVar_FromSecret_KeyNotFound(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       secret := &v1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-secret",
+                       Namespace: "default",
+               },
+               Data: map[string][]byte{
+                       "password": []byte("secret123"),
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(secret).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       SecretKeyRef: &v1.SecretKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-secret",
+                               },
+                               Key: "nonexistent",
+                       },
+               },
+       }
+
+       _, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err == nil {
+               t.Error("Expected error for non-existent key, got nil")
+       }
+}
+
+func TestFetchEnvVar_FromSecret_Optional(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       secret := &v1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-secret",
+                       Namespace: "default",
+               },
+               Data: map[string][]byte{
+                       "password": []byte("secret123"),
+               },
+       }
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               WithObjects(secret).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       optional := true
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       SecretKeyRef: &v1.SecretKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "test-secret",
+                               },
+                               Key:      "nonexistent",
+                               Optional: &optional,
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "" {
+               t.Errorf("Expected empty string for optional missing key, got 
'%s'", result)
+       }
+}
+
+func TestFetchEnvVar_ConfigMapNotFound_Optional(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       optional := true
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "nonexistent-cm",
+                               },
+                               Key:      "key",
+                               Optional: &optional,
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "" {
+               t.Errorf("Expected empty string for optional missing ConfigMap, 
got '%s'", result)
+       }
+}
+
+func TestFetchEnvVar_SecretNotFound_Optional(t *testing.T) {
+       scheme := runtime.NewScheme()
+       _ = v1.AddToScheme(scheme)
+       _ = seatav1alpha1.AddToScheme(scheme)
+
+       fakeClient := fake.NewClientBuilder().
+               WithScheme(scheme).
+               Build()
+
+       cr := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+       }
+
+       optional := true
+       envVar := v1.EnvVar{
+               Name: "TEST_VAR",
+               ValueFrom: &v1.EnvVarSource{
+                       SecretKeyRef: &v1.SecretKeySelector{
+                               LocalObjectReference: v1.LocalObjectReference{
+                                       Name: "nonexistent-secret",
+                               },
+                               Key:      "key",
+                               Optional: &optional,
+                       },
+               },
+       }
+
+       result, err := FetchEnvVar(context.Background(), fakeClient, cr, envVar)
+       if err != nil {
+               t.Errorf("FetchEnvVar failed: %v", err)
+       }
+
+       if result != "" {
+               t.Errorf("Expected empty string for optional missing Secret, 
got '%s'", result)
+       }
+}
+
diff --git a/pkg/seata/generators_test.go b/pkg/seata/generators_test.go
new file mode 100644
index 0000000..3abfb6d
--- /dev/null
+++ b/pkg/seata/generators_test.go
@@ -0,0 +1,342 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package seata
+
+import (
+       "strings"
+       "testing"
+
+       seatav1alpha1 "github.com/apache/seata-k8s/api/v1alpha1"
+       apiv1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/resource"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/types"
+)
+
+func TestMakeLabels(t *testing.T) {
+       name := "test-seata"
+       labels := makeLabels(name)
+
+       if labels["cr_name"] != name {
+               t.Errorf("Expected label cr_name='%s', got '%s'", name, 
labels["cr_name"])
+       }
+}
+
+func TestMakeHeadlessService(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ServiceName: "seata-cluster",
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+               },
+       }
+
+       svc := MakeHeadlessService(seataServer)
+
+       if svc.Name != "seata-cluster" {
+               t.Errorf("Expected service name 'seata-cluster', got '%s'", 
svc.Name)
+       }
+
+       if svc.Namespace != "default" {
+               t.Errorf("Expected namespace 'default', got '%s'", 
svc.Namespace)
+       }
+
+       if svc.Spec.ClusterIP != "None" {
+               t.Errorf("Expected ClusterIP 'None', got '%s'", 
svc.Spec.ClusterIP)
+       }
+
+       if len(svc.Spec.Ports) != 3 {
+               t.Errorf("Expected 3 ports, got %d", len(svc.Spec.Ports))
+       }
+
+       // Verify ports
+       portMap := make(map[string]int32)
+       for _, port := range svc.Spec.Ports {
+               portMap[port.Name] = port.Port
+       }
+
+       if portMap["service-port"] != 8091 {
+               t.Errorf("Expected service-port 8091, got %d", 
portMap["service-port"])
+       }
+       if portMap["console-port"] != 7091 {
+               t.Errorf("Expected console-port 7091, got %d", 
portMap["console-port"])
+       }
+       if portMap["raft-port"] != 9091 {
+               t.Errorf("Expected raft-port 9091, got %d", 
portMap["raft-port"])
+       }
+}
+
+func TestMakeStatefulSet(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+                       UID:       types.UID("test-uid-123"),
+                       Labels: map[string]string{
+                               "app": "seata",
+                       },
+                       Annotations: map[string]string{
+                               "description": "test seata server",
+                       },
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ContainerSpec: seatav1alpha1.ContainerSpec{
+                               ContainerName: "seata-server",
+                               Image:         "apache/seata-server:latest",
+                               Resources: apiv1.ResourceRequirements{
+                                       Requests: apiv1.ResourceList{
+                                               apiv1.ResourceCPU:    
resource.MustParse("500m"),
+                                               apiv1.ResourceMemory: 
resource.MustParse("1Gi"),
+                                       },
+                               },
+                       },
+                       ServiceName: "seata-cluster",
+                       Replicas:    3,
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+                       Persistence: seatav1alpha1.Persistence{
+                               PersistentVolumeClaimSpec: 
apiv1.PersistentVolumeClaimSpec{
+                                       Resources: apiv1.ResourceRequirements{
+                                               Requests: apiv1.ResourceList{
+                                                       apiv1.ResourceStorage: 
resource.MustParse("5Gi"),
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       sts := MakeStatefulSet(seataServer)
+
+       if sts.Name != "test-seata" {
+               t.Errorf("Expected StatefulSet name 'test-seata', got '%s'", 
sts.Name)
+       }
+
+       if sts.Namespace != "default" {
+               t.Errorf("Expected namespace 'default', got '%s'", 
sts.Namespace)
+       }
+
+       if *sts.Spec.Replicas != 3 {
+               t.Errorf("Expected 3 replicas, got %d", *sts.Spec.Replicas)
+       }
+
+       if sts.Spec.ServiceName != "seata-cluster" {
+               t.Errorf("Expected service name 'seata-cluster', got '%s'", 
sts.Spec.ServiceName)
+       }
+
+       // Check VolumeClaimTemplates
+       if len(sts.Spec.VolumeClaimTemplates) != 1 {
+               t.Fatalf("Expected 1 VolumeClaimTemplate, got %d", 
len(sts.Spec.VolumeClaimTemplates))
+       }
+
+       pvc := sts.Spec.VolumeClaimTemplates[0]
+       if pvc.Name != "seata-store" {
+               t.Errorf("Expected PVC name 'seata-store', got '%s'", pvc.Name)
+       }
+
+       if pvc.Labels["app"] != "test-seata" {
+               t.Errorf("Expected PVC label app='test-seata', got '%s'", 
pvc.Labels["app"])
+       }
+
+       if pvc.Labels["uid"] != "test-uid-123" {
+               t.Errorf("Expected PVC label uid='test-uid-123', got '%s'", 
pvc.Labels["uid"])
+       }
+
+       // Check container
+       if len(sts.Spec.Template.Spec.Containers) != 1 {
+               t.Fatalf("Expected 1 container, got %d", 
len(sts.Spec.Template.Spec.Containers))
+       }
+
+       container := sts.Spec.Template.Spec.Containers[0]
+       if container.Name != "seata-server" {
+               t.Errorf("Expected container name 'seata-server', got '%s'", 
container.Name)
+       }
+
+       if container.Image != "apache/seata-server:latest" {
+               t.Errorf("Expected image 'apache/seata-server:latest', got 
'%s'", container.Image)
+       }
+
+       // Check ports
+       if len(container.Ports) != 3 {
+               t.Fatalf("Expected 3 container ports, got %d", 
len(container.Ports))
+       }
+
+       // Check volume mounts
+       if len(container.VolumeMounts) != 1 {
+               t.Fatalf("Expected 1 volume mount, got %d", 
len(container.VolumeMounts))
+       }
+
+       if container.VolumeMounts[0].Name != "seata-store" {
+               t.Errorf("Expected volume mount name 'seata-store', got '%s'", 
container.VolumeMounts[0].Name)
+       }
+
+       if container.VolumeMounts[0].MountPath != "/seata-server/sessionStore" {
+               t.Errorf("Expected mount path '/seata-server/sessionStore', got 
'%s'", container.VolumeMounts[0].MountPath)
+       }
+}
+
+func TestBuildEntrypointScript(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ServiceName: "seata-cluster",
+               },
+       }
+
+       script := buildEntrypointScript(seataServer)
+
+       if !strings.Contains(script, "SEATA_IP=$(HOST_NAME).seata-cluster") {
+               t.Error("Script should contain SEATA_IP export")
+       }
+
+       if !strings.Contains(script, "python3 -c") {
+               t.Error("Script should contain python3 command")
+       }
+
+       if !strings.Contains(script, "/seata-server-entrypoint.sh") {
+               t.Error("Script should contain seata-server-entrypoint.sh")
+       }
+}
+
+func TestBuildEnvVars(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       ContainerSpec: seatav1alpha1.ContainerSpec{
+                               Env: []apiv1.EnvVar{
+                                       {Name: "CUSTOM_ENV", Value: 
"custom-value"},
+                               },
+                       },
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+               },
+       }
+
+       envs := buildEnvVars(seataServer)
+
+       // Check that we have at least the base envs + custom env
+       if len(envs) < 5 {
+               t.Errorf("Expected at least 5 env vars, got %d", len(envs))
+       }
+
+       // Create a map for easier checking
+       envMap := make(map[string]string)
+       for _, env := range envs {
+               if env.ValueFrom == nil {
+                       envMap[env.Name] = env.Value
+               }
+       }
+
+       // Check standard environment variables
+       if envMap["store.mode"] != "raft" {
+               t.Errorf("Expected store.mode='raft', got '%s'", 
envMap["store.mode"])
+       }
+
+       if envMap["server.port"] != "7091" {
+               t.Errorf("Expected server.port='7091', got '%s'", 
envMap["server.port"])
+       }
+
+       if envMap["server.servicePort"] != "8091" {
+               t.Errorf("Expected server.servicePort='8091', got '%s'", 
envMap["server.servicePort"])
+       }
+
+       // Check custom env
+       if envMap["CUSTOM_ENV"] != "custom-value" {
+               t.Errorf("Expected CUSTOM_ENV='custom-value', got '%s'", 
envMap["CUSTOM_ENV"])
+       }
+
+       // Check HOST_NAME has ValueFrom
+       hasHostName := false
+       for _, env := range envs {
+               if env.Name == "HOST_NAME" && env.ValueFrom != nil {
+                       hasHostName = true
+                       if env.ValueFrom.FieldRef.FieldPath != "metadata.name" {
+                               t.Errorf("Expected HOST_NAME field path 
'metadata.name', got '%s'", env.ValueFrom.FieldRef.FieldPath)
+                       }
+               }
+       }
+       if !hasHostName {
+               t.Error("Expected HOST_NAME environment variable with 
ValueFrom")
+       }
+
+       // Check server.raft.serverAddr exists
+       if _, exists := envMap["server.raft.serverAddr"]; !exists {
+               t.Error("Expected server.raft.serverAddr environment variable")
+       }
+}
+
+func TestBuildEnvVars_WithMultipleReplicas(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-seata",
+                       Namespace: "default",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas: 3,
+                       Ports: seatav1alpha1.Ports{
+                               ServicePort: 8091,
+                               ConsolePort: 7091,
+                               RaftPort:    9091,
+                       },
+                       ServiceName: "seata-cluster",
+               },
+       }
+
+       envs := buildEnvVars(seataServer)
+
+       envMap := make(map[string]string)
+       for _, env := range envs {
+               if env.ValueFrom == nil {
+                       envMap[env.Name] = env.Value
+               }
+       }
+
+       raftAddr := envMap["server.raft.serverAddr"]
+       // Should contain all 3 replicas in the address
+       expectedSubstrings := []string{
+               "test-seata-0.seata-cluster:9091",
+               "test-seata-1.seata-cluster:9091",
+               "test-seata-2.seata-cluster:9091",
+       }
+
+       for _, substr := range expectedSubstrings {
+               if !strings.Contains(raftAddr, substr) {
+                       t.Errorf("Expected raft address to contain '%s', got 
'%s'", substr, raftAddr)
+               }
+       }
+}
+
diff --git a/pkg/seata/synchronizers_test.go b/pkg/seata/synchronizers_test.go
new file mode 100644
index 0000000..bcae8d2
--- /dev/null
+++ b/pkg/seata/synchronizers_test.go
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package seata
+
+import (
+       "testing"
+
+       appsv1 "k8s.io/api/apps/v1"
+       apiv1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/resource"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestSyncService(t *testing.T) {
+       // Current service
+       curr := &apiv1.Service{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-service",
+                       Namespace: "default",
+               },
+               Spec: apiv1.ServiceSpec{
+                       Ports: []apiv1.ServicePort{
+                               {Name: "old-port", Port: 8080},
+                       },
+               },
+       }
+
+       // Next/desired service
+       next := &apiv1.Service{
+               Spec: apiv1.ServiceSpec{
+                       Ports: []apiv1.ServicePort{
+                               {Name: "service-port", Port: 8091},
+                               {Name: "console-port", Port: 7091},
+                               {Name: "raft-port", Port: 9091},
+                       },
+               },
+       }
+
+       SyncService(curr, next)
+
+       // Verify ports are synced
+       if len(curr.Spec.Ports) != 3 {
+               t.Errorf("Expected 3 ports after sync, got %d", 
len(curr.Spec.Ports))
+       }
+
+       portMap := make(map[string]int32)
+       for _, port := range curr.Spec.Ports {
+               portMap[port.Name] = port.Port
+       }
+
+       if portMap["service-port"] != 8091 {
+               t.Errorf("Expected service-port 8091, got %d", 
portMap["service-port"])
+       }
+       if portMap["console-port"] != 7091 {
+               t.Errorf("Expected console-port 7091, got %d", 
portMap["console-port"])
+       }
+       if portMap["raft-port"] != 9091 {
+               t.Errorf("Expected raft-port 9091, got %d", 
portMap["raft-port"])
+       }
+}
+
+func TestSyncStatefulSet(t *testing.T) {
+       replicas1 := int32(1)
+       replicas3 := int32(3)
+
+       // Current StatefulSet
+       curr := &appsv1.StatefulSet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-sts",
+                       Namespace: "default",
+               },
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas1,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {
+                                                       Name:  "old-container",
+                                                       Image: "old-image:v1",
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       // Next/desired StatefulSet
+       next := &appsv1.StatefulSet{
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas3,
+                       Template: apiv1.PodTemplateSpec{
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Labels: map[string]string{
+                                               "app": "seata",
+                                       },
+                               },
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {
+                                                       Name:  "seata-server",
+                                                       Image: 
"apache/seata-server:latest",
+                                                       Resources: 
apiv1.ResourceRequirements{
+                                                               Requests: 
apiv1.ResourceList{
+                                                                       
apiv1.ResourceCPU:    resource.MustParse("500m"),
+                                                                       
apiv1.ResourceMemory: resource.MustParse("1Gi"),
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       SyncStatefulSet(curr, next)
+
+       // Verify template is synced
+       if len(curr.Spec.Template.Spec.Containers) != 1 {
+               t.Errorf("Expected 1 container after sync, got %d", 
len(curr.Spec.Template.Spec.Containers))
+       }
+
+       container := curr.Spec.Template.Spec.Containers[0]
+       if container.Name != "seata-server" {
+               t.Errorf("Expected container name 'seata-server', got '%s'", 
container.Name)
+       }
+
+       if container.Image != "apache/seata-server:latest" {
+               t.Errorf("Expected image 'apache/seata-server:latest', got 
'%s'", container.Image)
+       }
+
+       // Verify replicas are synced
+       if *curr.Spec.Replicas != 3 {
+               t.Errorf("Expected 3 replicas after sync, got %d", 
*curr.Spec.Replicas)
+       }
+
+       // Verify labels are synced
+       if curr.Spec.Template.Labels["app"] != "seata" {
+               t.Errorf("Expected template label app='seata', got '%s'", 
curr.Spec.Template.Labels["app"])
+       }
+}
+
+func TestSyncStatefulSet_EmptyReplicas(t *testing.T) {
+       replicas1 := int32(1)
+
+       // Current StatefulSet
+       curr := &appsv1.StatefulSet{
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: nil,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {Name: "old", Image: "old:v1"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       // Next StatefulSet
+       next := &appsv1.StatefulSet{
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas1,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {Name: "new", Image: "new:v2"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       SyncStatefulSet(curr, next)
+
+       if curr.Spec.Replicas == nil {
+               t.Error("Expected replicas to be set")
+       } else if *curr.Spec.Replicas != 1 {
+               t.Errorf("Expected 1 replica, got %d", *curr.Spec.Replicas)
+       }
+}
+
+func TestSyncService_EmptyPorts(t *testing.T) {
+       curr := &apiv1.Service{
+               Spec: apiv1.ServiceSpec{
+                       Ports: []apiv1.ServicePort{
+                               {Name: "port1", Port: 8080},
+                               {Name: "port2", Port: 9090},
+                       },
+               },
+       }
+
+       next := &apiv1.Service{
+               Spec: apiv1.ServiceSpec{
+                       Ports: []apiv1.ServicePort{},
+               },
+       }
+
+       SyncService(curr, next)
+
+       if len(curr.Spec.Ports) != 0 {
+               t.Errorf("Expected 0 ports after sync, got %d", 
len(curr.Spec.Ports))
+       }
+}
+
+func TestSyncStatefulSet_WithMultipleContainers(t *testing.T) {
+       replicas2 := int32(2)
+
+       curr := &appsv1.StatefulSet{
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas2,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {Name: "container1", Image: 
"image1:v1"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       next := &appsv1.StatefulSet{
+               Spec: appsv1.StatefulSetSpec{
+                       Replicas: &replicas2,
+                       Template: apiv1.PodTemplateSpec{
+                               Spec: apiv1.PodSpec{
+                                       Containers: []apiv1.Container{
+                                               {Name: "container1", Image: 
"image1:v2"},
+                                               {Name: "sidecar", Image: 
"sidecar:latest"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       SyncStatefulSet(curr, next)
+
+       if len(curr.Spec.Template.Spec.Containers) != 2 {
+               t.Errorf("Expected 2 containers after sync, got %d", 
len(curr.Spec.Template.Spec.Containers))
+       }
+
+       if curr.Spec.Template.Spec.Containers[0].Image != "image1:v2" {
+               t.Errorf("Expected first container image 'image1:v2', got 
'%s'", curr.Spec.Template.Spec.Containers[0].Image)
+       }
+
+       if curr.Spec.Template.Spec.Containers[1].Name != "sidecar" {
+               t.Errorf("Expected second container name 'sidecar', got '%s'", 
curr.Spec.Template.Spec.Containers[1].Name)
+       }
+}
+
diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go
new file mode 100644
index 0000000..a9f0fd4
--- /dev/null
+++ b/pkg/utils/utils_test.go
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils
+
+import (
+       "strings"
+       "testing"
+
+       seatav1alpha1 "github.com/apache/seata-k8s/api/v1alpha1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestConcatRaftServerAddress(t *testing.T) {
+       testCases := []struct {
+               name           string
+               seataServer    *seatav1alpha1.SeataServer
+               expectedParts  []string
+               expectedLength int
+       }{
+               {
+                       name: "single replica",
+                       seataServer: &seatav1alpha1.SeataServer{
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Name: "seata",
+                               },
+                               Spec: seatav1alpha1.SeataServerSpec{
+                                       Replicas:    1,
+                                       ServiceName: "seata-svc",
+                                       Ports: seatav1alpha1.Ports{
+                                               RaftPort: 9091,
+                                       },
+                               },
+                       },
+                       expectedParts:  []string{"seata-0.seata-svc:9091"},
+                       expectedLength: 1,
+               },
+               {
+                       name: "three replicas",
+                       seataServer: &seatav1alpha1.SeataServer{
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Name: "seata-cluster",
+                               },
+                               Spec: seatav1alpha1.SeataServerSpec{
+                                       Replicas:    3,
+                                       ServiceName: "seata-headless",
+                                       Ports: seatav1alpha1.Ports{
+                                               RaftPort: 9091,
+                                       },
+                               },
+                       },
+                       expectedParts: []string{
+                               "seata-cluster-0.seata-headless:9091",
+                               "seata-cluster-1.seata-headless:9091",
+                               "seata-cluster-2.seata-headless:9091",
+                       },
+                       expectedLength: 3,
+               },
+               {
+                       name: "zero replicas",
+                       seataServer: &seatav1alpha1.SeataServer{
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Name: "seata",
+                               },
+                               Spec: seatav1alpha1.SeataServerSpec{
+                                       Replicas:    0,
+                                       ServiceName: "seata-svc",
+                                       Ports: seatav1alpha1.Ports{
+                                               RaftPort: 9091,
+                                       },
+                               },
+                       },
+                       expectedParts:  []string{},
+                       expectedLength: 0,
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       result := ConcatRaftServerAddress(tc.seataServer)
+
+                       if tc.expectedLength == 0 {
+                               if result != "" {
+                                       t.Errorf("Expected empty string for 
zero replicas, got '%s'", result)
+                               }
+                               return
+                       }
+
+                       // Check that result contains all expected parts
+                       for _, part := range tc.expectedParts {
+                               if !strings.Contains(result, part) {
+                                       t.Errorf("Expected result to contain 
'%s', but got '%s'", part, result)
+                               }
+                       }
+
+                       // Check number of addresses (separated by commas)
+                       addresses := strings.Split(result, ",")
+                       if len(addresses) != tc.expectedLength {
+                               t.Errorf("Expected %d addresses, got %d: %v", 
tc.expectedLength, len(addresses), addresses)
+                       }
+               })
+       }
+}
+
+func TestContainsString(t *testing.T) {
+       testCases := []struct {
+               name     string
+               slice    []string
+               str      string
+               expected bool
+       }{
+               {
+                       name:     "string exists in slice",
+                       slice:    []string{"apple", "banana", "cherry"},
+                       str:      "banana",
+                       expected: true,
+               },
+               {
+                       name:     "string does not exist in slice",
+                       slice:    []string{"apple", "banana", "cherry"},
+                       str:      "orange",
+                       expected: false,
+               },
+               {
+                       name:     "empty slice",
+                       slice:    []string{},
+                       str:      "test",
+                       expected: false,
+               },
+               {
+                       name:     "empty string in slice",
+                       slice:    []string{"", "a", "b"},
+                       str:      "",
+                       expected: true,
+               },
+               {
+                       name:     "single element matching",
+                       slice:    []string{"only"},
+                       str:      "only",
+                       expected: true,
+               },
+               {
+                       name:     "single element not matching",
+                       slice:    []string{"only"},
+                       str:      "other",
+                       expected: false,
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       result := ContainsString(tc.slice, tc.str)
+                       if result != tc.expected {
+                               t.Errorf("Expected %v, got %v", tc.expected, 
result)
+                       }
+               })
+       }
+}
+
+func TestRemoveString(t *testing.T) {
+       testCases := []struct {
+               name     string
+               slice    []string
+               str      string
+               expected []string
+       }{
+               {
+                       name:     "remove existing string",
+                       slice:    []string{"apple", "banana", "cherry"},
+                       str:      "banana",
+                       expected: []string{"apple", "cherry"},
+               },
+               {
+                       name:     "remove non-existing string",
+                       slice:    []string{"apple", "banana", "cherry"},
+                       str:      "orange",
+                       expected: []string{"apple", "banana", "cherry"},
+               },
+               {
+                       name:     "remove from empty slice",
+                       slice:    []string{},
+                       str:      "test",
+                       expected: []string{},
+               },
+               {
+                       name:     "remove only element",
+                       slice:    []string{"only"},
+                       str:      "only",
+                       expected: []string{},
+               },
+               {
+                       name:     "remove duplicate strings",
+                       slice:    []string{"a", "b", "a", "c", "a"},
+                       str:      "a",
+                       expected: []string{"b", "c"},
+               },
+               {
+                       name:     "remove first element",
+                       slice:    []string{"first", "second", "third"},
+                       str:      "first",
+                       expected: []string{"second", "third"},
+               },
+               {
+                       name:     "remove last element",
+                       slice:    []string{"first", "second", "third"},
+                       str:      "third",
+                       expected: []string{"first", "second"},
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       result := RemoveString(tc.slice, tc.str)
+
+                       if len(result) != len(tc.expected) {
+                               t.Errorf("Expected length %d, got %d", 
len(tc.expected), len(result))
+                               return
+                       }
+
+                       for i, v := range result {
+                               if v != tc.expected[i] {
+                                       t.Errorf("At index %d: expected '%s', 
got '%s'", i, tc.expected[i], v)
+                               }
+                       }
+               })
+       }
+}
+
+func TestIsPVCOrphan(t *testing.T) {
+       testCases := []struct {
+               name     string
+               pvcName  string
+               replicas int32
+               expected bool
+       }{
+               {
+                       name:     "PVC within replica range",
+                       pvcName:  "seata-store-seata-0",
+                       replicas: 3,
+                       expected: false,
+               },
+               {
+                       name:     "PVC at replica boundary",
+                       pvcName:  "seata-store-seata-2",
+                       replicas: 3,
+                       expected: false,
+               },
+               {
+                       name:     "PVC beyond replica range",
+                       pvcName:  "seata-store-seata-3",
+                       replicas: 3,
+                       expected: true,
+               },
+               {
+                       name:     "PVC far beyond replica range",
+                       pvcName:  "seata-store-seata-10",
+                       replicas: 3,
+                       expected: true,
+               },
+               {
+                       name:     "zero replicas with PVC 0",
+                       pvcName:  "seata-store-seata-0",
+                       replicas: 0,
+                       expected: true,
+               },
+               {
+                       name:     "single replica with PVC 1",
+                       pvcName:  "seata-store-seata-1",
+                       replicas: 1,
+                       expected: true,
+               },
+               {
+                       name:     "invalid PVC name format",
+                       pvcName:  "invalid-name-without-number",
+                       replicas: 3,
+                       expected: false,
+               },
+               {
+                       name:     "PVC name with letters after dash",
+                       pvcName:  "seata-store-abc",
+                       replicas: 3,
+                       expected: false,
+               },
+               {
+                       name:     "PVC name without dash",
+                       pvcName:  "singlename",
+                       replicas: 3,
+                       expected: false,
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       result := IsPVCOrphan(tc.pvcName, tc.replicas)
+                       if result != tc.expected {
+                               t.Errorf("Expected %v, got %v for pvcName='%s' 
with replicas=%d",
+                                       tc.expected, result, tc.pvcName, 
tc.replicas)
+                       }
+               })
+       }
+}
+
+func TestSeataFinalizer(t *testing.T) {
+       // Test that the constant is defined
+       if SeataFinalizer == "" {
+               t.Error("SeataFinalizer constant should not be empty")
+       }
+
+       if SeataFinalizer != "cleanUpSeataPVC" {
+               t.Errorf("Expected SeataFinalizer to be 'cleanUpSeataPVC', got 
'%s'", SeataFinalizer)
+       }
+}
+
+func TestConcatRaftServerAddress_CustomPort(t *testing.T) {
+       seataServer := &seatav1alpha1.SeataServer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name: "custom",
+               },
+               Spec: seatav1alpha1.SeataServerSpec{
+                       Replicas:    2,
+                       ServiceName: "custom-svc",
+                       Ports: seatav1alpha1.Ports{
+                               RaftPort: 7777,
+                       },
+               },
+       }
+
+       result := ConcatRaftServerAddress(seataServer)
+
+       expectedParts := []string{
+               "custom-0.custom-svc:7777",
+               "custom-1.custom-svc:7777",
+       }
+
+       for _, part := range expectedParts {
+               if !strings.Contains(result, part) {
+                       t.Errorf("Expected result to contain '%s', but got 
'%s'", part, result)
+               }
+       }
+
+       // Should not contain trailing comma
+       if strings.HasSuffix(result, ",") {
+               t.Error("Result should not have trailing comma")
+       }
+}
+


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to