Add unittests and set mockgen env 05/11205/2
authorYouhwan Seol <yh.seol@samsung.com>
Wed, 22 Feb 2023 05:38:12 +0000 (14:38 +0900)
committerYouhwan Seol <yh.seol@samsung.com>
Wed, 24 May 2023 10:16:06 +0000 (19:16 +0900)
Change-Id: Ib5b24e977d1986e00346fcd12b7fa61060358589
Signed-off-by: Youhwan Seol <yh.seol@samsung.com>
15 files changed:
.gitignore
Makefile [new file with mode: 0644]
go.mod
pkg/api/rest_server_test.go [new file with mode: 0644]
pkg/api/v1/deployment/deployment.go
pkg/api/v1/deployment/deployment_test.go [new file with mode: 0644]
pkg/client/kserve/client.go
pkg/client/onboard/client.go
pkg/controller/v1/adapter/controller.go
pkg/controller/v1/adapter/controller_test.go [new file with mode: 0644]
sample/invalidServing/inferenceServing/values.yaml [new file with mode: 0755]
sample/validServing/inferenceServing/Chart.yaml [new file with mode: 0755]
sample/validServing/inferenceServing/templates/_helpers.tpl [new file with mode: 0755]
sample/validServing/inferenceServing/templates/inferenceservice.yaml [new file with mode: 0755]
sample/validServing/inferenceServing/values.yaml [new file with mode: 0755]

index e5902ed..a6de96e 100755 (executable)
@@ -3,4 +3,5 @@
 docs/_build/
 pkg/helm/data/sample-xapp-2.2.0/
 *.tgz
-go.sum
\ No newline at end of file
+go.sum
+mock*.go
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..6c4b98b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+genmock:
+       go generate ./...
diff --git a/go.mod b/go.mod
index c01b951..d290dd0 100755 (executable)
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
 )
 
 require (
+       github.com/golang/mock v1.4.4
        github.com/spf13/viper v1.7.0
        github.com/xeipuuv/gojsonschema v1.2.0
 )
diff --git a/pkg/api/rest_server_test.go b/pkg/api/rest_server_test.go
new file mode 100644 (file)
index 0000000..c6a56ba
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 api
+
+import (
+       "net/http"
+       "net/http/httptest"
+       "testing"
+
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/api/commons/url"
+       deploymentmock "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/api/v1/deployment/mock"
+
+       "github.com/golang/mock/gomock"
+)
+
+func TestRecivedDeploymentRequest_ExpectCalledDeploymentHandle(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       mockobj := deploymentmock.NewMockCommand(ctrl)
+       gomock.InOrder(
+               mockobj.EXPECT().Deploy(gomock.Any()),
+       )
+
+       // pass mockObj to a real object.
+       deploymentExecutor = mockobj
+
+       ts := httptest.NewServer(setupRouter())
+       defer ts.Close()
+       http.Post(ts.URL+url.V1()+url.IPS(),
+               "application/json", nil)
+}
index 93df005..3712c46 100644 (file)
@@ -30,6 +30,7 @@ import (
        "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/controller/v1/adapter"
 )
 
+//go:generate mockgen -source=deployment.go -destination=./mock/mock_deployment.go -package=mock
 type Command interface {
        Deploy(c *gin.Context)
        Delete(c *gin.Context)
diff --git a/pkg/api/v1/deployment/deployment_test.go b/pkg/api/v1/deployment/deployment_test.go
new file mode 100644 (file)
index 0000000..6d39143
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 deployment
+
+import (
+       "net/http"
+       "net/http/httptest"
+       . "net/url"
+       "testing"
+
+       "github.com/gin-gonic/gin"
+       "github.com/golang/mock/gomock"
+
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/api/commons/url"
+
+       controllermock "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/controller/v1/adapter/mock"
+)
+
+const (
+       testName    = "test_name"
+       testVesrion = "test_version"
+)
+
+func TestReceivedDeploymentRequest_ExpectCalledDeployController(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       mockobj := controllermock.NewMockCommand(ctrl)
+       gomock.InOrder(
+               mockobj.EXPECT().Deploy(testName, testVesrion),
+       )
+
+       ipsAdapter = mockobj
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("name", testName)
+       param.Set("version", testVesrion)
+
+       r, err := http.NewRequest("POST", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Deploy(c)
+}
+
+func TestNegativeReceivedDeploymentRequestWithoutVersionQuery_ExpectErrorReturn(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("name", testName)
+
+       r, err := http.NewRequest("POST", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Deploy(c)
+
+       if w.Code == 201 {
+               t.Errorf("Unexpected Response Code : %d", w.Code)
+       }
+}
+
+func TestNegativeReceivedDeploymentRequestWithoutNameQuery_ExpectErrorREturn(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("version", testVesrion)
+
+       r, err := http.NewRequest("POST", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Deploy(c)
+
+       if w.Code == http.StatusCreated {
+               t.Errorf("Unexpected Response Code : %d", w.Code)
+       }
+}
+
+func TestReceivedDeleteRequest_ExpectCalledDeleteController(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       mockobj := controllermock.NewMockCommand(ctrl)
+       gomock.InOrder(
+               mockobj.EXPECT().Delete(testName),
+       )
+
+       ipsAdapter = mockobj
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("name", testName)
+
+       r, err := http.NewRequest("DELETE", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Delete(c)
+}
+
+func TestNegativeReceivedDeleteRequestWithoutNameQuery_ExpectErrorReturn(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+
+       r, err := http.NewRequest("DELETE", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Delete(c)
+
+       if w.Code == 201 {
+               t.Errorf("Unexpected Response Code : %d", w.Code)
+       }
+}
+
+func TestReceivedUpdateRequest_ExpectCalledUpdateController(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       mockobj := controllermock.NewMockCommand(ctrl)
+       gomock.InOrder(
+               mockobj.EXPECT().Update(testName, testVesrion, gomock.Any()),
+       )
+
+       ipsAdapter = mockobj
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("name", testName)
+       param.Set("version", testVesrion)
+
+       r, err := http.NewRequest("PUT", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Update(c)
+}
+
+func TestNegativeReceivedUpdateRequestWithoutVersionQuery_ExpectErrorReturn(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("name", testName)
+
+       r, err := http.NewRequest("PUT", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Update(c)
+
+       if w.Code == 201 {
+               t.Errorf("Unexpected Response Code : %d", w.Code)
+       }
+}
+
+func TestNegativeReceivedUpdateRequestWithoutNameQuery_ExpectErrorReturn(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       w := httptest.NewRecorder()
+
+       param := Values{}
+       param.Set("version", testVesrion)
+
+       r, err := http.NewRequest("PUT", url.V1()+url.IPS(), nil)
+       if err != nil {
+               t.Errorf("http.NewRequest return Error : %s", err.Error())
+       }
+       r.Header.Set("Content-Type", "application/json")
+       r.URL.RawQuery = param.Encode()
+
+       c, _ := gin.CreateTestContext(w)
+       c.Request = r
+
+       deployment := Executor{}
+       deployment.Update(c)
+
+       if w.Code == 201 {
+               t.Errorf("Unexpected Response Code : %d", w.Code)
+       }
+}
index c485fa1..3af41f4 100644 (file)
@@ -37,6 +37,7 @@ const (
 
 var ifsvGetter func(string) (client_v1beta1.InferenceServiceInterface, error)
 
+//go:generate mockgen -source=client.go -destination=./mock/mock_client.go -package=mock
 type Command interface {
        Init(kubeconfigPath string) error
        Create(values types.Values) (string, error)
index 8833cb6..16a7d72 100644 (file)
@@ -25,6 +25,7 @@ import (
        "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/logger"
 )
 
+//go:generate mockgen -source=client.go -destination=./mock/mock_client.go -package=mock
 type Command interface {
        Get(name string) error
        Download(name string, version string) (string, error)
index 2e34cdf..53283b3 100644 (file)
@@ -28,6 +28,7 @@ import (
        "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/logger"
 )
 
+//go:generate mockgen -source=controller.go -destination=./mock/mock_controller.go -package=mock
 type Command interface {
        Deploy(name string, version string) (string, error)
        Delete(name string) error
diff --git a/pkg/controller/v1/adapter/controller_test.go b/pkg/controller/v1/adapter/controller_test.go
new file mode 100644 (file)
index 0000000..ad80a73
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 adapter
+
+import (
+       "os"
+       "testing"
+
+       "github.com/golang/mock/gomock"
+       "github.com/kserve/kserve/pkg/apis/serving/v1beta1"
+       v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+       kservemock "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/client/kserve/mock"
+       onboardmock "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/client/onboard/mock"
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/errors"
+)
+
+const (
+       testName        = "test_name"
+       testVersion     = "test_version"
+       samplePath      = "/../../../../sample/validServing"
+       invalidPath     = "/../../../../invalid"
+       invalidYAMLPath = "/../../../../sample/invalidServing"
+)
+
+var (
+       sampleIFSV = v1beta1.InferenceService{
+               TypeMeta:   v1.TypeMeta{},
+               ObjectMeta: v1.ObjectMeta{},
+               Spec: v1beta1.InferenceServiceSpec{
+                       Predictor: v1beta1.PredictorSpec{
+                               ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{},
+                               Tensorflow: &v1beta1.TFServingSpec{
+                                       PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{},
+                               },
+                       },
+               },
+               Status: v1beta1.InferenceServiceStatus{},
+       }
+)
+
+func fakeRemoveFunc(path string) (err error) {
+       return nil
+}
+
+func TestNegativeCalledDepolyWithInvalidYAMLPath_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+invalidYAMLPath, nil),
+       )
+
+       // pass mockObj to a real object.
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Deploy(testName, testVersion)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestNegativeCalledDepolyWithInvalidPath_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+invalidPath, nil),
+       )
+
+       // pass mockObj to a real object.
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Deploy(testName, testVersion)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestCalledDepoly_ExpectReturnSuccess(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+samplePath, nil),
+               kserveMockobj.EXPECT().Create(gomock.Any()),
+       )
+
+       // pass mockObj to a real object.
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       exec.Deploy(testName, testVersion)
+}
+
+func TestNegativeCalledDepoly_WhenOnboardClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return("", errors.InternalServerError{}),
+       )
+
+       // pass mockObj to a real object.
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Deploy(testName, testVersion)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestNegativeCalledDepoly_WhenKServeClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+samplePath, nil),
+               kserveMockobj.EXPECT().Create(gomock.Any()).Return("", errors.InternalServerError{}),
+       )
+
+       // pass mockObj to a real object.
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Deploy(testName, testVersion)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestCalledDelete_ExpectReturnSuccess(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Get(testName).Return(nil),
+               kserveMockobj.EXPECT().Delete(testName).Return(nil),
+       )
+
+       // pass mockObj to a real object.
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       err := exec.Delete(testName)
+       if err != nil {
+               t.Errorf("Expect error is nil, but error return, %s", err.Error())
+       }
+}
+
+func TestNegativeCalledDelete_WhenOnboardClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Get(testName).Return(errors.IOError{}),
+       )
+
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       err := exec.Delete(testName)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestNegativeCalledDelete_WhenKServeClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Get(testName).Return(nil),
+               kserveMockobj.EXPECT().Delete(testName).Return(errors.InternalServerError{}),
+       )
+
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       err := exec.Delete(testName)
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestCalledUpdate_ExpectReturnSuccess(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+samplePath, nil),
+               kserveMockobj.EXPECT().Get(testName).Return(&sampleIFSV, nil),
+               kserveMockobj.EXPECT().Update(gomock.Any()),
+       )
+
+       // pass mockObj to a real object.
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       exec.Update(testName, testVersion, "0")
+}
+
+func TestNegativeCalledUpdate_WhenOnboardClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return("", errors.InternalServerError{}),
+       )
+
+       // pass mockObj to a real object.
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Update(testName, testVersion, "0")
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
+
+func TestNegativeCalledUpdate_WhenKServeClientReturnError_ExpectReturnError(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       kserveMockobj := kservemock.NewMockCommand(ctrl)
+       onboardMockobj := onboardmock.NewMockCommand(ctrl)
+
+       workspace, _ := os.Getwd()
+
+       gomock.InOrder(
+               onboardMockobj.EXPECT().Download(testName, testVersion).Return(workspace+samplePath, nil),
+               kserveMockobj.EXPECT().Get(testName).Return(&sampleIFSV, nil),
+               kserveMockobj.EXPECT().Update(gomock.Any()).Return("", errors.InternalServerError{}),
+       )
+
+       // pass mockObj to a real object.
+       kserveClient = kserveMockobj
+       onboardClient = onboardMockobj
+       removeFunc = fakeRemoveFunc
+
+       exec := Executor{}
+
+       _, err := exec.Update(testName, testVersion, "0")
+       if err == nil {
+               t.Errorf("Expect error return, but error is nil")
+       }
+}
diff --git a/sample/invalidServing/inferenceServing/values.yaml b/sample/invalidServing/inferenceServing/values.yaml
new file mode 100755 (executable)
index 0000000..f7e3d60
--- /dev/null
@@ -0,0 +1,19 @@
+# ==================================================================================
+#
+#       Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   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.
+#
+# ==================================================================================
+invalid yaml
+!!@
diff --git a/sample/validServing/inferenceServing/Chart.yaml b/sample/validServing/inferenceServing/Chart.yaml
new file mode 100755 (executable)
index 0000000..711a270
--- /dev/null
@@ -0,0 +1,23 @@
+# ==================================================================================
+#
+#       Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   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.
+#
+# ==================================================================================
+
+apiVersion: v1
+appVersion: "1.0"
+description: ML xApp Helm Chart
+name: lb
+version: 2.0.0
diff --git a/sample/validServing/inferenceServing/templates/_helpers.tpl b/sample/validServing/inferenceServing/templates/_helpers.tpl
new file mode 100755 (executable)
index 0000000..b5505ad
--- /dev/null
@@ -0,0 +1,77 @@
+# ==================================================================================
+#
+#       Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   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.
+#
+# ==================================================================================
+
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "ricxapp.name" -}}
+  {{- default .Chart.Name .Values.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "ricxapp.fullname" -}}
+  {{- $name := ( include "ricxapp.name" . ) -}}
+  {{- $fullname := ( printf "%s-%s" .Release.Namespace $name ) -}}
+  {{- default $fullname .Values.fullname | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "ricxapp.chart" -}}
+  {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "ricxapp.namespace" -}}
+  {{- default .Release.Namespace .Values.nsPrefix -}}
+{{- end -}}
+
+
+
+{{- define "ricxapp.servicename" -}}
+  {{- $name := ( include "ricxapp.fullname" . ) -}}
+  {{- printf "service-%s" $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "ricxapp.configmapname" -}}
+  {{- $name := ( include "ricxapp.fullname" . ) -}}
+  {{- printf "configmap-%s" $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "ricxapp.deploymentname" -}}
+  {{- $name := ( include "ricxapp.fullname" . ) -}}
+  {{- printf "deployment-%s" $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+
+{{- define "ricxapp.containername" -}}
+  {{- $name := ( include "ricxapp.fullname" . ) -}}
+  {{- printf "container-%s" $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+
+{{- define "ricxapp.imagepullsecret" -}}
+  {{- $reponame := .repo -}}
+  {{- $postfix := $reponame | replace "." "-" | replace ":" "-" | replace "/" "-" | trunc 63 | trimSuffix "-" -}}
+  {{- printf "secret-%s" $postfix -}}
+{{- end -}}
diff --git a/sample/validServing/inferenceServing/templates/inferenceservice.yaml b/sample/validServing/inferenceServing/templates/inferenceservice.yaml
new file mode 100755 (executable)
index 0000000..c26ba5d
--- /dev/null
@@ -0,0 +1,42 @@
+# ==================================================================================
+#
+#       Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   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.
+#
+# ==================================================================================
+
+apiVersion: {{ quote .Values.api_version }}
+kind: "InferenceService"
+metadata:
+  name: {{ .Values.fullname }}
+  labels:
+    controller-tools.k8s.io: "1.0"
+    app: {{ .Values.fullname }}
+spec:
+  predictor:
+    serviceAccountName: {{ .Values.ric_serviceaccount_name }}
+    {{- if .Values.max_replicas }}
+    maxReplicas: {{ .Values.max_replicas }}
+    {{- end }}
+    {{- if .Values.max_replicas }}
+    minReplicas: {{ .Values.min_replicas }}
+    {{- end }}
+    {{- if eq .Values.engine "tensorflow" }}
+    tensorflow:
+    {{- end }}
+      storageUri: {{ .Values.storageUri }}
+      {{- if .Values.resources }}
+      resources:
+        {{- toYaml .Values.resources | nindent 10 }}
+      {{- end -}}
diff --git a/sample/validServing/inferenceServing/values.yaml b/sample/validServing/inferenceServing/values.yaml
new file mode 100755 (executable)
index 0000000..1f76f90
--- /dev/null
@@ -0,0 +1,28 @@
+# ==================================================================================
+#
+#       Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   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.
+#
+# ==================================================================================
+
+api_version: serving.kserve.io/v1beta1
+engine: tensorflow
+fullname: lb
+image_pull_policy: Always
+max_replicas: null
+min_replicas: null
+name: lb
+resources: null
+ric_serviceaccount_name: ric-sa
+storageUri: s3://mlpipeline/artifacts/load-balance-pipeline-5xd67/load-balance-pipeline-5xd67-3012811736/lb