Repository: kubevela/kubevela

Issue:
Cue short-circuit for native definitions
CUE expression using `&&` operator that previously worked in Vela 1.9.6 now throws index out-of-bounds errors in 1.10.x. The `&&` operator appears to no longer short-circuit, causing the second operand to be evaluated even when the first operand is false.

**To Reproduce**
1. Use a CUE expression with `&&` operator that depends on short-circuiting behavior:
   ```cue
   isHealth: len(readyConditions) > 0 && readyConditions[0].status == "True"
   ```
2. Upgrade from KubeVela 1.9.6 to 1.10.x
3. Apply the component/application with this expression
4. Observe index out-of-bounds error when `readyConditions` is empty

**Expected behavior**
The `&&` operator should short-circuit, meaning if `len(readyConditions) > 0` evaluates to false, the second part `readyConditions[0].status == "True"` should not be evaluated, preventing the index out-of-bounds error.

**Additional context**
- Workaround implemented using if statement:
  ```cue
  if len(readyConditions) > 0 {
      isHealth: readyConditions[0].status == "True"
  }
  ```
- This issue may also affect built-in definitions that use similar unsafe patterns
- According to CUE documentation, the `&&` operator is not expected to short-circuit, which suggests this may have been working by accident in previous versions
- All the native definitions are following this pattern

Discussion:

Solution patch:
diff --git a/charts/vela-core/templates/defwithtemplate/expose.yaml b/charts/vela-core/templates/defwithtemplate/expose.yaml
index 81033a13..289bd008 100644
--- a/charts/vela-core/templates/defwithtemplate/expose.yaml
+++ b/charts/vela-core/templates/defwithtemplate/expose.yaml
@@ -96,21 +96,17 @@ spec:
   stage: PostDispatch
   status:
     customStatus: |-
-      message: *"" | string
       service: context.outputs.service
+      message: *"" | string
       if service.spec.type == "ClusterIP" {
       	message: "ClusterIP: \(service.spec.clusterIP)"
       }
       if service.spec.type == "LoadBalancer" {
       	status: service.status
       	isHealth: *false | bool
+      	message: *"ExternalIP: Pending" | string
       	if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
       		isHealth: true
-      	}
-      	if !isHealth {
-      		message: "ExternalIP: Pending"
-      	}
-      	if isHealth {
       		message: "ExternalIP: \(status.loadBalancer.ingress[0].ip)"
       	}
       }
diff --git a/charts/vela-core/templates/defwithtemplate/gateway.yaml b/charts/vela-core/templates/defwithtemplate/gateway.yaml
index d8370b48..bfa3e2c7 100644
--- a/charts/vela-core/templates/defwithtemplate/gateway.yaml
+++ b/charts/vela-core/templates/defwithtemplate/gateway.yaml
@@ -160,13 +160,17 @@ spec:
         if parameter.name == _|_ { "" }
       }
       let ingressMetaName = context.name + nameSuffix
-      let ig  = [for i in context.outputs if (i.kind == "Ingress") && (i.metadata.name == ingressMetaName) {i}][0]
+      let igList = [for i in context.outputs if (i.kind == "Ingress") && (i.metadata.name == ingressMetaName) {i}]
+      ig: *_|_ | _
+      if len(igList) > 0 {
+        ig: igList[0]
+      }
       igs: *{} | {}
-      if ig != _|_ if ig.status != _|_ if ig.status.loadbalancer != _|_ {
+      if ig != _|_ if ig.status != _|_ if ig.status.loadbalancer != _|_ if len(ig.status.loadbalancer.ingress) > 0 {
         igs: ig.status.loadbalancer.ingress[0]
       }
       igr: *{} | {}
-      if ig != _|_ if ig.spec != _|_  {
+      if ig != _|_ if ig.spec != _|_ if len(ig.spec.rules) > 0 {
         igr: ig.spec.rules[0]
       }
       if igs == _|_ {
diff --git a/charts/vela-core/templates/defwithtemplate/ingress-1-20.yaml b/charts/vela-core/templates/defwithtemplate/ingress-1-20.yaml
index ed85f6d2..5f4daf77 100644
--- a/charts/vela-core/templates/defwithtemplate/ingress-1-20.yaml
+++ b/charts/vela-core/templates/defwithtemplate/ingress-1-20.yaml
@@ -69,11 +69,16 @@ spec:
         message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + "'\n"
       }
       if len(igs) > 0 {
+        let rules = context.outputs.ingress.spec.rules
+        host: *"" | string
+        if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+      	  host: rules[0].host
+        }
         if igs[0].ip != _|_ {
-      	  message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+      	  message: "Visiting URL: " + host + ", IP: " + igs[0].ip
         }
         if igs[0].ip == _|_ {
-      	  message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+      	  message: "Visiting URL: " + host
         }
       }
     healthPolicy: 'isHealth: len(context.outputs.service.spec.clusterIP) > 0'
diff --git a/charts/vela-core/templates/defwithtemplate/ingress.yaml b/charts/vela-core/templates/defwithtemplate/ingress.yaml
index 20ee4506..3eb0467f 100644
--- a/charts/vela-core/templates/defwithtemplate/ingress.yaml
+++ b/charts/vela-core/templates/defwithtemplate/ingress.yaml
@@ -62,11 +62,16 @@ spec:
       	message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + "'\n"
       }
       if len(igs) > 0 {
+      	let rules = context.outputs.ingress.spec.rules
+      	host: *"" | string
+      	if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+      		host: rules[0].host
+      	}
       	if igs[0].ip != _|_ {
-      		message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+      		message: "Visiting URL: " + host + ", IP: " + igs[0].ip
       	}
       	if igs[0].ip == _|_ {
-      		message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+      		message: "Visiting URL: " + host
       	}
       }
     healthPolicy: 'isHealth: len(context.outputs.service.spec.clusterIP) > 0'
diff --git a/charts/vela-core/templates/defwithtemplate/k8s-objects.yaml b/charts/vela-core/templates/defwithtemplate/k8s-objects.yaml
index 49bf1e9a..f8215dbb 100644
--- a/charts/vela-core/templates/defwithtemplate/k8s-objects.yaml
+++ b/charts/vela-core/templates/defwithtemplate/k8s-objects.yaml
@@ -11,7 +11,12 @@ spec:
   schematic:
     cue:
       template: |
-        output: parameter.objects[0]
+        output: {
+        	if len(parameter.objects) > 0 {
+        		parameter.objects[0]
+        	}
+        	...
+        }
 
         outputs: {
         	for i, v in parameter.objects {
diff --git a/charts/vela-core/templates/defwithtemplate/pure-ingress.yaml b/charts/vela-core/templates/defwithtemplate/pure-ingress.yaml
index 55f680b3..7885a6ea 100644
--- a/charts/vela-core/templates/defwithtemplate/pure-ingress.yaml
+++ b/charts/vela-core/templates/defwithtemplate/pure-ingress.yaml
@@ -49,11 +49,16 @@ spec:
       	message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
       }
       if len(igs) > 0 {
+      	let rules = context.outputs.ingress.spec.rules
+      	host: *"" | string
+      	if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+      		host: rules[0].host
+      	}
       	if igs[0].ip != _|_ {
-      		message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+      		message: "Visiting URL: " + host + ", IP: " + igs[0].ip
       	}
       	if igs[0].ip == _|_ {
-      		message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+      		message: "Visiting URL: " + host
       	}
       }
   workloadRefPath: ""
diff --git a/pkg/addon/reader_gitlab.go b/pkg/addon/reader_gitlab.go
index d2a0bcb1..8fe46399 100644
--- a/pkg/addon/reader_gitlab.go
+++ b/pkg/addon/reader_gitlab.go
@@ -52,6 +52,9 @@ func (g GitLabItem) GetType() string {
 
 // GetPath get addon's sub item path
 func (g GitLabItem) GetPath() string {
+	if g.basePath == "" {
+		return g.path
+	}
 	return g.path[len(g.basePath)+1:]
 }
 
diff --git a/pkg/workflow/providers/legacy/multicluster/multicluster.cue b/pkg/workflow/providers/legacy/multicluster/multicluster.cue
index 3988ed33..7b232a4a 100644
--- a/pkg/workflow/providers/legacy/multicluster/multicluster.cue
+++ b/pkg/workflow/providers/legacy/multicluster/multicluster.cue
@@ -103,7 +103,9 @@
 	envBindingPolicies: []
 	if inputs.policy == "" && loadPolicies.value != _|_ {
 		envBindingPolicies: [for k, v in loadPolicies.value if v.type == "env-binding" {k}]
-		policy_: envBindingPolicies[0]
+		if len(envBindingPolicies) > 0 {
+			policy_: envBindingPolicies[0]
+		}
 	}
 	if inputs.policy != "" {
 		policy_: inputs.policy
diff --git a/vela-templates/definitions/deprecated/ingress-1-20.cue b/vela-templates/definitions/deprecated/ingress-1-20.cue
index 5108b557..05c93fbf 100644
--- a/vela-templates/definitions/deprecated/ingress-1-20.cue
+++ b/vela-templates/definitions/deprecated/ingress-1-20.cue
@@ -14,11 +14,16 @@
 				  message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + "'\n"
 				}
 				if len(igs) > 0 {
+				  let rules = context.outputs.ingress.spec.rules
+				  host: *"" | string
+				  if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+					  host: rules[0].host
+				  }
 				  if igs[0].ip != _|_ {
-					  message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+					  message: "Visiting URL: " + host + ", IP: " + igs[0].ip
 				  }
 				  if igs[0].ip == _|_ {
-					  message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+					  message: "Visiting URL: " + host
 				  }
 				}
 				"""#
diff --git a/vela-templates/definitions/deprecated/ingress.cue b/vela-templates/definitions/deprecated/ingress.cue
index 74cbe27b..93bce4b2 100644
--- a/vela-templates/definitions/deprecated/ingress.cue
+++ b/vela-templates/definitions/deprecated/ingress.cue
@@ -14,11 +14,16 @@ ingress: {
 					message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + "'\n"
 				}
 				if len(igs) > 0 {
+					let rules = context.outputs.ingress.spec.rules
+					host: *"" | string
+					if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+						host: rules[0].host
+					}
 					if igs[0].ip != _|_ {
-						message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+						message: "Visiting URL: " + host + ", IP: " + igs[0].ip
 					}
 					if igs[0].ip == _|_ {
-						message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+						message: "Visiting URL: " + host
 					}
 				}
 				"""#
diff --git a/vela-templates/definitions/internal/component/k8s-objects.cue b/vela-templates/definitions/internal/component/k8s-objects.cue
index d9a2a447..a9b61f48 100644
--- a/vela-templates/definitions/internal/component/k8s-objects.cue
+++ b/vela-templates/definitions/internal/component/k8s-objects.cue
@@ -6,7 +6,12 @@
 	attributes: workload: type: "autodetects.core.oam.dev"
 }
 template: {
-	output: parameter.objects[0]
+	output: {
+		if len(parameter.objects) > 0 {
+			parameter.objects[0]
+		}
+		...
+	}
 
 	outputs: {
 		for i, v in parameter.objects {
diff --git a/vela-templates/definitions/internal/trait/expose.cue b/vela-templates/definitions/internal/trait/expose.cue
index 281640df..528fb57a 100644
--- a/vela-templates/definitions/internal/trait/expose.cue
+++ b/vela-templates/definitions/internal/trait/expose.cue
@@ -13,21 +13,17 @@ expose: {
 		appliesToWorkloads: ["deployments.apps", "statefulsets.apps"]
 		status: {
 			customStatus: #"""
-				message: *"" | string
 				service: context.outputs.service
+				message: *"" | string
 				if service.spec.type == "ClusterIP" {
 					message: "ClusterIP: \(service.spec.clusterIP)"
 				}
 				if service.spec.type == "LoadBalancer" {
 					status: service.status
 					isHealth: *false | bool
+					message: *"ExternalIP: Pending" | string
 					if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
 						isHealth: true
-					}
-					if !isHealth {
-						message: "ExternalIP: Pending"
-					}
-					if isHealth {
 						message: "ExternalIP: \(status.loadBalancer.ingress[0].ip)"
 					}
 				}
diff --git a/vela-templates/definitions/internal/trait/gateway.cue b/vela-templates/definitions/internal/trait/gateway.cue
index 772d252a..955a6608 100644
--- a/vela-templates/definitions/internal/trait/gateway.cue
+++ b/vela-templates/definitions/internal/trait/gateway.cue
@@ -16,13 +16,17 @@ gateway: {
 				  if parameter.name == _|_ { "" }
 				}
 				let ingressMetaName = context.name + nameSuffix
-				let ig  = [for i in context.outputs if (i.kind == "Ingress") && (i.metadata.name == ingressMetaName) {i}][0]
+				let igList = [for i in context.outputs if (i.kind == "Ingress") && (i.metadata.name == ingressMetaName) {i}]
+				ig: *_|_ | _
+				if len(igList) > 0 {
+				  ig: igList[0]
+				}
 				igs: *{} | {}
-				if ig != _|_ if ig.status != _|_ if ig.status.loadbalancer != _|_ {
+				if ig != _|_ if ig.status != _|_ if ig.status.loadbalancer != _|_ if len(ig.status.loadbalancer.ingress) > 0 {
 				  igs: ig.status.loadbalancer.ingress[0]
 				}
 				igr: *{} | {}
-				if ig != _|_ if ig.spec != _|_  {
+				if ig != _|_ if ig.spec != _|_ if len(ig.spec.rules) > 0 {
 				  igr: ig.spec.rules[0]
 				}
 				if igs == _|_ {
diff --git a/vela-templates/definitions/internal/trait/pure-ingress.cue b/vela-templates/definitions/internal/trait/pure-ingress.cue
index adc710b4..84480f08 100644
--- a/vela-templates/definitions/internal/trait/pure-ingress.cue
+++ b/vela-templates/definitions/internal/trait/pure-ingress.cue
@@ -10,11 +10,16 @@
 					message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
 				}
 				if len(igs) > 0 {
+					let rules = context.outputs.ingress.spec.rules
+					host: *"" | string
+					if rules != _|_ if len(rules) > 0 if rules[0].host != _|_ {
+						host: rules[0].host
+					}
 					if igs[0].ip != _|_ {
-						message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
+						message: "Visiting URL: " + host + ", IP: " + igs[0].ip
 					}
 					if igs[0].ip == _|_ {
-						message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
+						message: "Visiting URL: " + host
 					}
 				}
 				"""#



Test patch:
diff --git a/pkg/addon/error_test.go b/pkg/addon/error_test.go
index 05709d37..d9066211 100644
--- a/pkg/addon/error_test.go
+++ b/pkg/addon/error_test.go
@@ -20,6 +20,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/google/go-github/v32/github"
+	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -41,3 +43,13 @@ func TestGetAvailableVersion(t *testing.T) {
 	assert.NotEmpty(t, err)
 	assert.Equal(t, version, "")
 }
+
+func TestWrapErrRateLimit(t *testing.T) {
+	regularErr := errors.New("regular error")
+	wrappedErr := WrapErrRateLimit(regularErr)
+	assert.Equal(t, regularErr, wrappedErr)
+
+	rateLimitErr := &github.RateLimitError{}
+	wrappedErr = WrapErrRateLimit(rateLimitErr)
+	assert.Equal(t, ErrRateLimit, wrappedErr)
+}
diff --git a/pkg/addon/memory_reader_test.go b/pkg/addon/memory_reader_test.go
deleted file mode 100644
index d778b02d..00000000
--- a/pkg/addon/memory_reader_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright 2021 The KubeVela Authors.
-
-Licensed 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 addon
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"helm.sh/helm/v3/pkg/chart/loader"
-)
-
-var files = []*loader.BufferedFile{
-	{
-		Name: "metadata.yaml",
-		Data: []byte(`name: test-helm-addon
-version: 1.0.0
-description: This is a addon for test when install addon from helm repo
-icon: https://www.terraform.io/assets/images/logo-text-8c3ba8a6.svg
-url: https://terraform.io/
-
-tags: []
-
-deployTo:
-  control_plane: true
-  runtime_cluster: false
-
-dependencies: []
-
-invisible: false`),
-	},
-	{
-		Name: "/resources/parameter.cue",
-		Data: []byte(`parameter: {
-	// test wrong parameter
-	example: *"default"
-}`),
-	},
-}
-
-func TestMemoryReader(t *testing.T) {
-	m := MemoryReader{
-		Name:  "fluxcd",
-		Files: files,
-	}
-
-	meta, err := m.ListAddonMeta()
-	assert.NoError(t, err)
-	assert.Equal(t, len(meta["fluxcd"].Items), 2)
-
-	metaFile, err := m.ReadFile("metadata.yaml")
-	assert.NoError(t, err)
-	assert.NotEmpty(t, metaFile)
-
-	parameterData, err := m.ReadFile("/resources/parameter.cue")
-	assert.NoError(t, err)
-	assert.NotEmpty(t, parameterData)
-}
diff --git a/pkg/addon/reader_gitee_test.go b/pkg/addon/reader_gitee_test.go
index 2f6f9d02..a242e070 100644
--- a/pkg/addon/reader_gitee_test.go
+++ b/pkg/addon/reader_gitee_test.go
@@ -97,3 +97,155 @@ func TestGiteeReader(t *testing.T) {
 	assert.NoError(t, err)
 
 }
+
+func TestNewGiteeClient(t *testing.T) {
+	defaultURL, _ := url.Parse(DefaultGiteeURL)
+
+	testCases := map[string]struct {
+		httpClient *http.Client
+		baseURL    *url.URL
+		wantClient *http.Client
+		wantURL    *url.URL
+	}{
+		"Nil inputs": {
+			httpClient: nil,
+			baseURL:    nil,
+			wantClient: &http.Client{},
+			wantURL:    defaultURL,
+		},
+		"Custom inputs": {
+			httpClient: &http.Client{Timeout: 10},
+			baseURL:    &url.URL{Host: "my-gitee.com"},
+			wantClient: &http.Client{Timeout: 10},
+			wantURL:    &url.URL{Host: "my-gitee.com"},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			client := NewGiteeClient(tc.httpClient, tc.baseURL)
+			assert.Equal(t, tc.wantClient.Timeout, client.Client.Timeout)
+			assert.Equal(t, tc.wantURL.Host, client.BaseURL.Host)
+		})
+	}
+}
+
+func TestGiteeReaderRelativePath(t *testing.T) {
+	testCases := map[string]struct {
+		basePath     string
+		itemPath     string
+		expectedPath string
+	}{
+		"No base path": {
+			basePath:     "",
+			itemPath:     "fluxcd/metadata.yaml",
+			expectedPath: "fluxcd/metadata.yaml",
+		},
+		"With base path": {
+			basePath:     "addons",
+			itemPath:     "addons/fluxcd/metadata.yaml",
+			expectedPath: "fluxcd/metadata.yaml",
+		},
+		"With deep base path": {
+			basePath:     "official/addons",
+			itemPath:     "official/addons/fluxcd/template.cue",
+			expectedPath: "fluxcd/template.cue",
+		},
+		"Item at root of base path": {
+			basePath:     "addons",
+			itemPath:     "addons/README.md",
+			expectedPath: "README.md",
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			gith := &giteeHelper{
+				Meta: &utils.Content{GiteeContent: utils.GiteeContent{
+					Path: tc.basePath,
+				}},
+			}
+			r := &giteeReader{h: gith}
+			item := &github.RepositoryContent{Path: &tc.itemPath}
+
+			result := r.RelativePath(item)
+			assert.Equal(t, tc.expectedPath, result)
+		})
+	}
+}
+
+func TestGiteeReader_ListAddonMeta(t *testing.T) {
+	client, mux, teardown := giteeSetup()
+	defer teardown()
+
+	giteePattern := "/repos/o/r/contents/"
+	mux.HandleFunc(giteePattern, func(rw http.ResponseWriter, req *http.Request) {
+		var contents []*github.RepositoryContent
+		queryPath := strings.TrimPrefix(req.URL.Path, giteePattern)
+
+		switch queryPath {
+		case "": // Root directory
+			contents = []*github.RepositoryContent{
+				{Type: String("dir"), Name: String("fluxcd"), Path: String("fluxcd")},
+				{Type: String("dir"), Name: String("velaux"), Path: String("velaux")},
+				{Type: String("file"), Name: String("README.md"), Path: String("README.md")},
+			}
+		case "fluxcd":
+			contents = []*github.RepositoryContent{
+				{Type: String("file"), Name: String("metadata.yaml"), Path: String("fluxcd/metadata.yaml")},
+				{Type: String("dir"), Name: String("resources"), Path: String("fluxcd/resources")},
+			}
+		case "fluxcd/resources":
+			contents = []*github.RepositoryContent{
+				{Type: String("file"), Name: String("parameter.cue"), Path: String("fluxcd/resources/parameter.cue")},
+			}
+		case "velaux":
+			contents = []*github.RepositoryContent{
+				{Type: String("file"), Name: String("metadata.yaml"), Path: String("velaux/metadata.yaml")},
+			}
+		default:
+			rw.WriteHeader(http.StatusNotFound)
+			return
+		}
+		res, _ := json.Marshal(contents)
+		rw.Write(res)
+	})
+
+	gith := &giteeHelper{
+		Client: client,
+		Meta: &utils.Content{GiteeContent: utils.GiteeContent{
+			Owner: "o",
+			Repo:  "r",
+		}},
+	}
+	r := &giteeReader{h: gith}
+
+	meta, err := r.ListAddonMeta()
+	assert.NoError(t, err)
+	assert.NotNil(t, meta)
+	assert.Equal(t, 2, len(meta), "Expected to find 2 addons, root files should be ignored")
+
+	t.Run("fluxcd addon discovery", func(t *testing.T) {
+		addon, ok := meta["fluxcd"]
+		assert.True(t, ok, "fluxcd addon should be discovered")
+		assert.Equal(t, "fluxcd", addon.Name)
+
+		// Should find 2 items recursively: metadata.yaml and resources/parameter.cue
+		assert.Equal(t, 2, len(addon.Items), "fluxcd should contain 2 files")
+
+		foundPaths := make(map[string]bool)
+		for _, item := range addon.Items {
+			foundPaths[item.GetPath()] = true
+		}
+		assert.True(t, foundPaths["fluxcd/metadata.yaml"], "should find fluxcd/metadata.yaml")
+		assert.True(t, foundPaths["fluxcd/resources/parameter.cue"], "should find fluxcd/resources/parameter.cue")
+	})
+
+	t.Run("velaux addon discovery", func(t *testing.T) {
+		addon, ok := meta["velaux"]
+		assert.True(t, ok, "velaux addon should be discovered")
+		assert.Equal(t, "velaux", addon.Name)
+		assert.Equal(t, 1, len(addon.Items), "velaux should contain 1 file")
+		assert.Equal(t, "velaux/metadata.yaml", addon.Items[0].GetPath())
+	})
+}
diff --git a/pkg/addon/reader_github_test.go b/pkg/addon/reader_github_test.go
index 1d971c03..5ef217d4 100644
--- a/pkg/addon/reader_github_test.go
+++ b/pkg/addon/reader_github_test.go
@@ -64,8 +64,10 @@ func setup() (client *github.Client, mux *http.ServeMux, teardown func()) {
 	return client, mux, server.Close
 }
 
-func TestGitHubReader(t *testing.T) {
+func TestGitHubReader_ReadFile(t *testing.T) {
 	client, mux, teardown := setup()
+	defer teardown()
+
 	githubPattern := "/repos/o/r/contents/"
 	mux.HandleFunc(githubPattern, func(rw http.ResponseWriter, req *http.Request) {
 		queryPath := strings.TrimPrefix(req.URL.Path, githubPattern)
@@ -76,6 +78,7 @@ func TestGitHubReader(t *testing.T) {
 			content := &github.RepositoryContent{Type: String("file"), Name: String(path.Base(queryPath)), Size: Int(len(file)), Encoding: String(""), Path: String(queryPath), Content: String(string(file))}
 			res, _ := json.Marshal(content)
 			rw.Write(res)
+			return
 		}
 
 		// otherwise, it could be directory
@@ -91,11 +94,11 @@ func TestGitHubReader(t *testing.T) {
 			}
 			dRes, _ := json.Marshal(contents)
 			rw.Write(dRes)
+			return
 		}
 
-		rw.Write([]byte("invalid github query"))
+		rw.WriteHeader(http.StatusNotFound)
 	})
-	defer teardown()
 
 	gith := &gitHelper{
 		Client: client,
@@ -107,7 +110,95 @@ func TestGitHubReader(t *testing.T) {
 	var r AsyncReader = &gitReader{gith}
 	_, err := r.ReadFile("example/metadata.yaml")
 	assert.NoError(t, err)
+}
+
+func TestGitReader_RelativePath(t *testing.T) {
+	testCases := map[string]struct {
+		basePath     string
+		itemPath     string
+		expectedPath string
+	}{
+		"No base path": {
+			basePath:     "",
+			itemPath:     "fluxcd/metadata.yaml",
+			expectedPath: "fluxcd/metadata.yaml",
+		},
+		"With base path": {
+			basePath:     "addons",
+			itemPath:     "addons/fluxcd/metadata.yaml",
+			expectedPath: "fluxcd/metadata.yaml",
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			gith := &gitHelper{
+				Meta: &utils.Content{GithubContent: utils.GithubContent{
+					Path: tc.basePath,
+				}},
+			}
+			r := &gitReader{h: gith}
+			item := &github.RepositoryContent{Path: &tc.itemPath}
+
+			result := r.RelativePath(item)
+			assert.Equal(t, tc.expectedPath, result)
+		})
+	}
+}
+
+func TestGitReader_ListAddonMeta(t *testing.T) {
+	client, mux, teardown := setup()
+	defer teardown()
+
+	githubPattern := "/repos/o/r/contents/"
+	mux.HandleFunc(githubPattern, func(rw http.ResponseWriter, req *http.Request) {
+		var contents []*github.RepositoryContent
+		queryPath := strings.TrimPrefix(req.URL.Path, githubPattern)
 
+		switch queryPath {
+		case "": // Root directory
+			contents = []*github.RepositoryContent{
+				{Type: String("dir"), Name: String("fluxcd"), Path: String("fluxcd")},
+				{Type: String("file"), Name: String("README.md"), Path: String("README.md")},
+			}
+		case "fluxcd":
+			contents = []*github.RepositoryContent{
+				{Type: String("file"), Name: String("metadata.yaml"), Path: String("fluxcd/metadata.yaml")},
+				{Type: String("dir"), Name: String("resources"), Path: String("fluxcd/resources")},
+				{Type: String("file"), Name: String("template.cue"), Path: String("fluxcd/template.cue")},
+			}
+		case "fluxcd/resources":
+			contents = []*github.RepositoryContent{
+				{Type: String("file"), Name: String("parameter.cue"), Path: String("fluxcd/resources/parameter.cue")},
+			}
+		default:
+			rw.WriteHeader(http.StatusNotFound)
+			return
+		}
+		res, _ := json.Marshal(contents)
+		rw.Write(res)
+	})
+
+	gith := &gitHelper{
+		Client: client,
+		Meta: &utils.Content{GithubContent: utils.GithubContent{
+			Owner: "o",
+			Repo:  "r",
+		}},
+	}
+	r := &gitReader{h: gith}
+
+	meta, err := r.ListAddonMeta()
+	assert.NoError(t, err)
+	assert.NotNil(t, meta)
+	assert.Equal(t, 1, len(meta), "Expected to find 1 addon, root files should be ignored")
+
+	t.Run("fluxcd addon discovery", func(t *testing.T) {
+		addon, ok := meta["fluxcd"]
+		assert.True(t, ok, "fluxcd addon should be discovered")
+		assert.Equal(t, "fluxcd", addon.Name)
+		assert.Equal(t, 3, len(addon.Items), "fluxcd should contain 3 files")
+	})
 }
 
 // Int is a helper routine that allocates a new int value
diff --git a/pkg/addon/reader_gitlab_test.go b/pkg/addon/reader_gitlab_test.go
index e193767a..4c0ace62 100644
--- a/pkg/addon/reader_gitlab_test.go
+++ b/pkg/addon/reader_gitlab_test.go
@@ -21,8 +21,7 @@ import (
 	"encoding/json"
 	"net/http"
 	"net/http/httptest"
-	"path"
-	"strings"
+	"strconv"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -33,81 +32,190 @@ import (
 
 var baseUrl = "/api/v4"
 
-func gitlabSetup() (client *gitlab.Client, mux *http.ServeMux, teardown func()) {
-	// mux is the HTTP request multiplexer used with the test server.
-	mux = http.NewServeMux()
-
+func gitlabSetup(t *testing.T) (*gitlab.Client, *http.ServeMux, func()) {
+	mux := http.NewServeMux()
 	apiHandler := http.NewServeMux()
 	apiHandler.Handle(baseUrl+"/", http.StripPrefix(baseUrl, mux))
-
-	// server is a test HTTP server used to provide mock API responses.
 	server := httptest.NewServer(apiHandler)
 
-	// client is the Gitlab client being tested and is
-	// configured to use test server.
 	client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL+baseUrl+"/"))
-	if err != nil {
-		return
-	}
+	assert.NoError(t, err)
 
 	return client, mux, server.Close
 }
 
-func TestGitlabReader(t *testing.T) {
-	client, mux, teardown := gitlabSetup()
-	gitlabPattern := "/projects/9999/repository/files/"
-	mux.HandleFunc(gitlabPattern, func(rw http.ResponseWriter, req *http.Request) {
-		queryPath := strings.TrimPrefix(req.URL.Path, gitlabPattern)
-		localPath := path.Join(testdataPrefix, queryPath)
-		file, err := testdata.ReadFile(localPath)
-		// test if it's a file
-		if err == nil {
-			content := &gitlab.File{
-				FilePath: localPath,
-				FileName: path.Base(queryPath),
-				Size:     *Int(len(file)),
-				Encoding: "base64",
-				Ref:      "master",
-				Content:  base64.StdEncoding.EncodeToString(file),
-			}
-			res, _ := json.Marshal(content)
-			rw.Write(res)
-			return
+func TestGitlabReader_ReadFile(t *testing.T) {
+	client, mux, teardown := gitlabSetup(t)
+	defer teardown()
+
+	// The gitlab client URL-encodes the file path, so we must match the encoded path.
+	mux.HandleFunc("/projects/9999/repository/files/example%2Fmetadata.yaml", func(rw http.ResponseWriter, req *http.Request) {
+		content := &gitlab.File{
+			Content: base64.StdEncoding.EncodeToString([]byte("hello world")),
 		}
+		res, err := json.Marshal(content)
+		assert.NoError(t, err)
+		_, err = rw.Write(res)
+		assert.NoError(t, err)
+	})
+	mux.HandleFunc("/projects/9999/repository/files/example%2Fnot%2Ffound.yaml", func(rw http.ResponseWriter, req *http.Request) {
+		rw.WriteHeader(http.StatusNotFound)
+	})
+
+	gith := &gitlabHelper{
+		Client: client,
+		Meta:   &utils.Content{GitlabContent: utils.GitlabContent{PId: 9999, Path: "example"}},
+	}
+	var r AsyncReader = &gitlabReader{h: gith}
+
+	t.Run("success case", func(t *testing.T) {
+		content, err := r.ReadFile("metadata.yaml")
+		assert.NoError(t, err)
+		assert.Equal(t, "hello world", content)
+	})
+
+	t.Run("not found case", func(t *testing.T) {
+		_, err := r.ReadFile("not/found.yaml")
+		assert.Error(t, err)
+	})
+}
+
+func TestGitlabReader_ListAddonMeta(t *testing.T) {
+	client, mux, teardown := gitlabSetup(t)
+	defer teardown()
+
+	projectID := 9999
+	projectPath := "addons"
+
+	mux.HandleFunc("/projects/"+strconv.Itoa(projectID)+"/repository/tree", func(rw http.ResponseWriter, req *http.Request) {
+		pathParam := req.URL.Query().Get("path")
+		pageParam := req.URL.Query().Get("page")
+		if pageParam == "" {
+			pageParam = "1"
+		}
+
+		var tree []*gitlab.TreeNode
 
-		// otherwise, it could be directory
-		dir, err := testdata.ReadDir(localPath)
-		if err == nil {
-			contents := make([]*gitlab.TreeNode, 0)
-			for _, item := range dir {
-				tp := "file"
-				if item.IsDir() {
-					tp = "dir"
-				}
-				contents = append(contents, &gitlab.TreeNode{
-					ID:   "",
-					Name: item.Name(),
-					Type: tp,
-					Path: localPath + "/" + item.Name(),
-					Mode: "",
-				})
+		switch pathParam {
+		case projectPath:
+			rw.Header().Set("X-Total-Pages", "2")
+			if pageParam == "1" {
+				tree = []*gitlab.TreeNode{{ID: "1", Name: "fluxcd", Type: "tree", Path: "addons/fluxcd"}}
+			} else if pageParam == "2" {
+				tree = []*gitlab.TreeNode{{ID: "2", Name: "velaux", Type: "tree", Path: "addons/velaux"}}
 			}
-			dRes, _ := json.Marshal(contents)
-			rw.Write(dRes)
+		case "addons/fluxcd":
+			tree = []*gitlab.TreeNode{{ID: "3", Name: "metadata.yaml", Type: "blob", Path: "addons/fluxcd/metadata.yaml"}}
+		case "addons/velaux":
+			tree = []*gitlab.TreeNode{{ID: "4", Name: "template.cue", Type: "blob", Path: "addons/velaux/template.cue"}}
+		default:
+			rw.WriteHeader(http.StatusNotFound)
 			return
 		}
-
-		rw.Write([]byte("invalid gitlab query"))
+		res, err := json.Marshal(tree)
+		assert.NoError(t, err)
+		_, err = rw.Write(res)
+		assert.NoError(t, err)
 	})
-	defer teardown()
 
 	gith := &gitlabHelper{
 		Client: client,
 		Meta: &utils.Content{GitlabContent: utils.GitlabContent{
-			PId: 9999,
+			PId:  projectID,
+			Path: projectPath,
 		}},
 	}
-	var r AsyncReader = &gitlabReader{gith}
-	_, err := r.ReadFile("example/metadata.yaml")
+	r := &gitlabReader{h: gith}
+
+	meta, err := r.ListAddonMeta()
 	assert.NoError(t, err)
+	assert.NotNil(t, meta)
+	assert.Equal(t, 2, len(meta))
+
+	expectedAddons := map[string]struct {
+		itemCount int
+		itemPath  string
+	}{
+		"fluxcd": {itemCount: 1, itemPath: "fluxcd/metadata.yaml"},
+		"velaux": {itemCount: 1, itemPath: "velaux/template.cue"},
+	}
+
+	for name, expected := range expectedAddons {
+		t.Run(name, func(t *testing.T) {
+			addon, ok := meta[name]
+			assert.True(t, ok, "addon not found in result")
+			assert.Equal(t, name, addon.Name)
+			assert.Equal(t, expected.itemCount, len(addon.Items))
+			assert.Equal(t, expected.itemPath, addon.Items[0].GetPath())
+		})
+	}
+}
+
+func TestGitlabReader_Getters(t *testing.T) {
+	t.Run("GetRef", func(t *testing.T) {
+		githWithRef := &gitlabHelper{Meta: &utils.Content{GitlabContent: utils.GitlabContent{Ref: "develop"}}}
+		rWithRef := &gitlabReader{h: githWithRef}
+		assert.Equal(t, "develop", rWithRef.GetRef())
+
+		githWithoutRef := &gitlabHelper{Meta: &utils.Content{GitlabContent: utils.GitlabContent{Ref: ""}}}
+		rWithoutRef := &gitlabReader{h: githWithoutRef}
+		assert.Equal(t, "master", rWithoutRef.GetRef())
+	})
+
+	t.Run("GetProjectID and GetProjectPath", func(t *testing.T) {
+		gith := &gitlabHelper{
+			Meta: &utils.Content{GitlabContent: utils.GitlabContent{
+				PId:  12345,
+				Path: "my/project/path",
+			}},
+		}
+		r := &gitlabReader{h: gith}
+		assert.Equal(t, 12345, r.GetProjectID())
+		assert.Equal(t, "my/project/path", r.GetProjectPath())
+	})
+
+	t.Run("RelativePath", func(t *testing.T) {
+		r := &gitlabReader{}
+		item := &GitLabItem{
+			basePath: "addons",
+			path:     "addons/fluxcd/metadata.yaml",
+		}
+		assert.Equal(t, "fluxcd/metadata.yaml", r.RelativePath(item))
+	})
+}
+
+func TestGitLabItem(t *testing.T) {
+	t.Run("Getters", func(t *testing.T) {
+		item := GitLabItem{
+			tp:   "blob",
+			name: "metadata.yaml",
+		}
+		assert.Equal(t, "blob", item.GetType())
+		assert.Equal(t, "metadata.yaml", item.GetName())
+	})
+
+	t.Run("GetPath", func(t *testing.T) {
+		testCases := map[string]struct {
+			basePath string
+			fullPath string
+			expected string
+		}{
+			"no base path": {
+				basePath: "",
+				fullPath: "fluxcd/metadata.yaml",
+				expected: "fluxcd/metadata.yaml",
+			},
+			"with base path": {
+				basePath: "addons",
+				fullPath: "addons/fluxcd/metadata.yaml",
+				expected: "fluxcd/metadata.yaml",
+			},
+		}
+		for name, tc := range testCases {
+			t.Run(name, func(t *testing.T) {
+				item := GitLabItem{basePath: tc.basePath, path: tc.fullPath}
+				assert.Equal(t, tc.expected, item.GetPath())
+			})
+		}
+	})
 }
diff --git a/pkg/addon/reader_local_test.go b/pkg/addon/reader_local_test.go
index 909fa282..83226603 100644
--- a/pkg/addon/reader_local_test.go
+++ b/pkg/addon/reader_local_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package addon
 
 import (
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -25,17 +26,66 @@ import (
 
 func TestLocalReader(t *testing.T) {
 	r := localReader{name: "local", dir: "./testdata/local"}
-	m, err := r.ListAddonMeta()
-	assert.NoError(t, err)
-	assert.Equal(t, len(m["local"].Items), 2)
 
-	file, err := r.ReadFile("metadata.yaml")
-	assert.NoError(t, err)
-	assert.Equal(t, file, metaFile)
+	t.Run("ListAddonMeta", func(t *testing.T) {
+		m, err := r.ListAddonMeta()
+		assert.NoError(t, err)
+		assert.NotNil(t, m["local"])
+		assert.Equal(t, 2, len(m["local"].Items))
 
-	file, err = r.ReadFile("resources/parameter.cue")
-	assert.NoError(t, err)
-	assert.Equal(t, true, strings.Contains(file, parameterFile))
+		// Check that the correct files are found, regardless of order.
+		foundPaths := make(map[string]bool)
+		for _, item := range m["local"].Items {
+			// Normalize path separators for consistent checking
+			foundPaths[filepath.ToSlash(item.GetPath())] = true
+		}
+		assert.True(t, foundPaths[filepath.ToSlash("testdata/local/metadata.yaml")])
+		assert.True(t, foundPaths[filepath.ToSlash("testdata/local/resources/parameter.cue")])
+	})
+
+	t.Run("ReadFile", func(t *testing.T) {
+		t.Run("read root file", func(t *testing.T) {
+			file, err := r.ReadFile("metadata.yaml")
+			assert.NoError(t, err)
+			assert.Equal(t, file, metaFile)
+		})
+		t.Run("read nested file", func(t *testing.T) {
+			file, err := r.ReadFile("resources/parameter.cue")
+			assert.NoError(t, err)
+			assert.True(t, strings.Contains(file, parameterFile))
+		})
+	})
+}
+
+func TestLocalReader_RelativePath(t *testing.T) {
+	testCases := map[string]struct {
+		dir       string
+		addonName string
+		itemPath  string
+		expected  string
+	}{
+		"item in root": {
+			dir:       "./testdata/local",
+			addonName: "my-addon",
+			itemPath:  filepath.Join("./testdata/local", "metadata.yaml"),
+			expected:  filepath.Join("my-addon", "metadata.yaml"),
+		},
+		"item in subdirectory": {
+			dir:       "./testdata/local",
+			addonName: "my-addon",
+			itemPath:  filepath.Join("./testdata/local", "resources", "parameter.cue"),
+			expected:  filepath.Join("my-addon", "resources", "parameter.cue"),
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			r := localReader{name: tc.addonName, dir: tc.dir}
+			item := OSSItem{path: tc.itemPath}
+			result := r.RelativePath(item)
+			assert.Equal(t, tc.expected, result)
+		})
+	}
 }
 
 const (
diff --git a/pkg/addon/reader_memory_test.go b/pkg/addon/reader_memory_test.go
new file mode 100644
index 00000000..19289052
--- /dev/null
+++ b/pkg/addon/reader_memory_test.go
@@ -0,0 +1,116 @@
+/*
+Copyright 2021 The KubeVela Authors.
+
+Licensed 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 addon
+
+import (
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"helm.sh/helm/v3/pkg/chart/loader"
+)
+
+var files = []*loader.BufferedFile{
+	{
+		Name: "metadata.yaml",
+		Data: []byte(`name: test-helm-addon
+version: 1.0.0
+description: This is a addon for test when install addon from helm repo
+icon: https://www.terraform.io/assets/images/logo-text-8c3ba8a6.svg
+url: https://terraform.io/
+
+tags: []
+
+deployTo:
+  control_plane: true
+  runtime_cluster: false
+
+dependencies: []
+
+invisible: false`),
+	},
+	{
+		Name: "resources/parameter.cue",
+		Data: []byte(`parameter: {
+	// test wrong parameter
+	example: *"default"
+}`),
+	},
+}
+
+func TestMemoryReader(t *testing.T) {
+	m := MemoryReader{
+		Name:  "fluxcd",
+		Files: files,
+	}
+
+	t.Run("ListAddonMeta", func(t *testing.T) {
+		meta, err := m.ListAddonMeta()
+		assert.NoError(t, err)
+		assert.Equal(t, 2, len(meta["fluxcd"].Items))
+		// Verify the internal fileData map was populated
+		_, metadataExists := m.fileData["metadata.yaml"]
+		assert.True(t, metadataExists, "metadata.yaml should exist in fileData map")
+		_, parameterExists := m.fileData["resources/parameter.cue"]
+		assert.True(t, parameterExists, "resources/parameter.cue should exist in fileData map")
+	})
+
+	t.Run("ReadFile", func(t *testing.T) {
+		// Ensure ListAddonMeta has been called to populate the internal map
+		_, _ = m.ListAddonMeta()
+
+		t.Run("read by exact name", func(t *testing.T) {
+			metaFile, err := m.ReadFile("metadata.yaml")
+			assert.NoError(t, err)
+			assert.NotEmpty(t, metaFile)
+		})
+
+		t.Run("read by prefixed name", func(t *testing.T) {
+			parameterData, err := m.ReadFile("fluxcd/resources/parameter.cue")
+			assert.NoError(t, err)
+			assert.NotEmpty(t, parameterData)
+		})
+	})
+}
+
+func TestMemoryReader_RelativePath(t *testing.T) {
+	testCases := map[string]struct {
+		addonName string
+		itemName  string
+		expected  string
+	}{
+		"name without prefix": {
+			addonName: "my-addon",
+			itemName:  "metadata.yaml",
+			expected:  filepath.Join("my-addon", "metadata.yaml"),
+		},
+		"name with prefix": {
+			addonName: "my-addon",
+			itemName:  "my-addon/template.cue",
+			expected:  "my-addon/template.cue",
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			r := MemoryReader{Name: tc.addonName}
+			item := OSSItem{name: tc.itemName}
+			result := r.RelativePath(item)
+			assert.Equal(t, tc.expected, result)
+		})
+	}
+}
diff --git a/pkg/addon/source_test.go b/pkg/addon/source_test.go
index c1033eda..c344a5a7 100644
--- a/pkg/addon/source_test.go
+++ b/pkg/addon/source_test.go
@@ -22,6 +22,127 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+// mockItem implements the Item interface for testing
+type mockItem struct {
+	path     string
+	name     string
+	typeName string
+}
+
+func (m mockItem) GetType() string { return m.typeName }
+func (m mockItem) GetPath() string { return m.path }
+func (m mockItem) GetName() string { return m.name }
+
+// mockReader implements the AsyncReader interface for testing
+type mockReader struct{}
+
+func (m mockReader) ListAddonMeta() (map[string]SourceMeta, error) { return nil, nil }
+func (m mockReader) ReadFile(path string) (string, error)          { return "", nil }
+func (m mockReader) RelativePath(item Item) string                 { return item.GetPath() }
+
+func TestClassifyItemByPattern(t *testing.T) {
+	addonName := "my-addon"
+	meta := &SourceMeta{
+		Name: addonName,
+		Items: []Item{
+			mockItem{path: "my-addon/metadata.yaml"},
+			mockItem{path: "my-addon/template.cue"},
+			mockItem{path: "my-addon/definitions/def.cue"},
+			mockItem{path: "my-addon/resources/res.yaml"},
+			mockItem{path: "my-addon/schemas/schema.cue"},
+			mockItem{path: "my-addon/views/view.cue"},
+			mockItem{path: "my-addon/some-other-file.txt"}, // Should be ignored
+		},
+	}
+
+	r := mockReader{}
+	classified := ClassifyItemByPattern(meta, r)
+
+	assert.Contains(t, classified, MetadataFileName)
+	assert.Len(t, classified[MetadataFileName], 1)
+
+	assert.Contains(t, classified, AppTemplateCueFileName)
+	assert.Len(t, classified[AppTemplateCueFileName], 1)
+
+	assert.Contains(t, classified, DefinitionsDirName)
+	assert.Len(t, classified[DefinitionsDirName], 1)
+
+	assert.Contains(t, classified, ResourcesDirName)
+	assert.Len(t, classified[ResourcesDirName], 1)
+
+	assert.Contains(t, classified, DefSchemaName)
+	assert.Len(t, classified[DefSchemaName], 1)
+
+	assert.Contains(t, classified, ViewDirName)
+	assert.Len(t, classified[ViewDirName], 1)
+
+	assert.NotContains(t, classified, "some-other-file.txt")
+}
+
+func TestNewAsyncReader(t *testing.T) {
+	testCases := map[string]struct {
+		baseURL  string
+		bucket   string
+		repo     string
+		subPath  string
+		token    string
+		rdType   ReaderType
+		wantType interface{}
+		wantErr  bool
+	}{
+		"git type": {
+			baseURL:  "https://github.com/kubevela/catalog",
+			subPath:  "addons",
+			rdType:   gitType,
+			wantType: &gitReader{},
+			wantErr:  false,
+		},
+		"gitee type": {
+			baseURL:  "https://gitee.com/kubevela/catalog",
+			subPath:  "addons",
+			rdType:   giteeType,
+			wantType: &giteeReader{},
+			wantErr:  false,
+		},
+		"oss type": {
+			baseURL:  "oss-cn-hangzhou.aliyuncs.com",
+			bucket:   "kubevela-addons",
+			rdType:   ossType,
+			wantType: &ossReader{},
+			wantErr:  false,
+		},
+		"invalid url": {
+			baseURL: "://invalid-url",
+			rdType:  gitType,
+			wantErr: true,
+		},
+		"invalid type": {
+			baseURL: "https://github.com/kubevela/catalog",
+			rdType:  "invalid",
+			wantErr: true,
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			// Note: This test does not cover the gitlab case as it requires a live API call
+			// or a complex mock setup, which is beyond the scope of this unit test.
+			if tc.rdType == gitlabType {
+				t.Skip("Skipping gitlab test in this unit test suite.")
+			}
+
+			reader, err := NewAsyncReader(tc.baseURL, tc.bucket, tc.repo, tc.subPath, tc.token, tc.rdType)
+
+			if tc.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				assert.IsType(t, tc.wantType, reader)
+			}
+		})
+	}
+}
+
 func TestPathWithParent(t *testing.T) {
 	testCases := []struct {
 		readPath       string
diff --git a/pkg/addon/utils_test.go b/pkg/addon/utils_test.go
index e1c462d1..673f0ac7 100644
--- a/pkg/addon/utils_test.go
+++ b/pkg/addon/utils_test.go
@@ -26,6 +26,7 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/assert"
+	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chartutil"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -402,6 +403,90 @@ func TestCheckAddonPackageValid(t *testing.T) {
 	}
 }
 
+func TestIsRegistryFuncs(t *testing.T) {
+	t.Run("IsLocalRegistry", func(t *testing.T) {
+		assert.True(t, IsLocalRegistry(Registry{Name: "local"}))
+		assert.False(t, IsLocalRegistry(Registry{Name: "KubeVela"}))
+	})
+	t.Run("IsVersionRegistry", func(t *testing.T) {
+		assert.True(t, IsVersionRegistry(Registry{Helm: &HelmSource{}}))
+		assert.False(t, IsVersionRegistry(Registry{Git: &GitAddonSource{}}))
+		assert.False(t, IsVersionRegistry(Registry{}))
+	})
+}
+
+func TestInstallOptions(t *testing.T) {
+	t.Run("SkipValidateVersion", func(t *testing.T) {
+		installer := &Installer{}
+		SkipValidateVersion(installer)
+		assert.True(t, installer.skipVersionValidate)
+	})
+	t.Run("DryRunAddon", func(t *testing.T) {
+		installer := &Installer{}
+		DryRunAddon(installer)
+		assert.True(t, installer.dryRun)
+	})
+	t.Run("OverrideDefinitions", func(t *testing.T) {
+		installer := &Installer{}
+		OverrideDefinitions(installer)
+		assert.True(t, installer.overrideDefs)
+	})
+}
+
+func TestProduceDefConflictError(t *testing.T) {
+	t.Run("no conflicts", func(t *testing.T) {
+		err := produceDefConflictError(map[string]string{})
+		assert.NoError(t, err)
+	})
+	t.Run("with conflicts", func(t *testing.T) {
+		conflicts := map[string]string{
+			"def1": "error message 1",
+			"def2": "error message 2",
+		}
+		err := produceDefConflictError(conflicts)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "error message 1")
+		assert.Contains(t, err.Error(), "error message 2")
+		assert.Contains(t, err.Error(), "--override-definitions")
+	})
+}
+
+func TestGenerateChartMetadata(t *testing.T) {
+	addonDir := t.TempDir()
+
+	// Corrected YAML content with no leading whitespace
+	metaFileContent := `name: my-addon
+version: 1.2.3
+description: my addon description
+icon: http://my-icon.com
+url: http://my-home.com
+tags:
+  - tag1
+  - tag2
+system:
+  vela: ">=1.5.0"
+  kubernetes: "1.20.0"
+`
+	err := os.WriteFile(filepath.Join(addonDir, MetadataFileName), []byte(metaFileContent), 0644)
+	assert.NoError(t, err)
+
+	chartMeta, err := generateChartMetadata(addonDir)
+	assert.NoError(t, err)
+	assert.NotNil(t, chartMeta)
+
+	assert.Equal(t, "my-addon", chartMeta.Name)
+	assert.Equal(t, "1.2.3", chartMeta.Version)
+	assert.Equal(t, "my addon description", chartMeta.Description)
+	assert.Equal(t, "library", chartMeta.Type)
+	assert.Equal(t, chart.APIVersionV2, chartMeta.APIVersion)
+	assert.Equal(t, "http://my-icon.com", chartMeta.Icon)
+	assert.Equal(t, "http://my-home.com", chartMeta.Home)
+	assert.Equal(t, []string{"tag1", "tag2"}, chartMeta.Keywords)
+	assert.Equal(t, ">=1.5.0", chartMeta.Annotations[velaSystemRequirement])
+	assert.Equal(t, "1.20.0", chartMeta.Annotations[kubernetesSystemRequirement])
+	assert.Equal(t, "my-addon", chartMeta.Annotations[addonSystemRequirement])
+}
+
 const (
 	compDefYaml = `
 apiVersion: core.oam.dev/v1beta1
diff --git a/pkg/addon/versioned_registry_test.go b/pkg/addon/versioned_registry_test.go
index 34abd314..a15f8e67 100644
--- a/pkg/addon/versioned_registry_test.go
+++ b/pkg/addon/versioned_registry_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package addon
 
 import (
+	"context"
 	"encoding/base64"
 	"net/http"
 	"net/http/httptest"
@@ -28,6 +29,7 @@ import (
 	"helm.sh/helm/v3/pkg/repo"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/oam-dev/kubevela/pkg/utils/common"
 	"github.com/oam-dev/kubevela/pkg/utils/helm"
@@ -227,3 +229,159 @@ func TestToVersionedRegistry(t *testing.T) {
 	assert.EqualError(t, err, "registry 'git-based-registry' is not a versioned registry")
 	assert.Nil(t, actual)
 }
+
+func TestResolveAddonListFromIndex(t *testing.T) {
+	r := &versionedRegistry{name: "test-repo"}
+	indexFile := &repo.IndexFile{
+		Entries: map[string]repo.ChartVersions{
+			"addon-good": {
+				{Metadata: &chart.Metadata{Name: "addon-good", Version: "1.0.0", Description: "old desc", Icon: "old_icon", Keywords: []string{"tag1"}}},
+				{Metadata: &chart.Metadata{Name: "addon-good", Version: "1.2.0", Description: "latest desc", Icon: "latest_icon", Keywords: []string{"tag2"}}},
+				{Metadata: &chart.Metadata{Name: "addon-good", Version: "1.1.0", Description: "middle desc", Icon: "middle_icon", Keywords: []string{"tag3"}}},
+			},
+			"addon-empty": {},
+			"addon-single": {
+				{Metadata: &chart.Metadata{Name: "addon-single", Version: "0.1.0", Description: "single desc"}},
+			},
+		},
+	}
+
+	result := r.resolveAddonListFromIndex(r.name, indexFile)
+
+	assert.Equal(t, 2, len(result))
+
+	var addonGood, addonSingle *UIData
+	for _, addon := range result {
+		if addon.Name == "addon-good" {
+			addonGood = addon
+		}
+		if addon.Name == "addon-single" {
+			addonSingle = addon
+		}
+	}
+
+	require.NotNil(t, addonGood)
+	assert.Equal(t, "addon-good", addonGood.Name)
+	assert.Equal(t, "test-repo", addonGood.RegistryName)
+	assert.Equal(t, "1.2.0", addonGood.Version)
+	assert.Equal(t, "latest desc", addonGood.Description)
+	assert.Equal(t, "latest_icon", addonGood.Icon)
+	assert.Equal(t, []string{"tag2"}, addonGood.Tags)
+	assert.Equal(t, []string{"1.2.0", "1.1.0", "1.0.0"}, addonGood.AvailableVersions)
+
+	require.NotNil(t, addonSingle)
+	assert.Equal(t, "addon-single", addonSingle.Name)
+	assert.Equal(t, "0.1.0", addonSingle.Version)
+	assert.Equal(t, []string{"0.1.0"}, addonSingle.AvailableVersions)
+}
+
+// setupAddonTestServer creates a mock HTTP server for testing addon loading.
+// It can simulate success, 404 errors, or serving corrupt data based on the handlerType.
+func setupAddonTestServer(t *testing.T, handlerType string) string {
+	var server *httptest.Server
+	// This handler rewrites URLs in the index file to point to the server it's running on.
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if strings.Contains(r.URL.Path, "index.yaml") {
+			content, err := os.ReadFile("./testdata/multiversion-helm-repo/index.yaml")
+			assert.NoError(t, err)
+			newContent := strings.ReplaceAll(string(content), "http://127.0.0.1:18083/multi", server.URL)
+			_, err = w.Write([]byte(newContent))
+			assert.NoError(t, err)
+			return
+		}
+
+		// After serving the index, the next request depends on the handler type.
+		switch handlerType {
+		case "success":
+			multiVersionHandler(w, r)
+		case "notfound":
+			w.WriteHeader(http.StatusNotFound)
+		case "corrupt":
+			_, err := w.Write([]byte("this is not a valid tgz file"))
+			assert.NoError(t, err)
+		default:
+			t.Errorf("unknown handler type: %s", handlerType)
+		}
+	})
+	server = httptest.NewServer(handler)
+	t.Cleanup(server.Close)
+	return server.URL
+}
+
+func TestLoadAddon(t *testing.T) {
+	testCases := []struct {
+		name           string
+		handlerType    string
+		addonName      string
+		addonVersion   string
+		expectErr      bool
+		expectedErrStr string
+		checkFunc      func(t *testing.T, pkg *WholeAddonPackage)
+	}{
+		{
+			name:         "Success case",
+			handlerType:  "success",
+			addonName:    "fluxcd",
+			addonVersion: "1.0.0",
+			expectErr:    false,
+			checkFunc: func(t *testing.T, pkg *WholeAddonPackage) {
+				assert.NotNil(t, pkg)
+				assert.Equal(t, "fluxcd", pkg.Name)
+				assert.Equal(t, "1.0.0", pkg.Version)
+				assert.NotEmpty(t, pkg.YAMLTemplates)
+				assert.Equal(t, []string{"2.0.0", "1.0.0"}, pkg.AvailableVersions)
+			},
+		},
+		{
+			name:           "Version not found",
+			handlerType:    "success",
+			addonName:      "fluxcd",
+			addonVersion:   "3.0.0",
+			expectErr:      true,
+			expectedErrStr: "specified version 3.0.0 for addon fluxcd not exist",
+		},
+		{
+			name:           "Chart download fails",
+			handlerType:    "notfound",
+			addonName:      "fluxcd",
+			addonVersion:   "1.0.0",
+			expectErr:      true,
+			expectedErrStr: ErrFetch.Error(),
+		},
+		{
+			name:           "Corrupt chart file",
+			handlerType:    "corrupt",
+			addonName:      "fluxcd",
+			addonVersion:   "1.0.0",
+			expectErr:      true,
+			expectedErrStr: ErrFetch.Error(),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			serverURL := setupAddonTestServer(t, tc.handlerType)
+			reg := &versionedRegistry{
+				name: "test-registry",
+				url:  serverURL,
+				h:    helm.NewHelperWithCache(),
+				Opts: nil,
+			}
+
+			pkg, err := reg.loadAddon(context.Background(), tc.addonName, tc.addonVersion)
+
+			if tc.expectErr {
+				assert.Error(t, err)
+				if tc.expectedErrStr != "" {
+					assert.Contains(t, err.Error(), tc.expectedErrStr)
+				}
+			} else {
+				assert.NoError(t, err)
+			}
+
+			if tc.checkFunc != nil {
+				tc.checkFunc(t, pkg)
+			}
+		})
+	}
+}
diff --git a/pkg/appfile/appfile_test.go b/pkg/appfile/appfile_test.go
index 521a5aa9..cca5e852 100644
--- a/pkg/appfile/appfile_test.go
+++ b/pkg/appfile/appfile_test.go
@@ -19,26 +19,33 @@ package appfile
 import (
 	"context"
 	"encoding/json"
+	"strings"
 	"testing"
 
 	"cuelang.org/go/cue/cuecontext"
+	"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
 	"github.com/crossplane/crossplane-runtime/pkg/test"
 	"github.com/google/go-cmp/cmp"
 	terraformtypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
 	terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 	appsv1 "k8s.io/api/apps/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"sigs.k8s.io/yaml"
 
 	"github.com/kubevela/workflow/pkg/cue/model"
 
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	oamtypes "github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/cue/definition"
@@ -47,8 +54,9 @@ import (
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 )
 
-var _ = Describe("Test Workflow", func() {
-	It("generate workflow task runners", func() {
+func TestWorkflow(t *testing.T) {
+	k8sClient := fake.NewClientBuilder().Build()
+	t.Run("generate workflow task runners", func(t *testing.T) {
 		workflowStepDef := v1beta1.WorkflowStepDefinition{
 			Spec: v1beta1.WorkflowStepDefinitionSpec{
 				Schematic: &common.Schematic{
@@ -65,7 +73,7 @@ wait: op.#ConditionalWait & {
 		workflowStepDef.Name = "test-wait"
 		workflowStepDef.Namespace = "default"
 		err := k8sClient.Create(context.Background(), &workflowStepDef)
-		Expect(err).To(BeNil())
+		assert.NoError(t, err)
 
 		notCueStepDef := v1beta1.WorkflowStepDefinition{
 			Spec: v1beta1.WorkflowStepDefinitionSpec{
@@ -76,12 +84,12 @@ wait: op.#ConditionalWait & {
 		notCueStepDef.Name = "not-cue"
 		notCueStepDef.Namespace = "default"
 		err = k8sClient.Create(context.Background(), &notCueStepDef)
-		Expect(err).To(BeNil())
+		assert.NoError(t, err)
 	})
-})
+}
 
-var _ = Describe("Test Terraform schematic appfile", func() {
-	It("workload capability is Terraform", func() {
+func TestTerraformSchematicAppfile(t *testing.T) {
+	t.Run("workload capability is Terraform", func(t *testing.T) {
 		var (
 			ns            = "default"
 			compName      = "sample-db"
@@ -202,10 +210,10 @@ variable "password" {
 
 		comps, err := af.GenerateComponentManifests()
 		diff := cmp.Diff(comps[0], expectCompManifest)
-		Expect(diff).ShouldNot(BeEmpty())
-		Expect(err).Should(BeNil())
+		assert.NotEmpty(t, diff)
+		assert.NoError(t, err)
 	})
-})
+}
 
 func TestSetParameterValuesToKubeObj(t *testing.T) {
 	tests := map[string]struct {
@@ -301,18 +309,20 @@ func TestSetParameterValuesToKubeObj(t *testing.T) {
 			obj := tc.obj.DeepCopy()
 			err := setParameterValuesToKubeObj(obj, tc.values)
 			if diff := cmp.Diff(tc.wantObj, *obj); diff != "" {
-				t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
+				t.Errorf(
+					"\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
 			}
 			if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
-				t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
+				t.Errorf(
+					"\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
 			}
 		})
 	}
 
 }
 
-var _ = Describe("Test evalWorkloadWithContext", func() {
-	It("workload capability is Terraform", func() {
+func TestEvalWorkloadWithContext(t *testing.T) {
+	t.Run("workload capability is Terraform", func(t *testing.T) {
 		var (
 			ns       = "default"
 			compName = "sample-db"
@@ -404,11 +414,11 @@ variable "password" {
 		}, args.wl.Name)
 		pCtx := NewBasicContext(ctxData, args.wl.Params)
 		comp, err := evalWorkloadWithContext(pCtx, args.wl, ns, args.appName)
-		Expect(comp.ComponentOutput).ShouldNot(BeNil())
-		Expect(comp.Name).Should(Equal(""))
-		Expect(err).Should(BeNil())
+		assert.NotNil(t, comp.ComponentOutput)
+		assert.Equal(t, "", comp.Name)
+		assert.NoError(t, err)
 	})
-})
+}
 
 func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
 	var (
@@ -700,16 +710,16 @@ func TestBaseGenerateComponent(t *testing.T) {
 	}, wlName)
 	pContext := NewBasicContext(ctxData, nil)
 	base := `
-	apiVersion: "apps/v1"
-	kind:       "Deployment"
-	spec: {
-		template: {
-			spec: containers: [{
-				image: "nginx"
-			}]
+		apiVersion: "apps/v1"
+		kind:       "Deployment"
+		spec: {
+			template: {
+				spec: containers: [{
+					image: "nginx"
+				}]
+			}
 		}
-	}
-`
+	`
 	inst := cuecontext.New().CompileString(base)
 	bs, _ := model.NewBase(inst.Value())
 	err := pContext.SetBase(bs)
@@ -738,8 +748,8 @@ if context.componentType == "stateless" {
 	assert.Equal(t, cm.ComponentOutputsAndTraits[0].Object["publishVersion"], publishVersion)
 }
 
-var _ = Describe("Test use context.appLabels& context.appAnnotations in componentDefinition ", func() {
-	It("Test generate AppConfig resources from ", func() {
+func TestAppLabelsAndAnnotationsInComponentDefinition(t *testing.T) {
+	t.Run("generate AppConfig resources", func(t *testing.T) {
 		af := &Appfile{
 			Name:      "app",
 			Namespace: "ns",
@@ -769,7 +779,6 @@ var _ = Describe("Test use context.appLabels& context.appAnnotations in componen
 								selector: matchLabels: {
 									"app.oam.dev/component": context.name
 								}
-						  
 								template: {
 									metadata: {
 										labels: {
@@ -783,47 +792,971 @@ var _ = Describe("Test use context.appLabels& context.appAnnotations in componen
 											}
 										}
 									}
-						  
 									spec: {
-										containers: [{
+										containers: [{ 
 											name:  context.name
 											image: parameter.image
-						  
 											if parameter["cmd"] != _|_ {
 												command: parameter.cmd
 											}
 										}]
 									}
 								}
-						  
 								selector:
 									matchLabels:
 										"app.oam.dev/component": context.name
 							}
 						  }
-						  
-						  parameter: {
+						  parameter: { 
 							// +usage=Which image would you like to use for your service
 							// +short=i
 							image: string
-						  
 							cmd?: [...string]
 						  }`},
 				},
 			},
 		}
-		By("Generate ComponentManifests")
+
+		// Generate ComponentManifests
 		componentManifests, err := af.GenerateComponentManifests()
-		Expect(err).To(BeNil())
-		By("Verify expected ComponentManifest")
+		if err != nil {
+			t.Fatalf("GenerateComponentManifests() error = %v", err)
+		}
+
+		// Verify expected ComponentManifest
 		deployment := &appsv1.Deployment{}
-		runtime.DefaultUnstructuredConverter.FromUnstructured(componentManifests[0].ComponentOutput.Object, deployment)
+		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(
+			componentManifests[0].ComponentOutput.Object, deployment); err != nil {
+			t.Fatalf("Failed to convert to Deployment: %v", err)
+		}
+
 		labels := deployment.Spec.Template.Labels
 		annotations := deployment.Spec.Template.Annotations
-		Expect(cmp.Diff(len(labels), 2)).Should(BeEmpty())
-		Expect(cmp.Diff(len(annotations), 2)).Should(BeEmpty())
-		Expect(cmp.Diff(labels["lk1"], "lv1")).Should(BeEmpty())
-		Expect(cmp.Diff(annotations["ak1"], "av1")).Should(BeEmpty())
+
+		if diff := cmp.Diff(len(labels), 2); diff != "" {
+			t.Errorf("labels length mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(len(annotations), 2); diff != "" {
+			t.Errorf("annotations length mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(labels["lk1"], "lv1"); diff != "" {
+			t.Errorf("label mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(annotations["ak1"], "av1"); diff != "" {
+			t.Errorf("annotation mismatch (-want +got): %s", diff)
+		}
+	})
+}
+
+func TestIsNotFoundInAppFile(t *testing.T) {
+	tests := map[string]struct {
+		err  error
+		want bool
+	}{
+		"ErrorIsNil": {
+			err:  nil,
+			want: false,
+		},
+		"ErrorContainsSubstring": {
+			err:  errors.New("some error not found in appfile"),
+			want: true,
+		},
+		"ErrorNotContainsSubstring": {
+			err:  errors.New("some other error"),
+			want: false,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			got := IsNotFoundInAppFile(tc.err)
+			assert.Equal(t, tc.want, got)
+		})
+	}
+}
+
+func TestPrepareProcessContext(t *testing.T) {
+	ctxData := process.ContextData{
+		AppName:   "test-app",
+		Namespace: "test-ns",
+	}
+
+	t.Run("Success case with nil initial context", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+		assert.Nil(t, comp.Ctx)
+
+		ctx, err := PrepareProcessContext(comp, ctxData)
+
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx)
+		assert.NotNil(t, comp.Ctx)
+		assert.Equal(t, ctx, comp.Ctx)
+	})
+
+	t.Run("Error case from invalid CUE template", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string & int}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+
+		_, err := PrepareProcessContext(comp, ctxData)
+
+		assert.Error(t, err)
+		assert.ErrorContains(t, err, "conflicting values string and int")
 	})
 
-})
+	t.Run("Use existing context if not nil", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+
+		// First call to populate context
+		ctx1, err := PrepareProcessContext(comp, ctxData)
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx1)
+
+		// Second call
+		ctx2, err := PrepareProcessContext(comp, ctxData)
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx2)
+
+		// Check if the context object is the same
+		assert.Same(t, ctx1, ctx2)
+	})
+}
+
+func TestLoadDynamicComponent(t *testing.T) {
+	ctx := context.Background()
+	cli := fake.NewClientBuilder().Build()
+
+	t.Run("Non-ref-objects component", func(t *testing.T) {
+		af := &Appfile{}
+		comp := &common.ApplicationComponent{
+			Type: "webservice",
+		}
+		resultComp, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.NoError(t, err)
+		assert.Equal(t, comp, resultComp)
+	})
+
+	t.Run("Invalid properties", func(t *testing.T) {
+		af := &Appfile{}
+		comp := &common.ApplicationComponent{
+			Type: v1alpha1.RefObjectsComponentType,
+			Properties: &runtime.RawExtension{
+				Raw: []byte(`{"objects": "not-an-array"}`),
+			},
+		}
+		_, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "invalid ref-objects component properties")
+	})
+
+	t.Run("Success with URL selectors", func(t *testing.T) {
+		objWithURL := &unstructured.Unstructured{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "Secret",
+				"metadata": map[string]interface{}{
+					"name":      "secret1",
+					"namespace": "default",
+					"annotations": map[string]interface{}{
+						oam.AnnotationResourceURL: "http://example.com/secret.yaml",
+					},
+				},
+			},
+		}
+		af := &Appfile{
+			Namespace:       "default",
+			ReferredObjects: []*unstructured.Unstructured{objWithURL},
+			app:             &v1beta1.Application{},
+		}
+		comp := &common.ApplicationComponent{
+			Name: "my-comp",
+			Type: v1alpha1.RefObjectsComponentType,
+			Properties: &runtime.RawExtension{
+				Raw: []byte(`{"urls": ["http://example.com/secret.yaml"]}`),
+			},
+		}
+
+		resultComp, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.NoError(t, err)
+
+		refObjList := &common.ReferredObjectList{}
+		err = json.Unmarshal(resultComp.Properties.Raw, refObjList)
+		assert.NoError(t, err)
+		assert.Len(t, refObjList.Objects, 1)
+
+		u := &unstructured.Unstructured{}
+		err = u.UnmarshalJSON(refObjList.Objects[0].Raw)
+		assert.NoError(t, err)
+		assert.Equal(t, "Secret", u.GetKind())
+		assert.Equal(t, "secret1", u.GetName())
+	})
+}
+
+func TestPolicyClient(t *testing.T) {
+	ctx := context.Background()
+	policy := &v1alpha1.Policy{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-policy", Namespace: "default"},
+		Type:       "override",
+	}
+	policyKey := client.ObjectKeyFromObject(policy)
+
+	cm := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"},
+		Data:       map[string]string{"key": "value"},
+	}
+	cmKey := client.ObjectKeyFromObject(cm)
+
+	t.Run("With AppRevision, policy in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+			ExternalPolicies: map[string]*v1alpha1.Policy{
+				policyKey.String(): policy,
+			},
+		}
+		cli := fake.NewClientBuilder().Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.NoError(t, err)
+		assert.Equal(t, policy, retrievedPolicy)
+	})
+
+	t.Run("With AppRevision, policy not in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision:      &v1beta1.ApplicationRevision{},
+			ExternalPolicies: map[string]*v1alpha1.Policy{},
+		}
+		// The policy exists in the client, but it should not be returned
+		cli := fake.NewClientBuilder().WithObjects(policy).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("With AppRevision, get non-policy object", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := policyClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+
+	t.Run("Without AppRevision, policy in client", func(t *testing.T) {
+		af := &Appfile{
+			ExternalPolicies: make(map[string]*v1alpha1.Policy),
+		}
+		cli := fake.NewClientBuilder().WithObjects(policy).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.NoError(t, err)
+		assert.Equal(t, policy, retrievedPolicy)
+
+		// Check if the policy is now cached
+		cachedPolicy, found := af.ExternalPolicies[policyKey.String()]
+		assert.True(t, found)
+		assert.Equal(t, policy, cachedPolicy)
+	})
+
+	t.Run("Without AppRevision, policy not in client", func(t *testing.T) {
+		af := &Appfile{
+			ExternalPolicies: make(map[string]*v1alpha1.Policy),
+		}
+		cli := fake.NewClientBuilder().Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("Without AppRevision, get non-policy object", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := policyClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+}
+
+func TestWorkflowClient(t *testing.T) {
+	ctx := context.Background()
+	workflow := &workflowv1alpha1.Workflow{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-workflow", Namespace: "default"},
+	}
+	workflowKey := client.ObjectKeyFromObject(workflow)
+
+	cm := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"},
+		Data:       map[string]string{"key": "value"},
+	}
+	cmKey := client.ObjectKeyFromObject(cm)
+
+	t.Run("With AppRevision, workflow in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision:      &v1beta1.ApplicationRevision{},
+			ExternalWorkflow: workflow,
+		}
+		cli := fake.NewClientBuilder().Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.NoError(t, err)
+		assert.Equal(t, workflow, retrievedWorkflow)
+	})
+
+	t.Run("With AppRevision, workflow not in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		// The workflow exists in the client, but it should not be returned
+		cli := fake.NewClientBuilder().WithObjects(workflow).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("With AppRevision, get non-workflow object", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := workflowClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+
+	t.Run("Without AppRevision, workflow in client", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(workflow).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.NoError(t, err)
+		assert.Equal(t, workflow, retrievedWorkflow)
+
+		// Check if the workflow is now cached
+		assert.Equal(t, workflow, af.ExternalWorkflow)
+	})
+
+	t.Run("Without AppRevision, workflow not in client", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("Without AppRevision, get non-workflow object", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := workflowClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+}
+
+func TestSetWorkloadRefToTrait(t *testing.T) {
+	wlRef := corev1.ObjectReference{
+		APIVersion: "apps/v1",
+		Kind:       "Deployment",
+		Name:       "my-workload",
+	}
+
+	tests := map[string]struct {
+		reason    string
+		appfile   *Appfile
+		trait     *unstructured.Unstructured
+		wantTrait *unstructured.Unstructured
+		wantErr   error
+	}{
+		"AuxiliaryWorkloadTrait": {
+			reason:  "Should do nothing for auxiliary-workload traits",
+			appfile: &Appfile{},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: definition.AuxiliaryWorkload,
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: definition.AuxiliaryWorkload,
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"TraitDefinitionNotFound": {
+			reason: "Should return error if TraitDefinition is not found",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: errors.Errorf("TraitDefinition %s not found in appfile", "my-trait"),
+		},
+		"WorkloadRefPathIsEmpty": {
+			reason: "Should do nothing if workloadRefPath is empty",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"SetWorkloadRefSucceeds": {
+			reason: "Should set workload reference correctly",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec.workloadRef",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+				"spec": map[string]interface{}{},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+				"spec": map[string]interface{}{
+					"workloadRef": map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"name":       "my-workload",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"SetWorkloadRefWithSuffixedTraitType": {
+			reason: "Should find the base trait definition and set workload reference",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec.workloadRef",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait-abcde",
+					},
+				},
+				"spec": map[string]interface{}{},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait-abcde",
+					},
+				},
+				"spec": map[string]interface{}{
+					"workloadRef": map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"name":       "my-workload",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"InvalidWorkloadRefPath": {
+			reason: "Should return an error for an invalid workloadRefPath",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec[workloadRef", // Invalid path
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: fieldpath.Pave(map[string]interface{}{}).SetValue("spec[workloadRef", wlRef),
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			traitCopy := tc.trait.DeepCopy()
+			err := tc.appfile.setWorkloadRefToTrait(wlRef, traitCopy)
+
+			if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
+				t.Errorf("\n%s\nsetWorkloadRefToTrait(...): -want error, +got error:\n%s\n", tc.reason, diff)
+			}
+			if diff := cmp.Diff(tc.wantTrait, traitCopy); diff != "" {
+				t.Errorf("\n%s\nsetWorkloadRefToTrait(...): -want trait, +got trait:\n%s\n", tc.reason, diff)
+			}
+		})
+	}
+}
+
+func TestSetOAMContract(t *testing.T) {
+	baseAppfile := &Appfile{
+		Name:            "test-app",
+		Namespace:       "test-ns",
+		AppRevisionName: "test-app-v1",
+		AppLabels: map[string]string{
+			"app-label": "app-label-val",
+		},
+		AppAnnotations: map[string]string{
+			"app-annot": "app-annot-val",
+		},
+		RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+			"my-trait": {
+				Spec: v1beta1.TraitDefinitionSpec{
+					WorkloadRefPath: "spec.workloadRef",
+				},
+			},
+			"no-ref-trait": {
+				Spec: v1beta1.TraitDefinitionSpec{},
+			},
+		},
+	}
+
+	baseWorkload := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "apps/v1",
+			"kind":       "Deployment",
+			"metadata": map[string]interface{}{
+				"name": "my-workload",
+			},
+		},
+	}
+
+	baseTrait := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"labels": map[string]interface{}{
+					oam.TraitTypeLabel: "my-trait",
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		reason   string
+		appfile  *Appfile
+		comp     *oamtypes.ComponentManifest
+		wantComp *oamtypes.ComponentManifest
+		wantErr  bool
+	}{
+		"SuccessBasic": {
+			reason:  "A basic workload and trait should be assembled correctly",
+			appfile: baseAppfile,
+			comp: &oamtypes.ComponentManifest{
+				Name:            "my-comp",
+				ComponentOutput: baseWorkload.DeepCopy(),
+				ComponentOutputsAndTraits: []*unstructured.Unstructured{
+					baseTrait.DeepCopy(),
+				},
+			},
+			wantComp: &oamtypes.ComponentManifest{
+				Name: "my-comp",
+				ComponentOutput: &unstructured.Unstructured{
+					Object: map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"metadata": map[string]interface{}{
+							"name":      "my-workload",
+							"namespace": "test-ns",
+							"labels": map[string]interface{}{
+								"app-label":              "app-label-val",
+								oam.LabelAppName:         "test-app",
+								oam.LabelAppNamespace:    "test-ns",
+								oam.LabelAppRevision:     "test-app-v1",
+								oam.LabelAppComponent:    "my-comp",
+								oam.LabelOAMResourceType: oam.ResourceTypeWorkload,
+							},
+							"annotations": map[string]interface{}{
+								"app-annot": "app-annot-val",
+							},
+						},
+					},
+				},
+				ComponentOutputsAndTraits: []*unstructured.Unstructured{
+					{
+						Object: map[string]interface{}{
+							"apiVersion": "v1",
+							"kind":       "Service",
+							"metadata": map[string]interface{}{
+								"name":      "", // Name will be generated
+								"namespace": "test-ns",
+								"labels": map[string]interface{}{
+									"app-label":              "app-label-val",
+									oam.TraitTypeLabel:       "my-trait",
+									oam.LabelAppName:         "test-app",
+									oam.LabelAppNamespace:    "test-ns",
+									oam.LabelAppRevision:     "test-app-v1",
+									oam.LabelAppComponent:    "my-comp",
+									oam.LabelOAMResourceType: oam.ResourceTypeTrait,
+								},
+								"annotations": map[string]interface{}{
+									"app-annot": "app-annot-val",
+								},
+							},
+							"spec": map[string]interface{}{
+								"workloadRef": map[string]interface{}{
+									"apiVersion": "apps/v1",
+									"kind":       "Deployment",
+									"name":       "my-workload",
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			compCopy := deepCopyComponentManifest(tc.comp)
+			err := tc.appfile.SetOAMContract(compCopy)
+
+			if (err != nil) != tc.wantErr {
+				t.Errorf("\n%s\nSetOAMContract(...): want error? %v, got error: %v\n", tc.reason, tc.wantErr, err)
+			}
+
+			if name == "SuccessBasic" || name == "SuccessNameGeneration" {
+				for i, trait := range compCopy.ComponentOutputsAndTraits {
+					gotName := trait.GetName()
+					wantTrait := tc.wantComp.ComponentOutputsAndTraits[i]
+					traitType := trait.GetLabels()[oam.TraitTypeLabel]
+					expectedPrefix := compCopy.Name + "-" + traitType + "-"
+
+					if !strings.HasPrefix(gotName, expectedPrefix) {
+						t.Errorf("\n%s\nSetOAMContract(...): trait name %q does not have expected prefix %q", tc.reason, gotName, expectedPrefix)
+					}
+					trait.SetName("")
+					wantTrait.SetName("")
+				}
+			}
+
+			if !tc.wantErr {
+				if diff := cmp.Diff(tc.wantComp.ComponentOutput, compCopy.ComponentOutput); diff != "" {
+					t.Errorf("\n%s\nSetOAMContract(...): -want workload, +got workload:\n%s\n", tc.reason, diff)
+				}
+				if diff := cmp.Diff(tc.wantComp.ComponentOutputsAndTraits, compCopy.ComponentOutputsAndTraits); diff != "" {
+					t.Errorf("\n%s\nSetOAMContract(...): -want traits, +got traits:\n%s\n", tc.reason, diff)
+				}
+			}
+		})
+	}
+}
+
+func deepCopyComponentManifest(in *oamtypes.ComponentManifest) *oamtypes.ComponentManifest {
+	if in == nil {
+		return nil
+	}
+	out := &oamtypes.ComponentManifest{
+		Name: in.Name,
+	}
+	if in.ComponentOutput != nil {
+		out.ComponentOutput = in.ComponentOutput.DeepCopy()
+	}
+	if in.ComponentOutputsAndTraits != nil {
+		out.ComponentOutputsAndTraits = make([]*unstructured.Unstructured, len(in.ComponentOutputsAndTraits))
+		for i, trait := range in.ComponentOutputsAndTraits {
+			if trait != nil {
+				out.ComponentOutputsAndTraits[i] = trait.DeepCopy()
+			}
+		}
+	}
+	return out
+}
+
+func TestGenerateComponentManifests(t *testing.T) {
+	baseEngine := definition.NewWorkloadAbstractEngine("test-engine")
+	baseTraitEngine := definition.NewTraitAbstractEngine("scaler")
+	baseTemplate := &Template{
+		TemplateStr: `
+			output: {
+				apiVersion: "apps/v1"
+				kind:       "Deployment"
+				spec: {
+					selector: matchLabels: {
+						"app.oam.dev/component": context.name
+					}
+					template: {
+						metadata: labels: {
+							"app.oam.dev/component": context.name
+						}
+						spec: containers: [{
+							name:  context.name
+							image: parameter.image
+						}]
+					}
+				}
+			}
+			parameter: {
+				image: string
+			}`,
+	}
+	baseTraitTemplate := &Trait{
+		Name:   "scaler",
+		engine: baseTraitEngine,
+		Template: `
+			outputs: scaler: {
+				apiVersion: "autoscaling/v2beta2"
+				kind:       "HorizontalPodAutoscaler"
+				spec: {
+					scaleTargetRef: {
+						apiVersion: "apps/v1"
+						kind:       "Deployment"
+						name:       context.name
+					}
+					minReplicas: parameter.min
+					maxReplicas: parameter.max
+				}
+			}
+			parameter: {
+				min: *1 | int
+				max: *10 | int
+			}
+		`,
+		Params: map[string]interface{}{
+			"min": 2,
+			"max": 15,
+		},
+	}
+
+	type fields struct {
+		Appfile *Appfile
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr bool
+	}{
+		{
+			name: "Test generate component manifests",
+			fields: fields{
+				Appfile: &Appfile{
+					Name:            "test-app",
+					Namespace:       "test-ns",
+					AppRevisionName: "test-app-v1",
+					ParsedComponents: []*Component{
+						{
+							Name:   "test-comp",
+							Type:   "worker",
+							Params: map[string]interface{}{"image": "nginx:1.10"},
+							engine: baseEngine,
+							FullTemplate: &Template{
+								TemplateStr: baseTemplate.TemplateStr,
+							},
+							Traits: []*Trait{baseTraitTemplate},
+						},
+					},
+					RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+						"scaler": {
+							Spec: v1beta1.TraitDefinitionSpec{
+								WorkloadRefPath: "spec.scaleTargetRef",
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			af := tt.fields.Appfile
+			got, err := af.GenerateComponentManifests()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GenerateComponentManifests() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			assert.NoError(t, err)
+			assert.Equal(t, len(af.ParsedComponents), len(got))
+			assert.Equal(t, "test-comp", got[0].Name)
+			assert.Equal(t, "Deployment", got[0].ComponentOutput.GetKind())
+			assert.Equal(t, "test-comp", got[0].ComponentOutput.GetName())
+			assert.Equal(t, "test-ns", got[0].ComponentOutput.GetNamespace())
+			assert.Equal(t, "test-app", got[0].ComponentOutput.GetLabels()[oam.LabelAppName])
+			assert.Equal(t, 1, len(got[0].ComponentOutputsAndTraits))
+			trait := got[0].ComponentOutputsAndTraits[0]
+			assert.Equal(t, "HorizontalPodAutoscaler", trait.GetKind())
+			assert.True(t, strings.HasPrefix(trait.GetName(), "test-comp-scaler-"))
+			paved := fieldpath.Pave(trait.Object)
+			name, err := paved.GetString("spec.scaleTargetRef.name")
+			assert.NoError(t, err)
+			assert.Equal(t, "test-comp", name)
+			assert.Equal(t, af.Artifacts, got)
+		})
+	}
+}
+
+func TestGeneratePolicyManifests(t *testing.T) {
+	policyEngine := definition.NewWorkloadAbstractEngine("test-policy")
+	policyTemplate := &Template{
+		TemplateStr: `
+			output: {
+				apiVersion: "v1"
+				kind:       "ConfigMap"
+				metadata: {
+					name: context.name
+				}
+				data: {
+					key: parameter.value
+				}
+			}
+			parameter: {
+				value: string
+			}
+		`,
+	}
+
+	af := &Appfile{
+		Name:            "test-app",
+		Namespace:       "test-ns",
+		AppRevisionName: "test-app-v1",
+		ParsedPolicies: []*Component{
+			{
+				Name:   "test-policy",
+				Type:   "override",
+				Params: map[string]interface{}{"value": "test-value"},
+				engine: policyEngine,
+				FullTemplate: &Template{
+					TemplateStr: policyTemplate.TemplateStr,
+				},
+			},
+		},
+		AppLabels: map[string]string{
+			"label-key": "label-value",
+		},
+	}
+
+	manifests, err := af.GeneratePolicyManifests(context.Background())
+	assert.NoError(t, err)
+	assert.Equal(t, 1, len(manifests))
+
+	cm := manifests[0]
+	assert.Equal(t, "v1", cm.GetAPIVersion())
+	assert.Equal(t, "ConfigMap", cm.GetKind())
+	assert.Equal(t, "test-policy", cm.GetName())
+	assert.Equal(t, "test-ns", cm.GetNamespace())
+	assert.Equal(t, "test-app", cm.GetLabels()[oam.LabelAppName])
+
+	data, found, err := unstructured.NestedStringMap(cm.Object, "data")
+	assert.NoError(t, err)
+	assert.True(t, found)
+	assert.Equal(t, "test-value", data["key"])
+}
diff --git a/pkg/appfile/parser_test.go b/pkg/appfile/parser_test.go
index 36953f13..6d431b1f 100644
--- a/pkg/appfile/parser_test.go
+++ b/pkg/appfile/parser_test.go
@@ -24,8 +24,7 @@ import (
 	"testing"
 
 	"github.com/crossplane/crossplane-runtime/pkg/test"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
 	"github.com/stretchr/testify/assert"
 	errors2 "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -34,16 +33,217 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"sigs.k8s.io/yaml"
 
-	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
-
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
-	"github.com/oam-dev/kubevela/apis/types"
-
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 	common2 "github.com/oam-dev/kubevela/pkg/utils/common"
 )
 
+func TestParsePolicies(t *testing.T) {
+	overrideCompDef := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "webservice", Namespace: "vela-system"},
+		Spec:       v1beta1.ComponentDefinitionSpec{Workload: common.WorkloadTypeDescriptor{Type: "Deployment"}},
+	}
+	customPolicyDef := &v1beta1.PolicyDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "custom-policy", Namespace: "vela-system"},
+		Spec: v1beta1.PolicyDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+		},
+	}
+	schemes := runtime.NewScheme()
+	v1beta1.AddToScheme(schemes)
+
+	testcases := []struct {
+		name           string
+		appfile        *Appfile
+		client         client.Client
+		wantErrContain string
+		assertFunc     func(*testing.T, *Appfile)
+	}{
+		{
+			name: "policy with nil properties",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "gc-policy",
+								Type:       v1alpha1.GarbageCollectPolicyType,
+								Properties: nil,
+							},
+						},
+					},
+				},
+			},
+			client:         fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErrContain: "must not have empty properties",
+		},
+		{
+			name: "debug policy",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "debug-policy",
+								Type: v1alpha1.DebugPolicyType,
+							},
+						},
+					},
+				},
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.True(t, af.Debug)
+			},
+		},
+		{
+			name: "override policy fails to get definition",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Components: []common.ApplicationComponent{
+							{
+								Name: "comp1",
+								Type: "webservice",
+							},
+						},
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "override-policy",
+								Type: v1alpha1.OverridePolicyType,
+								Properties: util.Object2RawExtension(v1alpha1.OverridePolicySpec{
+									Components: []v1alpha1.EnvComponentPatch{
+										{
+											Name: "comp1",
+											Type: "webservice",
+										},
+									},
+								}),
+							},
+						},
+					},
+				},
+				RelatedComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition),
+				RelatedTraitDefinitions:     make(map[string]*v1beta1.TraitDefinition),
+			},
+			client: &test.MockClient{
+				MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+					return fmt.Errorf("get definition error")
+				},
+			},
+			wantErrContain: "get definition error",
+		},
+		{
+			name: "override policy success",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Components: []common.ApplicationComponent{
+							{
+								Name: "comp1",
+								Type: "webservice",
+							},
+						},
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "override-policy",
+								Type: v1alpha1.OverridePolicyType,
+								Properties: util.Object2RawExtension(v1alpha1.OverridePolicySpec{
+									Components: []v1alpha1.EnvComponentPatch{
+										{
+											Name: "comp1",
+											Type: "webservice",
+										},
+									},
+								}),
+							},
+						},
+					},
+				},
+				RelatedComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition),
+				RelatedTraitDefinitions:     make(map[string]*v1beta1.TraitDefinition),
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).WithObjects(overrideCompDef).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.Contains(t, af.RelatedComponentDefinitions, "webservice")
+			},
+		},
+		{
+			name: "custom policy definition not found",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "my-policy",
+								Type:       "custom-policy",
+								Properties: util.Object2RawExtension(map[string]string{"name": "test"}),
+							},
+						},
+					},
+				},
+			},
+			client: &test.MockClient{
+				MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+					if _, ok := obj.(*v1beta1.PolicyDefinition); ok {
+						return errors2.NewNotFound(v1beta1.Resource("policydefinition"), "custom-policy")
+					}
+					return nil
+				},
+			},
+			wantErrContain: "fetch component/policy type of my-policy",
+		},
+		{
+			name: "custom policy success",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "my-policy",
+								Type:       "custom-policy",
+								Properties: util.Object2RawExtension(map[string]string{"name": "test"}),
+							},
+						},
+					},
+				},
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).WithObjects(customPolicyDef).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.Equal(t, 1, len(af.ParsedPolicies))
+				assert.Equal(t, "my-policy", af.ParsedPolicies[0].Name)
+				assert.Equal(t, "custom-policy", af.ParsedPolicies[0].Type)
+			},
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			p := NewApplicationParser(tc.client)
+			// This function is tested separated, mock it for parsePolicies
+			if tc.appfile.app != nil {
+				tc.appfile.Policies = tc.appfile.app.Spec.Policies
+			}
+			err := p.parsePolicies(context.Background(), tc.appfile)
+
+			if tc.wantErrContain != "" {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.wantErrContain)
+			} else {
+				assert.NoError(t, err)
+				if tc.assertFunc != nil {
+					tc.assertFunc(t, tc.appfile)
+				}
+			}
+		})
+	}
+}
+
 var expectedExceptApp = &Appfile{
 	Name: "application-sample",
 	ParsedComponents: []*Component{
@@ -81,10 +281,10 @@ var expectedExceptApp = &Appfile{
       			}
       		}
       
-      		selector:
-      			matchLabels:
-      				"app.oam.dev/component": context.name
-      	}
+      			selector:
+      				matchLabels:
+      					"app.oam.dev/component": context.name
+      		}
       }
       
       parameter: {
@@ -149,7 +349,7 @@ spec:
       		selector:
       			matchLabels:
       				"app.oam.dev/component": context.name
-      	}
+      		}
       }
 
       parameter: {
@@ -235,11 +435,11 @@ spec:
       properties:
 `
 
-var _ = Describe("Test application parser", func() {
-	It("Test parse an application", func() {
+func TestApplicationParser(t *testing.T) {
+	t.Run("Test parse an application", func(t *testing.T) {
 		o := v1beta1.Application{}
 		err := yaml.Unmarshal([]byte(appfileYaml), &o)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 
 		// Create a mock client
 		tclient := test.MockClient{
@@ -266,24 +466,24 @@ var _ = Describe("Test application parser", func() {
 		}
 
 		appfile, err := NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &o)
-		Expect(err).ShouldNot(HaveOccurred())
-		Expect(equal(expectedExceptApp, appfile)).Should(BeTrue())
+		assert.NoError(t, err)
+		assert.True(t, equal(expectedExceptApp, appfile))
 
 		notfound := v1beta1.Application{}
 		err = yaml.Unmarshal([]byte(appfileYaml2), &notfound)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 		_, err = NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &notfound)
-		Expect(err).Should(HaveOccurred())
+		assert.Error(t, err)
 
-		By("app with empty policy")
+		t.Log("app with empty policy")
 		emptyPolicy := v1beta1.Application{}
 		err = yaml.Unmarshal([]byte(appfileYamlEmptyPolicy), &emptyPolicy)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 		_, err = NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &emptyPolicy)
-		Expect(err).Should(HaveOccurred())
-		Expect(err.Error()).Should(ContainSubstring("have empty properties"))
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "have empty properties")
 	})
-})
+}
 
 func equal(af, dest *Appfile) bool {
 	if af.Name != dest.Name || len(af.ParsedComponents) != len(dest.ParsedComponents) {
@@ -313,29 +513,28 @@ func equal(af, dest *Appfile) bool {
 	return true
 }
 
-var _ = Describe("Test application parser", func() {
+func TestApplicationParserWithLegacyRevision(t *testing.T) {
 	var app v1beta1.Application
 	var apprev v1beta1.ApplicationRevision
 	var wsd v1beta1.WorkflowStepDefinition
 	var expectedExceptAppfile *Appfile
 	var mockClient test.MockClient
 
-	BeforeEach(func() {
-		// prepare WorkflowStepDefinition
-		Expect(common2.ReadYamlToObject("testdata/backport-1-2/wsd.yaml", &wsd)).Should(BeNil())
-
-		// prepare verify data
-		expectedExceptAppfile = &Appfile{
-			Name: "backport-1-2-test-demo",
-			ParsedComponents: []*Component{
-				{
-					Name: "backport-1-2-test-demo",
-					Type: "webservice",
-					Params: map[string]interface{}{
-						"image": "nginx",
-					},
-					FullTemplate: &Template{
-						TemplateStr: `
+	// prepare WorkflowStepDefinition
+	assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/wsd.yaml", &wsd))
+
+	// prepare verify data
+	expectedExceptAppfile = &Appfile{
+		Name: "backport-1-2-test-demo",
+		ParsedComponents: []*Component{
+			{
+				Name: "backport-1-2-test-demo",
+				Type: "webservice",
+				Params: map[string]interface{}{
+					"image": "nginx",
+				},
+				FullTemplate: &Template{
+					TemplateStr: `
       output: {
         apiVersion: "apps/v1"
       	kind:       "Deployment"
@@ -361,10 +560,10 @@ var _ = Describe("Test application parser", func() {
       			}
       		}
       
-      		selector:
-      			matchLabels:
-      				"app.oam.dev/component": context.name
-      	}
+      			selector:
+      				matchLabels:
+      					"app.oam.dev/component": context.name
+      		}
       }
       
       parameter: {
@@ -374,14 +573,14 @@ var _ = Describe("Test application parser", func() {
       
       	cmd?: [...string]
       }`,
-					},
-					Traits: []*Trait{
-						{
-							Name: "scaler",
-							Params: map[string]interface{}{
-								"replicas": float64(1),
-							},
-							Template: `
+				},
+				Traits: []*Trait{
+					{
+						Name: "scaler",
+						Params: map[string]interface{}{
+							"replicas": float64(1),
+						},
+						Template: `
 parameter: {
 	// +usage=Specify the number of workload
 	replicas: *1 | int
@@ -390,62 +589,59 @@ parameter: {
 patch: spec: replicas: parameter.replicas
 
 `,
-						},
 					},
 				},
 			},
-			WorkflowSteps: []workflowv1alpha1.WorkflowStep{
-				{
-					WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
-						Name: "apply",
-						Type: "apply-application",
-					},
+		},
+		WorkflowSteps: []workflowv1alpha1.WorkflowStep{
+			{
+				WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
+					Name: "apply",
+					Type: "apply-application",
 				},
 			},
-		}
+		},
+	}
 
-		// Create mock client
-		mockClient = test.MockClient{
-			MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
-				if strings.Contains(key.Name, "unknown") {
-					return &errors2.StatusError{ErrStatus: metav1.Status{Reason: "NotFound", Message: "not found"}}
-				}
-				switch o := obj.(type) {
-				case *v1beta1.ComponentDefinition:
-					wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
-					if err != nil {
-						return err
-					}
-					*o = *wd
-				case *v1beta1.WorkflowStepDefinition:
-					*o = wsd
-				case *v1beta1.ApplicationRevision:
-					*o = apprev
-				default:
-					// skip
+	// Create mock client
+	mockClient = test.MockClient{
+		MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+			if strings.Contains(key.Name, "unknown") {
+				return &errors2.StatusError{ErrStatus: metav1.Status{Reason: "NotFound", Message: "not found"}}
+			}
+			switch o := obj.(type) {
+			case *v1beta1.ComponentDefinition:
+				wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
+				if err != nil {
+					return err
 				}
-				return nil
-			},
-		}
-	})
+				*o = *wd
+			case *v1beta1.WorkflowStepDefinition:
+				*o = wsd
+			case *v1beta1.ApplicationRevision:
+				*o = apprev
+			default:
+				// skip
+			}
+			return nil
+		},
+	}
 
-	When("with apply-application workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev1.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with apply-application workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev1.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 1", func() {
+		t.Run("Test we can parse an application revision to an appFile 1", func(t *testing.T) {
 
 			appfile, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).ShouldNot(HaveOccurred())
-			Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
-			Expect(len(appfile.WorkflowSteps) > 0 &&
-				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
+			assert.NoError(t, err)
+			assert.True(t, equal(expectedExceptAppfile, appfile))
+			assert.True(t, len(appfile.WorkflowSteps) > 0 &&
+				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions))
 
-			Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
+			assert.True(t, len(appfile.WorkflowSteps) > 0 && func() bool {
 				this := appfile.RelatedWorkflowStepDefinitions
 				that := appfile.AppRevision.Spec.WorkflowStepDefinitions
 				for i, w := range this {
@@ -455,27 +651,25 @@ patch: spec: replicas: parameter.replicas
 					}
 				}
 				return true
-			}()).Should(BeTrue())
+			}())
 		})
 	})
 
-	When("with apply-application and apply-component build-in workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev2.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with apply-application and apply-component build-in workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev2.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 2", func() {
+		t.Run("Test we can parse an application revision to an appFile 2", func(t *testing.T) {
 
 			appfile, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).ShouldNot(HaveOccurred())
-			Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
-			Expect(len(appfile.WorkflowSteps) > 0 &&
-				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
+			assert.NoError(t, err)
+			assert.True(t, equal(expectedExceptAppfile, appfile))
+			assert.True(t, len(appfile.WorkflowSteps) > 0 &&
+				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions))
 
-			Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
+			assert.True(t, len(appfile.WorkflowSteps) > 0 && func() bool {
 				this := appfile.RelatedWorkflowStepDefinitions
 				that := appfile.AppRevision.Spec.WorkflowStepDefinitions
 				for i, w := range this {
@@ -486,29 +680,25 @@ patch: spec: replicas: parameter.replicas
 					}
 				}
 				return true
-			}()).Should(BeTrue())
+			}())
 		})
 	})
 
-	When("with unknown workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev3.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with unknown workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev3.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 3", func() {
+		t.Run("Test we can parse an application revision to an appFile 3", func(t *testing.T) {
 
 			_, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).Should(HaveOccurred())
-			Expect(err.Error()).Should(SatisfyAll(
-				ContainSubstring("failed to get workflow step definition apply-application-unknown: not found"),
-				ContainSubstring("failed to parseWorkflowStepsForLegacyRevision")),
-			)
+			assert.Error(t, err)
+			assert.Contains(t, err.Error(), "failed to get workflow step definition apply-application-unknown: not found")
+			assert.Contains(t, err.Error(), "failed to parseWorkflowStepsForLegacyRevision")
 		})
 	})
-})
+}
 
 func TestParser_parseTraits(t *testing.T) {
 	type args struct {
@@ -707,3 +897,145 @@ func TestParser_parseTraitsFromRevision(t *testing.T) {
 		})
 	}
 }
+
+func TestParseComponentFromRevisionAndClient(t *testing.T) {
+	compDef := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "webservice", Namespace: "vela-system"},
+		Spec: v1beta1.ComponentDefinitionSpec{
+			Workload:  common.WorkloadTypeDescriptor{Type: "Deployment"},
+			Schematic: &common.Schematic{CUE: &common.CUE{Template: "parameter: {image: string}"}},
+		},
+	}
+	traitDef := &v1beta1.TraitDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "scaler", Namespace: "vela-system"},
+		Spec: v1beta1.TraitDefinitionSpec{
+			Schematic: &common.Schematic{CUE: &common.CUE{Template: "parameter: {replicas: int}"}},
+		},
+	}
+
+	appComp := common.ApplicationComponent{
+		Name:       "my-comp",
+		Type:       "webservice",
+		Properties: util.Object2RawExtension(map[string]string{"image": "nginx"}),
+		Traits: []common.ApplicationTrait{
+			{
+				Type:       "scaler",
+				Properties: util.Object2RawExtension(map[string]int{"replicas": 2}),
+			},
+		},
+	}
+
+	schemes := runtime.NewScheme()
+	v1beta1.AddToScheme(schemes)
+
+	tests := []struct {
+		name       string
+		appRev     *v1beta1.ApplicationRevision
+		client     client.Client
+		wantErr    bool
+		assertFunc func(*testing.T, *Component)
+	}{
+		{
+			name: "component and trait found in revision",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+						TraitDefinitions:     map[string]*v1beta1.TraitDefinition{"scaler": traitDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "my-comp", c.Name)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name: "component not in revision, but in cluster",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						TraitDefinitions: map[string]*v1beta1.TraitDefinition{"scaler": traitDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(compDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name: "trait not in revision, but in cluster",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(traitDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name:    "component and trait not in revision, but in cluster",
+			appRev:  &v1beta1.ApplicationRevision{},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(compDef, traitDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name:    "component not found anywhere",
+			appRev:  &v1beta1.ApplicationRevision{},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: true,
+		},
+		{
+			name: "trait not found anywhere",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := NewApplicationParser(tt.client)
+			comp, err := p.ParseComponentFromRevisionAndClient(context.Background(), appComp, tt.appRev)
+
+			if tt.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				if tt.assertFunc != nil {
+					tt.assertFunc(t, comp)
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/appfile/suit_test.go b/pkg/appfile/suit_test.go
deleted file mode 100644
index 871bc309..00000000
--- a/pkg/appfile/suit_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-Copyright 2021 The KubeVela Authors.
-
-Licensed 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 appfile
-
-import (
-	"path/filepath"
-	"testing"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/apps/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
-
-	coreoam "github.com/oam-dev/kubevela/apis/core.oam.dev"
-	// +kubebuilder:scaffold:imports
-)
-
-var cfg *rest.Config
-var scheme *runtime.Scheme
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAppFile(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "Cli Suite")
-}
-
-var _ = BeforeSuite(func() {
-	logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
-	By("bootstrapping test environment")
-	useExistCluster := false
-	testEnv = &envtest.Environment{
-		ControlPlaneStartTimeout: time.Minute,
-		ControlPlaneStopTimeout:  time.Minute,
-		CRDDirectoryPaths:        []string{filepath.Join("..", "..", "charts", "vela-core", "crds")},
-		UseExistingCluster:       &useExistCluster,
-	}
-
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ToNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-	scheme = runtime.NewScheme()
-	Expect(coreoam.AddToScheme(scheme)).NotTo(HaveOccurred())
-	Expect(clientgoscheme.AddToScheme(scheme)).NotTo(HaveOccurred())
-	Expect(v1.AddToScheme(scheme)).NotTo(HaveOccurred())
-	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
-	Expect(err).ToNot(HaveOccurred())
-	Expect(k8sClient).ToNot(BeNil())
-})
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
diff --git a/pkg/appfile/template_test.go b/pkg/appfile/template_test.go
index c6485664..a87026ab 100644
--- a/pkg/appfile/template_test.go
+++ b/pkg/appfile/template_test.go
@@ -18,14 +18,19 @@ package appfile
 
 import (
 	"context"
+	"errors"
+	"fmt"
 	"testing"
 
 	"cuelang.org/go/cue/cuecontext"
 	"github.com/crossplane/crossplane-runtime/pkg/test"
 	"github.com/google/go-cmp/cmp"
 	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	ktypes "k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -35,6 +40,24 @@ import (
 	oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
 )
 
+type fakeRESTMapper struct {
+	meta.RESTMapper
+}
+
+func (f fakeRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+	if resource.Resource == "deployments" {
+		return schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, nil
+	}
+	return schema.GroupVersionKind{}, errors.New("no mapping for KindFor")
+}
+
+func (f fakeRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+	if resource.Resource == "deployments" {
+		return []schema.GroupVersionKind{{Group: "apps", Version: "v1", Kind: "Deployment"}}, nil
+	}
+	return nil, errors.New("no mapping for KindsFor")
+}
+
 func TestLoadComponentTemplate(t *testing.T) {
 	cueTemplate := `
       context: {
@@ -47,35 +70,35 @@ func TestLoadComponentTemplate(t *testing.T) {
       		selector: matchLabels: {
       			"app.oam.dev/component": context.name
       		}
-      
+
       		template: {
       			metadata: labels: {
       				"app.oam.dev/component": context.name
       			}
-      
+
       			spec: {
       				containers: [{
       					name:  context.name
       					image: parameter.image
-      
+
       					if parameter["cmd"] != _|_ {
       						command: parameter.cmd
       					}
       				}]
       			}
       		}
-      
+
       		selector:
       			matchLabels:
       				"app.oam.dev/component": context.name
       	}
       }
-      
+
       parameter: {
       	// +usage=Which image would you like to use for your service
       	// +short=i
       	image: string
-      
+
       	cmd?: [...string]
       }
       `
@@ -316,7 +339,7 @@ metadata:
 spec:
   status:
     customStatus: testCustomStatus
-    healthPolicy: testHealthPolicy 
+    healthPolicy: testHealthPolicy
   workload:
     definition:
       apiVersion: apps/v1
@@ -333,7 +356,7 @@ metadata:
 spec:
   status:
     customStatus: testCustomStatus
-    healthPolicy: testHealthPolicy 
+    healthPolicy: testHealthPolicy
   appliesToWorkloads:
     - deployments.apps
   schematic:
@@ -385,3 +408,349 @@ spec:
 		t.Fatal("failed load template of trait definition ", diff)
 	}
 }
+
+func TestLoadTemplateFromRevision(t *testing.T) {
+	compDef := v1beta1.ComponentDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.ComponentDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-comp"},
+		Spec: v1beta1.ComponentDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			Workload: common.WorkloadTypeDescriptor{
+				Definition: common.WorkloadGVK{APIVersion: "v1", Kind: "Pod"},
+			},
+		},
+	}
+	traitDef := v1beta1.TraitDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.TraitDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-trait"},
+		Spec: v1beta1.TraitDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {port: int}"},
+			},
+		},
+	}
+	policyDef := v1beta1.PolicyDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.PolicyDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-policy"},
+		Spec: v1beta1.PolicyDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {replicas: int}"},
+			},
+		},
+	}
+	wfStepDef := v1beta1.WorkflowStepDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.WorkflowStepDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-step"},
+		Spec: v1beta1.WorkflowStepDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {image: string}"},
+			},
+		},
+	}
+	wlDef := v1beta1.WorkloadDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.WorkloadDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-workload"},
+		Spec: v1beta1.WorkloadDefinitionSpec{
+			Reference: common.DefinitionReference{
+				Name: "deployments.apps",
+			},
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "output: {apiVersion: 'apps/v1', kind: 'Deployment'}"},
+			},
+		},
+	}
+
+	appRev := &v1beta1.ApplicationRevision{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-app-rev"},
+		Spec: v1beta1.ApplicationRevisionSpec{
+			ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+				ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{
+					"my-comp": &compDef,
+				},
+				TraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": &traitDef,
+				},
+				PolicyDefinitions: map[string]v1beta1.PolicyDefinition{
+					"my-policy": policyDef,
+				},
+				WorkflowStepDefinitions: map[string]*v1beta1.WorkflowStepDefinition{
+					"my-step": &wfStepDef,
+				},
+				WorkloadDefinitions: map[string]v1beta1.WorkloadDefinition{
+					"my-workload": wlDef,
+				},
+			},
+		},
+	}
+
+	mapper := fakeRESTMapper{}
+
+	testCases := map[string]struct {
+		capName   string
+		capType   types.CapType
+		apprev    *v1beta1.ApplicationRevision
+		checkFunc func(t *testing.T, tmpl *Template, err error)
+	}{
+		"load component definition": {
+			capName: "my-comp",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {name: string}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.ComponentDefinitionKind, tmpl.ComponentDefinition.Kind)
+			},
+		},
+		"load trait definition": {
+			capName: "my-trait",
+			capType: types.TypeTrait,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {port: int}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.TraitDefinitionKind, tmpl.TraitDefinition.Kind)
+			},
+		},
+		"load policy definition": {
+			capName: "my-policy",
+			capType: types.TypePolicy,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {replicas: int}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.PolicyDefinitionKind, tmpl.PolicyDefinition.Kind)
+			},
+		},
+		"load workflow step definition": {
+			capName: "my-step",
+			capType: types.TypeWorkflowStep,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {image: string}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.WorkflowStepDefinitionKind, tmpl.WorkflowStepDefinition.Kind)
+			},
+		},
+		"fallback to workload definition": {
+			capName: "my-workload",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "output: {apiVersion: 'apps/v1', kind: 'Deployment'}", tmpl.TemplateStr)
+				assert.NotNil(t, tmpl.WorkloadDefinition)
+				assert.Equal(t, v1beta1.WorkloadDefinitionKind, tmpl.WorkloadDefinition.Kind)
+				assert.Equal(t, "apps/v1", tmpl.Reference.Definition.APIVersion)
+				assert.Equal(t, "Deployment", tmpl.Reference.Definition.Kind)
+			},
+		},
+		"definition not found": {
+			capName: "not-exist",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.True(t, IsNotFoundInAppRevision(err))
+				assert.Contains(t, err.Error(), "component definition [not-exist] not found in app revision my-app-rev")
+			},
+		},
+		"nil app revision": {
+			capName: "any",
+			capType: types.TypeComponentDefinition,
+			apprev:  nil,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.Contains(t, err.Error(), "fail to find template for any as app revision is empty")
+			},
+		},
+		"unsupported type": {
+			capName: "any",
+			capType: "unsupported",
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.Contains(t, err.Error(), "kind(unsupported) of any not supported")
+			},
+		},
+		"verify revision name": {
+			capName: "my-comp@my-ns",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {name: string}", tmpl.TemplateStr)
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			tmpl, err := LoadTemplateFromRevision(tc.capName, tc.capType, tc.apprev, mapper)
+			tc.checkFunc(t, tmpl, err)
+		})
+	}
+}
+
+func TestConvertTemplateJSON2Object(t *testing.T) {
+	testCases := map[string]struct {
+		capName   string
+		in        *runtime.RawExtension
+		schematic *common.Schematic
+		wantCap   types.Capability
+		wantErr   bool
+	}{
+		"with schematic CUE": {
+			capName: "test-cap",
+			schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap",
+				CueTemplate: "parameter: {name: string}",
+			},
+			wantErr: false,
+		},
+		"with RawExtension": {
+			capName: "test-cap-2",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"template": "parameter: {age: int}"}`),
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap-2",
+				CueTemplate: "parameter: {age: int}",
+			},
+			wantErr: false,
+		},
+		"with both schematic and RawExtension": {
+			capName: "test-cap-3",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"description": "test"}`),
+			},
+			schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap-3",
+				Description: "test",
+				CueTemplate: "parameter: {name: string}",
+			},
+			wantErr: false,
+		},
+		"with invalid JSON in RawExtension": {
+			capName: "test-cap-4",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"template": "parameter: {age: int}"`),
+			},
+			wantErr: true,
+		},
+		"with no template": {
+			capName: "test-cap-5",
+			in:      &runtime.RawExtension{Raw: []byte(`{"description": "test"}`)},
+			wantCap: types.Capability{
+				Name:        "test-cap-5",
+				Description: "test",
+			},
+			wantErr: false,
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			cap, err := ConvertTemplateJSON2Object(tc.capName, tc.in, tc.schematic)
+			if tc.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				if diff := cmp.Diff(tc.wantCap, cap); diff != "" {
+					t.Errorf("ConvertTemplateJSON2Object() (-want, +got):\n%s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestTemplateAsStatusRequest(t *testing.T) {
+	tmpl := &Template{
+		Health:       "isHealth: true",
+		CustomStatus: "message: 'Ready'",
+		Details:      "details: 'some details'",
+	}
+	params := map[string]interface{}{
+		"param1": "value1",
+	}
+	statusReq := tmpl.AsStatusRequest(params)
+
+	assert.Equal(t, "isHealth: true", statusReq.Health)
+	assert.Equal(t, "message: 'Ready'", statusReq.Custom)
+	assert.Equal(t, "details: 'some details'", statusReq.Details)
+	assert.Equal(t, params, statusReq.Parameter)
+}
+
+func TestIsNotFoundInAppRevision(t *testing.T) {
+	testCases := map[string]struct {
+		err      error
+		expected bool
+	}{
+		"component definition not found": {
+			err:      fmt.Errorf("component definition [my-comp] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"trait definition not found": {
+			err:      fmt.Errorf("trait definition [my-trait] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"policy definition not found": {
+			err:      fmt.Errorf("policy definition [my-policy] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"workflow step definition not found": {
+			err:      fmt.Errorf("workflow step definition [my-step] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"different error": {
+			err:      errors.New("a completely different error"),
+			expected: false,
+		},
+		"nil error": {
+			err:      nil,
+			expected: false,
+		},
+		"error with similar text but not exactly": {
+			err:      fmt.Errorf("this resource is not found in revision of app"),
+			expected: false,
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			result := IsNotFoundInAppRevision(tc.err)
+			assert.Equal(t, tc.expected, result)
+		})
+	}
+}
diff --git a/pkg/appfile/unit_test.go b/pkg/appfile/unit_test.go
deleted file mode 100644
index 297559c0..00000000
--- a/pkg/appfile/unit_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-Copyright 2023 The KubeVela Authors.
-
-Licensed 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 appfile
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-)
-
-func TestIsNotFoundInAppFile(t *testing.T) {
-	require.True(t, IsNotFoundInAppFile(fmt.Errorf("ComponentDefinition XXX not found in appfile")))
-}
-
-func TestIsNotFoundInAppRevision(t *testing.T) {
-	require.True(t, IsNotFoundInAppRevision(fmt.Errorf("ComponentDefinition XXX not found in app revision")))
-}
-
-func TestParseComponentFromRevisionAndClient(t *testing.T) {
-	ctx := context.Background()
-	cli := fake.NewClientBuilder().WithScheme(scheme).Build()
-	p := &Parser{
-		client:     cli,
-		tmplLoader: LoadTemplate,
-	}
-	comp := common.ApplicationComponent{
-		Name:       "test",
-		Type:       "test",
-		Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		Traits: []common.ApplicationTrait{{
-			Type:       "tr",
-			Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		}, {
-			Type:       "internal",
-			Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		}},
-	}
-	appRev := &v1beta1.ApplicationRevision{}
-	cd := &v1beta1.ComponentDefinition{ObjectMeta: metav1.ObjectMeta{Name: "test"}}
-	td := &v1beta1.TraitDefinition{ObjectMeta: metav1.ObjectMeta{Name: "tr"}}
-	require.NoError(t, cli.Create(ctx, cd))
-	require.NoError(t, cli.Create(ctx, td))
-	appRev.Spec.TraitDefinitions = map[string]*v1beta1.TraitDefinition{"internal": {}}
-	_, err := p.ParseComponentFromRevisionAndClient(ctx, comp, appRev)
-	require.NoError(t, err)
-
-	_comp1 := comp.DeepCopy()
-	_comp1.Type = "bad"
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp1, appRev)
-	require.Error(t, err)
-
-	_comp2 := comp.DeepCopy()
-	_comp2.Traits[0].Type = "bad"
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp2, appRev)
-	require.Error(t, err)
-
-	_comp3 := comp.DeepCopy()
-	_comp3.Traits[0].Properties = &runtime.RawExtension{Raw: []byte(`bad`)}
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp3, appRev)
-	require.Error(t, err)
-}
diff --git a/pkg/appfile/validate_test.go b/pkg/appfile/validate_test.go
index 4fe61bc7..7720a799 100644
--- a/pkg/appfile/validate_test.go
+++ b/pkg/appfile/validate_test.go
@@ -17,67 +17,32 @@ limitations under the License.
 package appfile
 
 import (
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+	"testing"
+
+	"cuelang.org/go/cue"
+	"github.com/stretchr/testify/assert"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
 
 	"github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/cue/definition"
+	"github.com/oam-dev/kubevela/pkg/features"
 )
 
-var _ = Describe("Test validate CUE schematic Appfile", func() {
+func TestTrait_EvalContext_OutputNameUniqueness(t *testing.T) {
 	type SubTestCase struct {
+		name          string
 		compDefTmpl   string
 		traitDefTmpl1 string
 		traitDefTmpl2 string
 		wantErrMsg    string
 	}
 
-	DescribeTable("Test validate outputs name unique", func(tc SubTestCase) {
-		Expect("").Should(BeEmpty())
-		wl := &Component{
-			Name:               "myweb",
-			Type:               "worker",
-			CapabilityCategory: types.CUECategory,
-			Traits: []*Trait{
-				{
-					Name:               "myscaler",
-					CapabilityCategory: types.CUECategory,
-					Template:           tc.traitDefTmpl1,
-					engine:             definition.NewTraitAbstractEngine("myscaler"),
-				},
-				{
-					Name:               "myingress",
-					CapabilityCategory: types.CUECategory,
-					Template:           tc.traitDefTmpl2,
-					engine:             definition.NewTraitAbstractEngine("myingress"),
-				},
-			},
-			FullTemplate: &Template{
-				TemplateStr: tc.compDefTmpl,
-			},
-			engine: definition.NewWorkloadAbstractEngine("myweb"),
-		}
-
-		ctxData := GenerateContextDataFromAppFile(&Appfile{
-			Name:            "myapp",
-			Namespace:       "test-ns",
-			AppRevisionName: "myapp-v1",
-		}, wl.Name)
-		pCtx, err := newValidationProcessContext(wl, ctxData)
-		Expect(err).Should(BeNil())
-		Eventually(func() string {
-			for _, tr := range wl.Traits {
-				if err := tr.EvalContext(pCtx); err != nil {
-					return err.Error()
-				}
-			}
-			return ""
-		}).Should(ContainSubstring(tc.wantErrMsg))
-	},
-		Entry("Succeed", SubTestCase{
+	testCases := []SubTestCase{
+		{
+			name: "Succeed",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc: {
@@ -98,11 +63,12 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: "",
-		}),
-		Entry("CompDef and TraitDef have same outputs", SubTestCase{
+		},
+		{
+			name: "CompDef and TraitDef have same outputs",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc1: {
@@ -123,11 +89,12 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: `auxiliary "mysvc1" already exits`,
-		}),
-		Entry("TraitDefs have same outputs", SubTestCase{
+		},
+		{
+			name: "TraitDefs have same outputs",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc: {
@@ -148,41 +115,72 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: `auxiliary "mysvc1" already exits`,
-		}),
-	)
-})
+		},
+	}
 
-var _ = Describe("Test ValidateComponentParams", func() {
-	type ParamTestCase struct {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			wl := &Component{
+				Name:               "myweb",
+				Type:               "worker",
+				CapabilityCategory: types.CUECategory,
+				Traits: []*Trait{
+					{
+						Name:               "myscaler",
+						CapabilityCategory: types.CUECategory,
+						Template:           tc.traitDefTmpl1,
+						engine:             definition.NewTraitAbstractEngine("myscaler"),
+					},
+					{
+						Name:               "myingress",
+						CapabilityCategory: types.CUECategory,
+						Template:           tc.traitDefTmpl2,
+						engine:             definition.NewTraitAbstractEngine("myingress"),
+					},
+				},
+				FullTemplate: &Template{
+					TemplateStr: tc.compDefTmpl,
+				},
+				engine: definition.NewWorkloadAbstractEngine("myweb"),
+			}
+
+			ctxData := GenerateContextDataFromAppFile(&Appfile{
+				Name:            "myapp",
+				Namespace:       "test-ns",
+				AppRevisionName: "myapp-v1",
+			}, wl.Name)
+			pCtx, err := newValidationProcessContext(wl, ctxData)
+			assert.NoError(t, err)
+
+			var evalErr error
+			for _, tr := range wl.Traits {
+				if err := tr.EvalContext(pCtx); err != nil {
+					evalErr = err
+					break
+				}
+			}
+
+			if tc.wantErrMsg != "" {
+				assert.Error(t, evalErr)
+				assert.Contains(t, evalErr.Error(), tc.wantErrMsg)
+			} else {
+				assert.NoError(t, evalErr)
+			}
+		})
+	}
+}
+
+func TestParser_ValidateComponentParams(t *testing.T) {
+	testCases := []struct {
 		name     string
+		compName string
 		template string
 		params   map[string]interface{}
 		wantErr  string
-	}
-
-	DescribeTable("ValidateComponentParams cases", func(tc ParamTestCase) {
-		wl := &Component{
-			Name:         tc.name,
-			Type:         "worker",
-			FullTemplate: &Template{TemplateStr: tc.template},
-			Params:       tc.params,
-		}
-		app := &Appfile{
-			Name:      "myapp",
-			Namespace: "test-ns",
-		}
-		ctxData := GenerateContextDataFromAppFile(app, wl.Name)
-		parser := &Parser{}
-		err := parser.ValidateComponentParams(ctxData, wl, app)
-		if tc.wantErr == "" {
-			Expect(err).To(BeNil())
-		} else {
-			Expect(err).To(HaveOccurred())
-			Expect(err.Error()).To(ContainSubstring(tc.wantErr))
-		}
-	},
-		Entry("valid params and template", ParamTestCase{
-			name: "valid",
+	}{
+		{
+			name:     "valid params and template",
+			compName: "valid",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -196,9 +194,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": 2,
 			},
 			wantErr: "",
-		}),
-		Entry("invalid CUE in template", ParamTestCase{
-			name: "invalid-cue",
+		},
+		{
+			name:     "invalid CUE in template",
+			compName: "invalid-cue",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -213,9 +212,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": 2,
 			},
 			wantErr: "CUE compile error",
-		}),
-		Entry("missing required parameter", ParamTestCase{
-			name: "missing-required",
+		},
+		{
+			name:     "missing required parameter",
+			compName: "missing-required",
 			template: `
 			parameter: {
 				replicas: int
@@ -227,9 +227,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 			`,
 			params:  map[string]interface{}{},
 			wantErr: "component \"missing-required\": missing parameters: replicas",
-		}),
-		Entry("parameter constraint violation", ParamTestCase{
-			name: "constraint-violation",
+		},
+		{
+			name:     "parameter constraint violation",
+			compName: "constraint-violation",
 			template: `
 			parameter: {
 				replicas: int & >0
@@ -243,9 +244,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": -1,
 			},
 			wantErr: "parameter constraint violation",
-		}),
-		Entry("invalid parameter block", ParamTestCase{
-			name: "invalid-param-block",
+		},
+		{
+			name:     "invalid parameter block",
+			compName: "invalid-param-block",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -259,6 +261,340 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": "not-an-int",
 			},
 			wantErr: "parameter constraint violation",
-		}),
-	)
-})
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			wl := &Component{
+				Name:         tc.compName,
+				Type:         "worker",
+				FullTemplate: &Template{TemplateStr: tc.template},
+				Params:       tc.params,
+			}
+			app := &Appfile{
+				Name:      "myapp",
+				Namespace: "test-ns",
+			}
+			ctxData := GenerateContextDataFromAppFile(app, wl.Name)
+			parser := &Parser{}
+			err := parser.ValidateComponentParams(ctxData, wl, app)
+			if tc.wantErr == "" {
+				assert.NoError(t, err)
+			} else {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.wantErr)
+			}
+		})
+	}
+}
+
+func TestValidationHelpers(t *testing.T) {
+	t.Run("renderTemplate", func(t *testing.T) {
+		tmpl := "output: {}"
+		expected := "output: {}\ncontext: _\nparameter: _\n"
+		assert.Equal(t, expected, renderTemplate(tmpl))
+	})
+
+	t.Run("cueParamBlock", func(t *testing.T) {
+		t.Run("should handle empty params", func(t *testing.T) {
+			out, err := cueParamBlock(map[string]any{})
+			assert.NoError(t, err)
+			assert.Equal(t, "parameter: {}", out)
+		})
+
+		t.Run("should handle valid params", func(t *testing.T) {
+			params := map[string]any{"key": "value"}
+			out, err := cueParamBlock(params)
+			assert.NoError(t, err)
+			assert.Equal(t, `parameter: {"key":"value"}`, out)
+		})
+
+		t.Run("should return error for unmarshallable params", func(t *testing.T) {
+			params := map[string]any{"key": make(chan int)}
+			_, err := cueParamBlock(params)
+			assert.Error(t, err)
+		})
+	})
+
+	t.Run("filterMissing", func(t *testing.T) {
+		t.Run("should filter missing keys", func(t *testing.T) {
+			keys := []string{"a", "b.c", "d"}
+			provided := map[string]any{
+				"a": 1,
+				"b": map[string]any{
+					"c": 2,
+				},
+			}
+			out, err := filterMissing(keys, provided)
+			assert.NoError(t, err)
+			assert.Equal(t, []string{"d"}, out)
+		})
+
+		t.Run("should handle no missing keys", func(t *testing.T) {
+			keys := []string{"a"}
+			provided := map[string]any{"a": 1}
+			out, err := filterMissing(keys, provided)
+			assert.NoError(t, err)
+			assert.Empty(t, out)
+		})
+	})
+
+	t.Run("requiredFields", func(t *testing.T) {
+		t.Run("should identify required fields", func(t *testing.T) {
+			cueStr := `
+			parameter: {
+				name: string
+				age: int
+				nested: {
+					field1: string
+					field2: bool
+				}
+			}
+			`
+			var r cue.Runtime
+			inst, err := r.Compile("", cueStr)
+			assert.NoError(t, err)
+			val := inst.Value()
+			paramVal := val.LookupPath(cue.ParsePath("parameter"))
+
+			fields, err := requiredFields(paramVal)
+			assert.NoError(t, err)
+			assert.ElementsMatch(t, []string{"name", "age", "nested.field1", "nested.field2"}, fields)
+		})
+
+		t.Run("should ignore optional and default fields", func(t *testing.T) {
+			cueStr := `
+			parameter: {
+				name: string
+				age?: int
+				location: string | *"unknown"
+				nested: {
+					field1: string
+					field2?: bool
+				}
+			}
+			`
+			var r cue.Runtime
+			inst, err := r.Compile("", cueStr)
+			assert.NoError(t, err)
+			val := inst.Value()
+			paramVal := val.LookupPath(cue.ParsePath("parameter"))
+
+			fields, err := requiredFields(paramVal)
+			assert.NoError(t, err)
+			assert.ElementsMatch(t, []string{"name", "nested.field1"}, fields)
+		})
+	})
+}
+
+func TestEnforceRequiredParams(t *testing.T) {
+	var r cue.Runtime
+	cueStr := `
+		parameter: {
+			image: string
+			replicas: int
+			port: int
+			data: {
+				key: string
+				value: string
+			}
+		}
+		`
+	inst, err := r.Compile("", cueStr)
+	assert.NoError(t, err)
+	root := inst.Value()
+
+	t.Run("should pass if all params are provided directly", func(t *testing.T) {
+		params := map[string]any{
+			"image":    "nginx",
+			"replicas": 2,
+			"port":     80,
+			"data": map[string]any{
+				"key":   "k",
+				"value": "v",
+			},
+		}
+		app := &Appfile{}
+		err := enforceRequiredParams(root, params, app)
+		assert.NoError(t, err)
+	})
+
+	t.Run("should fail if params are missing", func(t *testing.T) {
+		params := map[string]any{
+			"image": "nginx",
+		}
+		app := &Appfile{}
+		err := enforceRequiredParams(root, params, app)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "missing parameters: replicas,port,data.key,data.value")
+	})
+}
+
+func TestParser_ValidateCUESchematicAppfile(t *testing.T) {
+	assert.NoError(t, utilfeature.DefaultMutableFeatureGate.Set(string(features.EnableCueValidation)+"=true"))
+	t.Cleanup(func() {
+		assert.NoError(t, utilfeature.DefaultMutableFeatureGate.Set(string(features.EnableCueValidation)+"=false"))
+	})
+
+	t.Run("should validate a valid CUE schematic appfile", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params: map[string]any{
+						"image": "nginx",
+					},
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+								spec: {
+									template: {
+										spec: {
+											containers: [{
+												name: "my-container"
+												image: parameter.image
+											}]
+										}
+									}
+								}
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+					Traits: []*Trait{
+						{
+							Name:               "my-trait",
+							CapabilityCategory: types.CUECategory,
+							Template: `
+								parameter: {
+								domain: string
+							}
+							patch: {}
+						`,
+							Params: map[string]any{
+								"domain": "example.com",
+							},
+							engine: definition.NewTraitAbstractEngine("my-trait"),
+						},
+					},
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.NoError(t, err)
+	})
+
+	t.Run("should return error for invalid trait evaluation", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params: map[string]any{
+						"image": "nginx",
+					},
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+					Traits: []*Trait{
+						{
+							Name:               "my-trait",
+							CapabilityCategory: types.CUECategory,
+							Template: `
+								// invalid CUE template
+								parameter: {
+									domain: string
+								}
+							patch: {
+									invalid: {
+							}
+						`,
+							Params: map[string]any{
+								"domain": "example.com",
+							},
+							engine: definition.NewTraitAbstractEngine("my-trait"),
+						},
+					},
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "cannot evaluate trait \"my-trait\"")
+	})
+
+	t.Run("should return error for missing parameters", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params:             map[string]any{}, // no params provided
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "missing parameters: image")
+	})
+
+	t.Run("should skip non-CUE components", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "helm",
+					CapabilityCategory: types.TerraformCategory,
+				},
+			},
+		}
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.NoError(t, err)
+	})
+}
diff --git a/pkg/component/main_test.go b/pkg/component/main_test.go
new file mode 100644
index 00000000..423a9769
--- /dev/null
+++ b/pkg/component/main_test.go
@@ -0,0 +1,66 @@
+/*
+Copyright 2021 The KubeVela Authors.
+
+Licensed 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 component
+
+import (
+	"log"
+	"math/rand"
+	"os"
+	"testing"
+	"time"
+
+	"k8s.io/client-go/rest"
+	"k8s.io/utils/ptr"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+
+	pkgcommon "github.com/oam-dev/kubevela/pkg/utils/common"
+)
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+
+func TestMain(m *testing.M) {
+	rand.Seed(time.Now().UnixNano())
+
+	testEnv = &envtest.Environment{
+		ControlPlaneStartTimeout: time.Minute * 3,
+		ControlPlaneStopTimeout:  time.Minute,
+		UseExistingCluster:       ptr.To(false),
+		CRDDirectoryPaths:        []string{"./testdata"},
+	}
+
+	var err error
+	cfg, err = testEnv.Start()
+	if err != nil {
+		log.Fatalf("Failed to start test environment: %v", err)
+	}
+
+	cfg.Timeout = time.Minute * 2
+	k8sClient, err = client.New(cfg, client.Options{Scheme: pkgcommon.Scheme})
+	if err != nil {
+		log.Fatalf("Failed to create new kube client: %v", err)
+	}
+
+	code := m.Run()
+
+	if err = testEnv.Stop(); err != nil {
+		log.Printf("Failed to tear down the test environment: %v", err)
+	}
+	os.Exit(code)
+}
diff --git a/pkg/component/ref_objects_suite_test.go b/pkg/component/ref_objects_suite_test.go
deleted file mode 100644
index 21d1a6a7..00000000
--- a/pkg/component/ref_objects_suite_test.go
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
-Copyright 2021 The KubeVela Authors.
-
-Licensed 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 component
-
-import (
-	"context"
-	"math/rand"
-	"testing"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	rbacv1 "k8s.io/api/rbac/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	utilfeature "k8s.io/apiserver/pkg/util/feature"
-	"k8s.io/client-go/rest"
-	featuregatetesting "k8s.io/component-base/featuregate/testing"
-	"k8s.io/utils/ptr"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
-	"github.com/oam-dev/kubevela/pkg/features"
-	pkgcommon "github.com/oam-dev/kubevela/pkg/utils/common"
-)
-
-var cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestUtils(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "Utils Suite")
-}
-
-var _ = BeforeSuite(func() {
-	rand.Seed(time.Now().UnixNano())
-	By("bootstrapping test environment for utils test")
-
-	testEnv = &envtest.Environment{
-		ControlPlaneStartTimeout: time.Minute * 3,
-		ControlPlaneStopTimeout:  time.Minute,
-		UseExistingCluster:       ptr.To(false),
-		CRDDirectoryPaths:        []string{"./testdata"},
-	}
-
-	By("start kube test env")
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ShouldNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-
-	By("new kube client")
-	cfg.Timeout = time.Minute * 2
-	k8sClient, err = client.New(cfg, client.Options{Scheme: pkgcommon.Scheme})
-	Expect(err).Should(Succeed())
-})
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
-
-var _ = Describe("Test ref-objects functions", func() {
-	It("Test SelectRefObjectsForDispatch", func() {
-		featuregatetesting.SetFeatureGateDuringTest(GinkgoT(), utilfeature.DefaultFeatureGate, features.LegacyObjectTypeIdentifier, true)
-		featuregatetesting.SetFeatureGateDuringTest(GinkgoT(), utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, true)
-		By("Create objects")
-		Expect(k8sClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}})).Should(Succeed())
-		for _, obj := range []client.Object{&corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "dynamic",
-				Namespace: "test",
-			},
-		}, &corev1.Service{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:       "dynamic",
-				Namespace:  "test",
-				Generation: int64(5),
-			},
-			Spec: corev1.ServiceSpec{
-				ClusterIP: "10.0.0.254",
-				Ports:     []corev1.ServicePort{{Port: 80}},
-			},
-		}, &corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "by-label-1",
-				Namespace: "test",
-				Labels:    map[string]string{"key": "value"},
-			},
-		}, &corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "by-label-2",
-				Namespace: "test",
-				Labels:    map[string]string{"key": "value"},
-			},
-		}, &rbacv1.ClusterRole{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "test-cluster-role",
-			},
-		}} {
-			Expect(k8sClient.Create(context.Background(), obj)).Should(Succeed())
-		}
-		createUnstructured := func(apiVersion string, kind string, name string, namespace string, labels map[string]interface{}) *unstructured.Unstructured {
-			un := &unstructured.Unstructured{
-				Object: map[string]interface{}{
-					"apiVersion": apiVersion,
-					"kind":       kind,
-					"metadata":   map[string]interface{}{"name": name},
-				},
-			}
-			if namespace != "" {
-				un.SetNamespace(namespace)
-			}
-			if labels != nil {
-				un.Object["metadata"].(map[string]interface{})["labels"] = labels
-			}
-			return un
-		}
-		testcases := map[string]struct {
-			Input         v1alpha1.ObjectReferrer
-			compName      string
-			appNs         string
-			Output        []*unstructured.Unstructured
-			Error         string
-			Scope         string
-			IsService     bool
-			IsClusterRole bool
-		}{
-			"normal": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"legacy-type-identifier": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "v1"}},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"invalid-apiVersion": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "a/b/v1"}},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs: "test",
-				Error: "invalid APIVersion",
-			},
-			"invalid-type-identifier": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs: "test",
-				Error: "neither resource or apiVersion/kind is set",
-			},
-			"name-and-selector-both-set": {
-				Input: v1alpha1.ObjectReferrer{ObjectSelector: v1alpha1.ObjectSelector{Name: "dynamic", LabelSelector: map[string]string{"key": "value"}}},
-				appNs: "test",
-				Error: "invalid object selector for ref-objects, name and labelSelector cannot be both set",
-			},
-			"empty-ref-object-name": {
-				Input:    v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"}},
-				compName: "dynamic",
-				appNs:    "test",
-				Output:   []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cannot-find-ref-object": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "static"},
-				},
-				appNs: "test",
-				Error: "failed to load ref object",
-			},
-			"modify-service": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "service"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:     "test",
-				IsService: true,
-			},
-			"by-labels": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{LabelSelector: map[string]string{"key": "value"}},
-				},
-				appNs: "test",
-				Output: []*unstructured.Unstructured{
-					createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
-					createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
-				},
-			},
-			"by-deprecated-labels": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{DeprecatedLabelSelector: map[string]string{"key": "value"}},
-				},
-				appNs: "test",
-				Output: []*unstructured.Unstructured{
-					createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
-					createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
-				},
-			},
-			"no-kind-for-resource": {
-				Input: v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "unknown"}},
-				appNs: "test",
-				Error: "no matches",
-			},
-			"cross-namespace": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
-				},
-				appNs:  "demo",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cross-namespace-forbidden": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
-				},
-				appNs: "demo",
-				Scope: RefObjectsAvailableScopeNamespace,
-				Error: "cannot refer to objects outside the application's namespace",
-			},
-			"cross-cluster": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cross-cluster-forbidden": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
-				},
-				appNs: "test",
-				Scope: RefObjectsAvailableScopeCluster,
-				Error: "cannot refer to objects outside control plane",
-			},
-			"test-cluster-scope-resource": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "clusterrole"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "test-cluster-role"},
-				},
-				appNs:         "test",
-				Scope:         RefObjectsAvailableScopeCluster,
-				Output:        []*unstructured.Unstructured{createUnstructured("rbac.authorization.k8s.io/v1", "ClusterRole", "test-cluster-role", "", nil)},
-				IsClusterRole: true,
-			},
-		}
-		for name, tt := range testcases {
-			By("Test " + name)
-			if tt.Scope == "" {
-				tt.Scope = RefObjectsAvailableScopeGlobal
-			}
-			RefObjectsAvailableScope = tt.Scope
-			output, err := SelectRefObjectsForDispatch(context.Background(), k8sClient, tt.appNs, tt.compName, tt.Input)
-			if tt.Error != "" {
-				Expect(err).ShouldNot(BeNil())
-				Expect(err.Error()).Should(ContainSubstring(tt.Error))
-			} else {
-				Expect(err).Should(Succeed())
-				if tt.IsService {
-					Expect(output[0].Object["kind"]).Should(Equal("Service"))
-					Expect(output[0].Object["spec"].(map[string]interface{})["clusterIP"]).Should(BeNil())
-				} else {
-					if tt.IsClusterRole {
-						delete(output[0].Object, "rules")
-					}
-					Expect(output).Should(Equal(tt.Output))
-				}
-			}
-		}
-	})
-
-	It("Test AppendUnstructuredObjects", func() {
-		testCases := map[string]struct {
-			Inputs  []*unstructured.Unstructured
-			Input   *unstructured.Unstructured
-			Outputs []*unstructured.Unstructured
-		}{
-			"overlap": {
-				Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}},
-				Input: &unstructured.Unstructured{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "c",
-				}},
-				Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "c",
-				}}},
-			},
-			"append": {
-				Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}},
-				Input: &unstructured.Unstructured{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
-					"data":       "c",
-				}},
-				Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
-					"data":       "c",
-				}}},
-			},
-		}
-		for name, tt := range testCases {
-			By("Test " + name)
-			Expect(AppendUnstructuredObjects(tt.Inputs, tt.Input)).Should(Equal(tt.Outputs))
-		}
-	})
-
-})
diff --git a/pkg/component/ref_objects_test.go b/pkg/component/ref_objects_test.go
new file mode 100644
index 00000000..c65bf93d
--- /dev/null
+++ b/pkg/component/ref_objects_test.go
@@ -0,0 +1,748 @@
+/*
+Copyright 2021 The KubeVela Authors.
+
+Licensed 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 component
+
+import (
+	"context"
+	"encoding/json"
+	"reflect"
+	"strings"
+	"testing"
+
+	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+	featuregatetesting "k8s.io/component-base/featuregate/testing"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
+	"github.com/oam-dev/kubevela/pkg/features"
+)
+
+func TestGetLabelSelectorFromRefObjectSelector(t *testing.T) {
+	type args struct {
+		selector v1alpha1.ObjectReferrer
+	}
+	tests := []struct {
+		name      string
+		args      args
+		featureOn bool
+		want      map[string]string
+	}{
+		{
+			name: "label selector present",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			want: map[string]string{"app": "my-app"},
+		},
+		{
+			name: "deprecated label selector present and feature on",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: true,
+			want:      map[string]string{"app": "my-app-deprecated"},
+		},
+		{
+			name: "deprecated label selector present and feature off",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: false,
+			want:      nil,
+		},
+		{
+			name: "both present, label selector takes precedence",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector:           map[string]string{"app": "my-app"},
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: true,
+			want:      map[string]string{"app": "my-app"},
+		},
+		{
+			name: "no selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{},
+			},
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, tt.featureOn)
+			if got := GetLabelSelectorFromRefObjectSelector(tt.args.selector); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("GetLabelSelectorFromRefObjectSelector() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestValidateRefObjectSelector(t *testing.T) {
+	type args struct {
+		selector v1alpha1.ObjectReferrer
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "name only",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						Name: "my-obj",
+					},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "label selector only",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "both name and label selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						Name:          "my-obj",
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := ValidateRefObjectSelector(tt.args.selector); (err != nil) != tt.wantErr {
+				t.Errorf("ValidateRefObjectSelector() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestClearRefObjectForDispatch(t *testing.T) {
+	un := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"metadata": map[string]interface{}{
+				"name":              "test-obj",
+				"namespace":         "default",
+				"resourceVersion":   "12345",
+				"generation":        int64(1),
+				"uid":               "abc-def",
+				"creationTimestamp": "2021-01-01T00:00:00Z",
+				"managedFields":     []interface{}{},
+				"ownerReferences":   []interface{}{},
+			},
+			"status": map[string]interface{}{
+				"phase": "Available",
+			},
+		},
+	}
+	ClearRefObjectForDispatch(un)
+
+	if un.GetResourceVersion() != "" {
+		t.Errorf("resourceVersion should be cleared")
+	}
+	if un.GetGeneration() != 0 {
+		t.Errorf("generation should be cleared")
+	}
+	if len(un.GetOwnerReferences()) != 0 {
+		t.Errorf("ownerReferences should be cleared")
+	}
+	if un.GetDeletionTimestamp() != nil {
+		t.Errorf("deletionTimestamp should be nil")
+	}
+	if len(un.GetManagedFields()) != 0 {
+		t.Errorf("managedFields should be cleared")
+	}
+	if un.GetUID() != "" {
+		t.Errorf("uid should be cleared")
+	}
+	if _, found, _ := unstructured.NestedFieldNoCopy(un.Object, "metadata", "creationTimestamp"); found {
+		t.Errorf("creationTimestamp should be removed")
+	}
+	if _, found, _ := unstructured.NestedFieldNoCopy(un.Object, "status"); found {
+		t.Errorf("status should be removed")
+	}
+
+	// Test for service
+	svc := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"name": "test-svc",
+			},
+			"spec": map[string]interface{}{
+				"clusterIP":  "1.2.3.4",
+				"clusterIPs": []interface{}{"1.2.3.4"},
+			},
+		},
+	}
+	ClearRefObjectForDispatch(svc)
+	if _, found, _ := unstructured.NestedString(svc.Object, "spec", "clusterIP"); found {
+		t.Errorf("service clusterIP should be removed")
+	}
+	if _, found, _ := unstructured.NestedStringSlice(svc.Object, "spec", "clusterIPs"); found {
+		t.Errorf("service clusterIPs should be removed")
+	}
+
+	// Test for service with ClusterIPNone
+	svcNone := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"name": "test-svc-none",
+			},
+			"spec": map[string]interface{}{
+				"clusterIP": corev1.ClusterIPNone,
+			},
+		},
+	}
+	ClearRefObjectForDispatch(svcNone)
+	if ip, found, _ := unstructured.NestedString(svcNone.Object, "spec", "clusterIP"); !found || ip != corev1.ClusterIPNone {
+		t.Errorf("service with clusterIP None should not be removed")
+	}
+}
+
+func TestSelectRefObjectsForDispatch(t *testing.T) {
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LegacyObjectTypeIdentifier, true)
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, true)
+	t.Log("Create objects")
+	if err := k8sClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}); err != nil {
+		t.Fatal(err)
+	}
+	for _, obj := range []client.Object{&corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "dynamic",
+			Namespace: "test",
+		},
+	}, &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:       "dynamic",
+			Namespace:  "test",
+			Generation: int64(5),
+		},
+		Spec: corev1.ServiceSpec{
+			ClusterIP: "10.0.0.254",
+			Ports:     []corev1.ServicePort{{Port: 80}},
+		},
+	}, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "by-label-1",
+			Namespace: "test",
+			Labels:    map[string]string{"key": "value"},
+		},
+	}, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "by-label-2",
+			Namespace: "test",
+			Labels:    map[string]string{"key": "value"},
+		},
+	}, &rbacv1.ClusterRole{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "test-cluster-role",
+		},
+	}} {
+		if err := k8sClient.Create(context.Background(), obj); err != nil {
+			t.Fatal(err)
+		}
+	}
+	createUnstructured := func(apiVersion string, kind string, name string, namespace string, labels map[string]interface{}) *unstructured.Unstructured {
+		un := &unstructured.Unstructured{
+			Object: map[string]interface{}{"apiVersion": apiVersion,
+				"kind":     kind,
+				"metadata": map[string]interface{}{"name": name},
+			},
+		}
+		if namespace != "" {
+			un.SetNamespace(namespace)
+		}
+		if labels != nil {
+			un.Object["metadata"].(map[string]interface{})["labels"] = labels
+		}
+		return un
+	}
+	testcases := map[string]struct {
+		Input         v1alpha1.ObjectReferrer
+		compName      string
+		appNs         string
+		Output        []*unstructured.Unstructured
+		Error         string
+		Scope         string
+		IsService     bool
+		IsClusterRole bool
+	}{
+		"normal": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"legacy-type-identifier": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "v1"}},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"invalid-apiVersion": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "a/b/v1"}},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs: "test",
+			Error: "invalid APIVersion",
+		},
+		"invalid-type-identifier": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs: "test",
+			Error: "neither resource or apiVersion/kind is set",
+		},
+		"name-and-selector-both-set": {
+			Input: v1alpha1.ObjectReferrer{ObjectSelector: v1alpha1.ObjectSelector{Name: "dynamic", LabelSelector: map[string]string{"key": "value"}}},
+			appNs: "test",
+			Error: "invalid object selector for ref-objects, name and labelSelector cannot be both set",
+		},
+		"empty-ref-object-name": {
+			Input:    v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"}},
+			compName: "dynamic",
+			appNs:    "test",
+			Output:   []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cannot-find-ref-object": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "static"},
+			},
+			appNs: "test",
+			Error: "failed to load ref object",
+		},
+		"modify-service": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "service"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:     "test",
+			IsService: true,
+		},
+		"by-labels": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{LabelSelector: map[string]string{"key": "value"}},
+			},
+			appNs: "test",
+			Output: []*unstructured.Unstructured{
+				createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
+				createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
+			},
+		},
+		"by-deprecated-labels": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{DeprecatedLabelSelector: map[string]string{"key": "value"}},
+			},
+			appNs: "test",
+			Output: []*unstructured.Unstructured{
+				createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
+				createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
+			},
+		},
+		"no-kind-for-resource": {
+			Input: v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "unknown"}},
+			appNs: "test",
+			Error: "no matches",
+		},
+		"cross-namespace": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
+			},
+			appNs:  "demo",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cross-namespace-forbidden": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
+			},
+			appNs: "demo",
+			Scope: RefObjectsAvailableScopeNamespace,
+			Error: "cannot refer to objects outside the application's namespace",
+		},
+		"cross-cluster": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cross-cluster-forbidden": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
+			},
+			appNs: "test",
+			Scope: RefObjectsAvailableScopeCluster,
+			Error: "cannot refer to objects outside control plane",
+		},
+		"test-cluster-scope-resource": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "clusterrole"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "test-cluster-role"},
+			},
+			appNs:         "test",
+			Scope:         RefObjectsAvailableScopeCluster,
+			Output:        []*unstructured.Unstructured{createUnstructured("rbac.authorization.k8s.io/v1", "ClusterRole", "test-cluster-role", "", nil)},
+			IsClusterRole: true,
+		},
+	}
+	for name, tt := range testcases {
+		t.Run(name, func(t *testing.T) {
+			if tt.Scope == "" {
+				tt.Scope = RefObjectsAvailableScopeGlobal
+			}
+			RefObjectsAvailableScope = tt.Scope
+			output, err := SelectRefObjectsForDispatch(context.Background(), k8sClient, tt.appNs, tt.compName, tt.Input)
+			if tt.Error != "" {
+				if err == nil {
+					t.Fatalf("expected error, got nil")
+				}
+				if !strings.Contains(err.Error(), tt.Error) {
+					t.Fatalf("expected error message to contain %q, got %q", tt.Error, err.Error())
+				}
+			} else {
+				if err != nil {
+					t.Fatal(err)
+				}
+				if tt.IsService {
+					if output[0].Object["kind"] != "Service" {
+						t.Fatalf(`expected kind to be "Service", got %q`, output[0].Object["kind"])
+					}
+					if output[0].Object["spec"].(map[string]interface{})["clusterIP"] != nil {
+						t.Fatalf(`expected clusterIP to be nil, got %v`, output[0].Object["spec"].(map[string]interface{})["clusterIP"])
+					}
+				} else {
+					if tt.IsClusterRole {
+						delete(output[0].Object, "rules")
+					}
+					if !reflect.DeepEqual(output, tt.Output) {
+						t.Fatalf("expected output to be %v, got %v", tt.Output, output)
+					}
+				}
+			}
+		})
+	}
+}
+
+func TestReferredObjectsDelegatingClient(t *testing.T) {
+	objs := []*unstructured.Unstructured{
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name":      "cm-1",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-1"},
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name":      "cm-2",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-2"},
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "Secret",
+				"metadata": map[string]interface{}{
+					"name":      "secret-1",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-1"},
+				},
+			},
+		},
+	}
+
+	delegatingClient := ReferredObjectsDelegatingClient(k8sClient, objs)
+
+	t.Run("Get", func(t *testing.T) {
+		t.Run("should get existing object", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-1", Namespace: "ns-1"}, obj)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if obj.GetName() != "cm-1" {
+				t.Errorf("expected object name cm-1, got %s", obj.GetName())
+			}
+		})
+
+		t.Run("should return not found for non-existing object", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-non-existent", Namespace: "ns-1"}, obj)
+			if !apierrors.IsNotFound(err) {
+				t.Errorf("expected not found error, got %v", err)
+			}
+		})
+
+		t.Run("should return not found for different kind", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-1", Namespace: "ns-1"}, obj)
+			if !apierrors.IsNotFound(err) {
+				t.Errorf("expected not found error, got %v", err)
+			}
+		})
+	})
+
+	t.Run("List", func(t *testing.T) {
+		t.Run("should list all objects of a kind", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 2 {
+				t.Errorf("expected 2 items, got %d", len(list.Items))
+			}
+		})
+
+		t.Run("should list with namespace", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list, client.InNamespace("ns-1"))
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 2 {
+				t.Errorf("expected 2 items, got %d", len(list.Items))
+			}
+
+			list.Items = []unstructured.Unstructured{}
+			err = delegatingClient.List(context.Background(), list, client.InNamespace("ns-2"))
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 0 {
+				t.Errorf("expected 0 items, got %d", len(list.Items))
+			}
+		})
+
+		t.Run("should list with label selector", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list, client.MatchingLabels{"app": "app-1"})
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 1 {
+				t.Errorf("expected 1 item, got %d", len(list.Items))
+			}
+			if list.Items[0].GetName() != "cm-1" {
+				t.Errorf("expected cm-1, got %s", list.Items[0].GetName())
+			}
+		})
+	})
+}
+
+func TestAppendUnstructuredObjects(t *testing.T) {
+	testCases := map[string]struct {
+		Inputs  []*unstructured.Unstructured
+		Input   *unstructured.Unstructured
+		Outputs []*unstructured.Unstructured
+	}{
+		"overlap": {
+			Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}},
+			Input: &unstructured.Unstructured{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "c",
+			}},
+			Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "c",
+			}}},
+		},
+		"append": {
+			Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}},
+			Input: &unstructured.Unstructured{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
+				"data":       "c",
+			}},
+			Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
+				"data":       "c",
+			}}},
+		},
+	}
+	for name, tt := range testCases {
+		t.Run(name, func(t *testing.T) {
+			got := AppendUnstructuredObjects(tt.Inputs, tt.Input)
+			if !reflect.DeepEqual(got, tt.Outputs) {
+				t.Fatalf("expected output to be %v, got %v", tt.Outputs, got)
+			}
+		})
+	}
+}
+
+func TestConvertUnstructuredsToReferredObjects(t *testing.T) {
+	uns := []*unstructured.Unstructured{
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name": "cm-1",
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "apps/v1",
+				"kind":       "Deployment",
+				"metadata": map[string]interface{}{
+					"name": "deploy-1",
+				},
+			},
+		},
+	}
+
+	refObjs, err := ConvertUnstructuredsToReferredObjects(uns)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+
+	if len(refObjs) != 2 {
+		t.Fatalf("expected 2 referred objects, got %d", len(refObjs))
+	}
+
+	for i, un := range uns {
+		raw, err := json.Marshal(un)
+		if err != nil {
+			t.Fatalf("failed to marshal unstructured: %v", err)
+		}
+		expected := common.ReferredObject{
+			RawExtension: runtime.RawExtension{Raw: raw},
+		}
+		if !reflect.DeepEqual(refObjs[i], expected) {
+			t.Errorf("expected refObj %v, got %v", expected, refObjs[i])
+		}
+	}
+}