Improve O1 agent UT coverage 85/4985/2
authorTimo Tietavainen <timo.tietavainen@nokia.com>
Tue, 3 Nov 2020 21:21:00 +0000 (23:21 +0200)
committerTimo Tietavainen <timo.tietavainen@nokia.com>
Wed, 4 Nov 2020 05:58:03 +0000 (07:58 +0200)
Improve existing unit tests coverage for O1 Agent's NBI package and implement
new unit tests for SBI and main packages.
Fix also few missing nil value checks found from the source code.

Signed-off-by: Timo Tietavainen <timo.tietavainen@nokia.com>
Change-Id: I3bd0cc1193730960e0bbfe8a7563a167edf4df8f

agent/build_o1agent.sh
agent/cmd/o1agent.go
agent/cmd/o1agent_test.go [new file with mode: 0644]
agent/pkg/nbi/nbi.go
agent/pkg/nbi/nbi_test.go
agent/pkg/sbi/sbi.go
agent/pkg/sbi/sbi_test.go [new file with mode: 0644]
agent/pkg/sbi/types.go

index 5b20e41..a708261 100755 (executable)
@@ -35,4 +35,4 @@ GO111MODULE=on GO_ENABLED=0 GOOS=linux
 go build -a -installsuffix cgo -ldflags "-X main.Version=$tag -X main.Hash=$hash" -o o1agent ./cmd/o1agent.go
 
 # Run o1agent UT
-go test -v -p 1 -cover -coverprofile cover.out ./pkg/nbi/ -c -o ./nbi_test && ./nbi_test
+go test -v -p 1 -cover -coverprofile=/go/src/ws/agent/coverage.out ./...
index 387f333..38b7341 100755 (executable)
@@ -33,6 +33,7 @@ import (
 
 var Version string
 var Hash string
+var osExit = os.Exit
 
 type O1Agent struct {
        rmrReady  bool
@@ -73,7 +74,7 @@ func (o *O1Agent) Sighandler() {
 
        <-o.sigChan
        o.nbiClient.Stop()
-       os.Exit(1)
+       osExit(1)
 }
 
 func NewO1Agent() *O1Agent {
diff --git a/agent/cmd/o1agent_test.go b/agent/cmd/o1agent_test.go
new file mode 100644 (file)
index 0000000..808ab46
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+==================================================================================
+  Copyright (c) 2020 AT&T Intellectual Property.
+  Copyright (c) 2020 Nokia
+
+   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 main
+
+import (
+       "github.com/stretchr/testify/assert"
+       "os"
+       "syscall"
+       "testing"
+       "time"
+)
+
+var o1Agent *O1Agent
+
+// Test cases
+func TestMain(M *testing.M) {
+       o1Agent = NewO1Agent()
+       go func() {
+               o1Agent.nbiClient.Start()
+               o1Agent.Run()
+       }()
+       time.Sleep(time.Duration(1) * time.Second)
+       os.Exit(M.Run())
+}
+
+func TestConsume(t *testing.T) {
+       ret := o1Agent.Consume(nil)
+       assert.Nil(t, ret)
+}
+
+func TestConfigChangeHandler(t *testing.T) {
+       o1Agent.ConfigChangeHandler("")
+}
+
+func TestStatusCB(t *testing.T) {
+       assert.True(t, o1Agent.StatusCB())
+}
+
+func TestSighandler(t *testing.T) {
+       oldOsExit := osExit
+       defer func() { osExit = oldOsExit }()
+
+       var exitCode int
+       osExit = func(code int) {
+               exitCode = code
+       }
+
+       go o1Agent.Sighandler()
+       o1Agent.sigChan <- syscall.SIGTERM
+       time.Sleep(time.Duration(1) * time.Second)
+       assert.Equal(t, 1, exitCode)
+}
index 8d5bb5f..b191db5 100755 (executable)
@@ -25,10 +25,10 @@ import (
        "fmt"
        "github.com/spf13/viper"
        "github.com/valyala/fastjson"
+       "os"
        "strings"
        "time"
        "unsafe"
-       "os"
 
        "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
        "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
@@ -48,6 +48,7 @@ import "C"
 var sbiClient sbi.SBIClientInterface
 var nbiClient *Nbi
 var log = xapp.Logger
+var rnib iRnib = xapp.Rnib
 
 func NewNbi(s sbi.SBIClientInterface) *Nbi {
        sbiClient = s
@@ -234,7 +235,11 @@ func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
        root := fmt.Sprintf("%s:ric", module)
        appName := string(value.GetStringBytes(root, "config", "name"))
        namespace := string(value.GetStringBytes(root, "config", "namespace"))
-       control := value.Get(root, "config", "control").String()
+       controlVal := value.Get(root, "config", "control")
+       if controlVal == nil {
+               return nil
+       }
+       control := controlVal.String()
 
        var f interface{}
        err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
@@ -270,10 +275,10 @@ func nbiGnbStateCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, r
        log.Info("nbiGnbStateCB: module='%s' xpath='%s' rpath='%s' [id=%d]", mod, C.GoString(xpath), C.GoString(rpath), reqid)
 
        if mod == "o-ran-sc-ric-xapp-desc-v1" {
-               xappnamespace := os.Getenv("XAPP_NAMESPACE")
-               if xappnamespace == "" {
-                   xappnamespace = "ricxapp"
-               }
+               xappnamespace := os.Getenv("XAPP_NAMESPACE")
+               if xappnamespace == "" {
+                       xappnamespace = "ricxapp"
+               }
                podList, _ := sbiClient.GetAllPodStatus(xappnamespace)
 
                for _, pod := range podList {
@@ -286,20 +291,21 @@ func nbiGnbStateCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, r
        }
 
        if mod == "o-ran-sc-ric-alarm-v1" {
-               alerts, _ := sbiClient.GetAlerts()
-               for _, alert := range alerts.Payload {
-                       id := alert.Annotations["alarm_id"]
-                       path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
-                       nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
-                       nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
-                       nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
-                       nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
-                       nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
+               if alerts, _ := sbiClient.GetAlerts(); alerts != nil {
+                       for _, alert := range alerts.Payload {
+                               id := alert.Annotations["alarm_id"]
+                               path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
+                               nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
+                               nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
+                               nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
+                               nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
+                               nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
+                       }
                }
                return C.SR_ERR_OK
        }
 
-       gnbs, err := xapp.Rnib.GetListGnbIds()
+       gnbs, err := rnib.GetListGnbIds()
        if err != nil || len(gnbs) == 0 {
                log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
                return C.SR_ERR_OK
@@ -307,7 +313,7 @@ func nbiGnbStateCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, r
 
        for _, gnb := range gnbs {
                ranName := gnb.GetInventoryName()
-               info, err := xapp.Rnib.GetNodeb(ranName)
+               info, err := rnib.GetNodeb(ranName)
                if err != nil {
                        log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
                        continue
@@ -387,3 +393,44 @@ func (n *Nbi) NodeType2Str(ntype int) string {
        }
        return "not-specified"
 }
+
+func (n *Nbi) testModuleChangeCB(module string) bool {
+       var event C.sr_event_t = C.SR_EV_CHANGE
+       reqID := C.int(100)
+       modName := C.CString(module)
+       defer C.free(unsafe.Pointer(modName))
+
+       if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
+               return false
+       }
+       return true
+}
+
+func (n *Nbi) testModuleChangeCBDone(module string) bool {
+       var event C.sr_event_t = C.SR_EV_DONE
+       reqID := C.int(100)
+       modName := C.CString(module)
+       defer C.free(unsafe.Pointer(modName))
+
+       if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
+               return false
+       }
+       return true
+}
+
+func (n *Nbi) testGnbStateCB(module string) bool {
+       modName := C.CString(module)
+       defer C.free(unsafe.Pointer(modName))
+       reqID := C.uint32_t(100)
+       parent := make([]*C.char, 1)
+
+       if ret := nbiGnbStateCB(n.session, modName, nil, nil, reqID, &parent[0]); ret != C.SR_ERR_OK {
+               return false
+       }
+       return true
+}
+
+type iRnib interface {
+       GetListGnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
+       GetNodeb(invName string) (*xapp.RNIBNodebInfo, xapp.RNIBIRNibError)
+}
index 953daa6..92835ba 100755 (executable)
@@ -21,6 +21,7 @@ package nbi
 
 import (
        "encoding/json"
+       "fmt"
        "github.com/stretchr/testify/assert"
        "net"
        "net/http"
@@ -28,12 +29,12 @@ import (
        "os"
        "testing"
        "time"
-       "github.com/go-openapi/strfmt"
-       "fmt"
 
-       "github.com/prometheus/alertmanager/api/v2/models"
+       "errors"
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
        apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
        "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
+       "github.com/stretchr/testify/mock"
 )
 
 var XappConfig = `{
@@ -69,17 +70,13 @@ var XappDescriptor = `{
        }
   }`
 
-var kpodOutput = `
-NAME                               READY   STATUS    RESTARTS   AGE
-ricxapp-ueec-7bfdd587db-2jl9j      1/1     Running   53         29d
-ricxapp-anr-6748846478-8hmtz       1-1     Running   1          29d
-ricxapp-dualco-7f76f65c99-5p6c6    0/1     Running   1          29d
-`
-
 var n *Nbi
+var rnibM *rnibMock
 
 // Test cases
 func TestMain(M *testing.M) {
+       rnibM = new(rnibMock)
+       rnib = rnibM
        n = NewNbi(sbi.NewSBIClient("localhost:8080", "localhost:9093", 5))
        go n.Start()
        time.Sleep(time.Duration(1) * time.Second)
@@ -99,6 +96,69 @@ func TestModifyConfigmap(t *testing.T) {
        assert.Equal(t, true, err == nil)
 }
 
+func TestXappDescModuleChangeCB(t *testing.T) {
+       ok := n.testModuleChangeCB("o-ran-sc-ric-xapp-desc-v1")
+       assert.True(t, ok)
+}
+
+func TestUeecConfigModuleChangeCB(t *testing.T) {
+       ok := n.testModuleChangeCB("o-ran-sc-ric-ueec-config-v1")
+       assert.True(t, ok)
+}
+
+func TestUeecConfigDoneModuleChangeCB(t *testing.T) {
+       ok := n.testModuleChangeCBDone("o-ran-sc-ric-ueec-config-v1")
+       assert.True(t, ok)
+}
+
+func TestXappDescGnbStateCB(t *testing.T) {
+       ok := n.testGnbStateCB("o-ran-sc-ric-xapp-desc-v1")
+       assert.True(t, ok)
+}
+
+func TestAlarmGnbStateCB(t *testing.T) {
+       ok := n.testGnbStateCB("o-ran-sc-ric-alarm-v1")
+       assert.True(t, ok)
+}
+
+func TestGnbStateCB(t *testing.T) {
+       var rnibOk xapp.RNIBIRNibError
+       var gNbIDs []*xapp.RNIBNbIdentity
+       gNbID := xapp.RNIBNbIdentity{
+               InventoryName: "test-gnb",
+       }
+       gNbIDs = append(gNbIDs, &gNbID)
+       nodeInfo := xapp.RNIBNodebInfo{}
+
+       rnibM.On("GetListGnbIds").Return(gNbIDs, rnibOk).Once()
+       rnibM.On("GetNodeb", mock.Anything).Return(&nodeInfo, rnibOk).Once()
+       ok := n.testGnbStateCB("")
+       assert.True(t, ok)
+}
+
+func TestGnbStateCBWhenRnibGetListGnbIdsFails(t *testing.T) {
+       var rnibErr xapp.RNIBIRNibError = errors.New("Some RNIB Error")
+
+       rnibM.On("GetListGnbIds").Return(nil, rnibErr).Once()
+       ok := n.testGnbStateCB("")
+       assert.True(t, ok)
+}
+
+func TestGnbStateCBWhenRnibGetNodebFails(t *testing.T) {
+       var rnibOk xapp.RNIBIRNibError
+       var rnibErr xapp.RNIBIRNibError = errors.New("Some RNIB Error")
+       var gNbIDs []*xapp.RNIBNbIdentity
+       gNbID := xapp.RNIBNbIdentity{
+               InventoryName: "test-gnb",
+       }
+       gNbIDs = append(gNbIDs, &gNbID)
+
+       rnibM.On("GetListGnbIds").Return(gNbIDs, rnibOk).Once()
+       rnibM.On("GetNodeb", mock.Anything).Return(nil, rnibErr).Once()
+       ok := n.testGnbStateCB("")
+       assert.True(t, ok)
+}
+
 func TestDeployXApp(t *testing.T) {
        ts := CreateHTTPServer(t, "POST", "/ric/v1/xapps", 8080, http.StatusCreated, apimodel.Xapp{})
        defer ts.Close()
@@ -131,80 +191,6 @@ func TestGetDeployedXapps(t *testing.T) {
        assert.Equal(t, true, err == nil)
 }
 
-func TestGetAlerts(t *testing.T) {
-       tim := strfmt.DateTime(time.Now())
-       fingerprint := "34c8f717936f063f"
-
-       alerts := []models.GettableAlert{
-               models.GettableAlert{
-                       Alert: models.Alert{
-                               Labels: models.LabelSet{
-                                       "status":      "active",
-                                       "alertname":   "E2 CONNECTIVITY LOST TO G-NODEB",
-                                       "severity":    "MAJOR",
-                                       "service":     "RIC:UEEC",
-                                       "system_name": "RIC",
-                               },
-                       },
-                       Annotations: models.LabelSet{
-                               "alarm_id": "8006",
-                               "additional_info": "ethernet",
-                               "description": "eth12",
-                               "instructions": "Not defined",
-                               "summary": "Communication error",
-                       },
-                       EndsAt: &tim,
-                       StartsAt: &tim,
-                       UpdatedAt: &tim,
-                       Fingerprint: &fingerprint,
-               },
-       }
-
-       url := "/api/v2/alerts?active=true&inhibited=true&silenced=true&unprocessed=true"
-       ts := CreateHTTPServer(t, "GET", url, 9093, http.StatusOK, alerts)
-       defer ts.Close()
-
-       resp, err := sbiClient.GetAlerts()
-
-       assert.Equal(t, true, err == nil)
-       assert.Equal(t, true, resp != nil)
-
-       for _, alert := range resp.Payload {
-               assert.Equal(t, alert.Annotations, alerts[0].Annotations)
-               assert.Equal(t, alert.Alert, alerts[0].Alert)
-               assert.Equal(t, alert.Fingerprint, alerts[0].Fingerprint)
-       }
-}
-
-func TestGetAllPodStatus(t *testing.T) {
-       sbi.CommandExec = func(args string) (out string, err error) {
-               assert.Equal(t, "/usr/local/bin/kubectl get pod -n ricxapp", args)
-               return kpodOutput, nil
-       }
-
-       expectedPodList := []sbi.PodStatus{
-               sbi.PodStatus{
-                       Name:   "ueec",
-                       Health: "healthy",
-                       Status: "Running",
-               },
-               sbi.PodStatus{
-                       Name:   "anr",
-                       Health: "unavailable",
-                       Status: "Running",
-               },
-               sbi.PodStatus{
-                       Name:   "dualco",
-                       Health: "unhealthy",
-                       Status: "Running",
-               },
-       }
-
-       podList, err := sbiClient.GetAllPodStatus("ricxapp")
-       assert.Equal(t, true, err == nil)
-       assert.Equal(t, podList, expectedPodList)
-}
-
 func TestErrorCases(t *testing.T) {
        // Invalid config
        err := n.ManageXapps("o-ran-sc-ric-xapp-desc-v1", "", 2)
@@ -296,3 +282,23 @@ func ConfigMatcher(result, expected *apimodel.XAppConfig) bool {
        }
        return false
 }
+
+type rnibMock struct {
+       mock.Mock
+}
+
+func (m *rnibMock) GetListGnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError) {
+       a := m.Called()
+       if a.Get(0) == nil {
+               return nil, a.Error(1)
+       }
+       return a.Get(0).([]*xapp.RNIBNbIdentity), a.Error(1)
+}
+
+func (m *rnibMock) GetNodeb(invName string) (*xapp.RNIBNodebInfo, xapp.RNIBIRNibError) {
+       a := m.Called(invName)
+       if a.Get(0) == nil {
+               return nil, a.Error(1)
+       }
+       return a.Get(0).(*xapp.RNIBNodebInfo), a.Error(1)
+}
index 5120f5e..a201f7f 100755 (executable)
@@ -38,7 +38,6 @@ import (
        apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
 
        "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
-
 )
 
 type PodStatus struct {
@@ -179,9 +178,9 @@ var CommandExec = func(args string) (string, error) {
        cmd.Stdout = &stdout
        cmd.Stderr = &stderr
 
-       xapp.Logger.Debug("Running command: '%s'", cmd)
+       xapp.Logger.Debug("Running command: '%s'", strings.Join(cmd.Args, " "))
        if err := cmd.Run(); err != nil {
-               xapp.Logger.Error("Command failed (%s): %v - %s", cmd, err.Error(), stderr.String())
+               xapp.Logger.Error("Command failed (%s): %v - %s", strings.Join(cmd.Args, " "), err.Error(), stderr.String())
                return "", err
        }
        xapp.Logger.Debug("Command executed successfully!")
@@ -199,4 +198,4 @@ func (s *SBIClient) GetAlerts() (*alert.GetAlertsOK, error) {
        }
 
        return resp, nil
-}
\ No newline at end of file
+}
diff --git a/agent/pkg/sbi/sbi_test.go b/agent/pkg/sbi/sbi_test.go
new file mode 100644 (file)
index 0000000..4d91022
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+==================================================================================
+  Copyright (c) 2020 AT&T Intellectual Property.
+  Copyright (c) 2020 Nokia
+
+   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 sbi_test
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "github.com/go-openapi/strfmt"
+       "net"
+       "net/http"
+       "net/http/httptest"
+       "os"
+       "testing"
+       "time"
+
+       apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
+       "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
+       "github.com/prometheus/alertmanager/api/v2/models"
+       "github.com/stretchr/testify/assert"
+)
+
+var s *sbi.SBIClient
+
+var xappName = "ueec"
+var ns = "ricxapp"
+var release = "ueec-xapp"
+var helmVer = "0.0.1"
+var cfgData = `{
+       "active":true,
+       "interfaceId": {
+               "globalENBId":{
+                       "plmnId": "1234",
+                       "eNBId":"55"
+               }
+       }
+}`
+var kpodOutput = `
+NAME                               READY   STATUS    RESTARTS   AGE
+ricxapp-ueec-7bfdd587db-2jl9j      1/1     Running   53         29d
+ricxapp-anr-6748846478-8hmtz       1-1     Running   1          29d
+ricxapp-dualco-7f76f65c99-5p6c6    0/1     Running   1          29d
+`
+
+// Test cases
+func TestMain(M *testing.M) {
+       s = sbi.NewSBIClient("localhost:8080", "localhost:9093", 5)
+       os.Exit(M.Run())
+}
+
+func TestBuildXappDescriptor(t *testing.T) {
+       var expDesc apimodel.XappDescriptor = apimodel.XappDescriptor{
+               XappName:    &xappName,
+               HelmVersion: helmVer,
+               ReleaseName: release,
+               Namespace:   ns,
+       }
+       assert.Equal(t, expDesc, *getTestXappDescriptor())
+}
+
+func TestDeployXapp(t *testing.T) {
+       ts := createHTTPServer(t, "POST", "/ric/v1/xapps", 8080, http.StatusCreated, apimodel.Xapp{})
+       defer ts.Close()
+       err := s.DeployXapp(getTestXappDescriptor())
+       assert.Nil(t, err)
+}
+
+func TestDeployXappReturnsErrorIfHttpErrorResponse(t *testing.T) {
+       ts := createHTTPServer(t, "POST", "/ric/v1/xapps", 8080, http.StatusInternalServerError, nil)
+       defer ts.Close()
+       err := s.DeployXapp(getTestXappDescriptor())
+       assert.NotNil(t, err)
+}
+
+func TestUndeployXapp(t *testing.T) {
+       ts := createHTTPServer(t, "DELETE", "/ric/v1/xapps/ueec-xapp", 8080, http.StatusNoContent, nil)
+       defer ts.Close()
+       err := s.UndeployXapp(getTestXappDescriptor())
+       assert.Nil(t, err)
+}
+
+func TestUndeployXappReturnsErrorIfHttpErrorResponse(t *testing.T) {
+       ts := createHTTPServer(t, "DELETE", "/ric/v1/xapps/ueec-xapp", 8080, http.StatusInternalServerError, nil)
+       defer ts.Close()
+       err := s.UndeployXapp(getTestXappDescriptor())
+       assert.NotNil(t, err)
+}
+
+func TestGetDeployedXapps(t *testing.T) {
+       ts := createHTTPServer(t, "GET", "/ric/v1/xapps", 8080, http.StatusOK, apimodel.AllDeployedXapps{})
+       defer ts.Close()
+       err := s.GetDeployedXapps()
+       assert.Nil(t, err)
+}
+
+func TestGetDeployedXappsReturnsErrorIfHttpErrorResponse(t *testing.T) {
+       ts := createHTTPServer(t, "GET", "/ric/v1/xapps", 8080, http.StatusInternalServerError, apimodel.AllDeployedXapps{})
+       defer ts.Close()
+       err := s.GetDeployedXapps()
+       assert.NotNil(t, err)
+}
+
+func TestBuildXappConfig(t *testing.T) {
+       expResp := &apimodel.XAppConfig{
+               Metadata: &apimodel.ConfigMetadata{
+                       XappName:  &xappName,
+                       Namespace: &ns,
+               },
+               Config: cfgData,
+       }
+       resp := s.BuildXappConfig(xappName, ns, cfgData)
+       assert.Equal(t, expResp, resp)
+}
+
+func TestModifyXappConfig(t *testing.T) {
+       ts := createHTTPServer(t, "PUT", "/ric/v1/config", 8080, http.StatusOK, apimodel.ConfigValidationErrors{})
+       defer ts.Close()
+       xappCfg := s.BuildXappConfig(xappName, ns, cfgData)
+       err := s.ModifyXappConfig(xappCfg)
+       assert.Nil(t, err)
+}
+
+func TestModifyXappConfigReturnsErrorIfHttpErrorResponse(t *testing.T) {
+       ts := createHTTPServer(t, "PUT", "/ric/v1/config", 8080, http.StatusInternalServerError, nil)
+       defer ts.Close()
+       xappCfg := s.BuildXappConfig(xappName, ns, cfgData)
+       err := s.ModifyXappConfig(xappCfg)
+       assert.NotNil(t, err)
+}
+
+func TestGetAllPodStatus(t *testing.T) {
+       oldCmdExec := sbi.CommandExec
+       defer func() { sbi.CommandExec = oldCmdExec }()
+       sbi.CommandExec = func(args string) (out string, err error) {
+               assert.Equal(t, "/usr/local/bin/kubectl get pod -n ricxapp", args)
+               return kpodOutput, nil
+       }
+
+       expPodList := []sbi.PodStatus{
+               sbi.PodStatus{
+                       Name:   "ueec",
+                       Health: "healthy",
+                       Status: "Running",
+               },
+               sbi.PodStatus{
+                       Name:   "anr",
+                       Health: "unavailable",
+                       Status: "Running",
+               },
+               sbi.PodStatus{
+                       Name:   "dualco",
+                       Health: "unhealthy",
+                       Status: "Running",
+               },
+       }
+
+       podList, err := s.GetAllPodStatus("ricxapp")
+       assert.Nil(t, err)
+       assert.Equal(t, podList, expPodList)
+}
+
+func TestGetAllPodStatusReturnsErrorIfKubectlCommandFails(t *testing.T) {
+       oldCmdExec := sbi.CommandExec
+       defer func() { sbi.CommandExec = oldCmdExec }()
+       sbi.CommandExec = func(args string) (out string, err error) {
+               assert.Equal(t, "/usr/local/bin/kubectl get pod -n ricxapp", args)
+               return "", errors.New("Some kubectl error")
+       }
+
+       expPodList := make([]sbi.PodStatus, 0)
+
+       podList, err := s.GetAllPodStatus("ricxapp")
+       assert.NotNil(t, err)
+       assert.Equal(t, podList, expPodList)
+}
+
+func TestGetHealthState(t *testing.T) {
+       assert.Equal(t, "healthy", s.GetHealthState("1/1"))
+}
+
+func TestGetHealthStateReturnsUnavailableIfReadyStatusUnkown(t *testing.T) {
+       assert.Equal(t, "unavailable", s.GetHealthState(""))
+}
+
+func TestGetHealthStateReturnsUnhealthyIfReadyStatusNotUp(t *testing.T) {
+       assert.Equal(t, "unhealthy", s.GetHealthState("0/1"))
+}
+
+func TestGetAlerts(t *testing.T) {
+       tim := strfmt.DateTime(time.Now())
+       fingerprint := "34c8f717936f063f"
+
+       alerts := []models.GettableAlert{
+               models.GettableAlert{
+                       Alert: models.Alert{
+                               Labels: models.LabelSet{
+                                       "status":      "active",
+                                       "alertname":   "E2 CONNECTIVITY LOST TO G-NODEB",
+                                       "severity":    "MAJOR",
+                                       "service":     "RIC:UEEC",
+                                       "system_name": "RIC",
+                               },
+                       },
+                       Annotations: models.LabelSet{
+                               "alarm_id":        "8006",
+                               "additional_info": "ethernet",
+                               "description":     "eth12",
+                               "instructions":    "Not defined",
+                               "summary":         "Communication error",
+                       },
+                       EndsAt:      &tim,
+                       StartsAt:    &tim,
+                       UpdatedAt:   &tim,
+                       Fingerprint: &fingerprint,
+               },
+       }
+
+       url := "/api/v2/alerts?active=true&inhibited=true&silenced=true&unprocessed=true"
+       ts := createHTTPServer(t, "GET", url, 9093, http.StatusOK, alerts)
+       defer ts.Close()
+
+       resp, err := s.GetAlerts()
+
+       assert.Nil(t, err)
+       assert.Equal(t, true, resp != nil)
+       assert.NotNil(t, resp)
+
+       for _, alert := range resp.Payload {
+               assert.Equal(t, alert.Annotations, alerts[0].Annotations)
+               assert.Equal(t, alert.Alert, alerts[0].Alert)
+               assert.Equal(t, alert.Fingerprint, alerts[0].Fingerprint)
+       }
+}
+
+func TestGetAlertsReturnErrorIfHttpErrorResponse(t *testing.T) {
+       url := "/api/v2/alerts?active=true&inhibited=true&silenced=true&unprocessed=true"
+       ts := createHTTPServer(t, "GET", url, 9093, http.StatusInternalServerError, nil)
+       defer ts.Close()
+
+       resp, err := s.GetAlerts()
+       assert.NotNil(t, err)
+       assert.Nil(t, resp)
+}
+
+func TestCommandExec(t *testing.T) {
+       resp, err := sbi.CommandExec("date")
+       assert.NotEqual(t, "", resp)
+       assert.Nil(t, err)
+}
+
+func TestCommandExecReturnErrorIfCmdExecutionFails(t *testing.T) {
+       resp, err := sbi.CommandExec("some-not-existing-command")
+       assert.Equal(t, "", resp)
+       assert.NotNil(t, err)
+}
+
+func getTestXappDescriptor() *apimodel.XappDescriptor {
+       return s.BuildXappDescriptor(xappName, ns, release, helmVer)
+}
+
+func createHTTPServer(t *testing.T, method, url string, port, status int, respData interface{}) *httptest.Server {
+       l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
+       if err != nil {
+               t.Error("Failed to create listener: " + err.Error())
+       }
+       ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+               assert.Equal(t, r.Method, method)
+               assert.Equal(t, r.URL.String(), url)
+               w.Header().Add("Content-Type", "application/json")
+               w.WriteHeader(status)
+               b, _ := json.Marshal(respData)
+               w.Write(b)
+       }))
+       ts.Listener.Close()
+       ts.Listener = l
+
+       ts.Start()
+
+       return ts
+}
index ae522bb..f5cd190 100755 (executable)
@@ -29,7 +29,7 @@ import (
 type SBIClient struct {
        appmgrAddr   string
        alertmgrAddr string
-       timeout          time.Duration
+       timeout      time.Duration
 }
 
 type SBIClientInterface interface {