"encoding/json"
"errors"
"fmt"
+ "github.com/spf13/viper"
+ "github.com/valyala/fastjson"
+ "github.com/xeipuuv/gojsonschema"
"io/ioutil"
"os"
"path"
"regexp"
- "strings"
"strconv"
- "github.com/spf13/viper"
- "github.com/valyala/fastjson"
- "github.com/xeipuuv/gojsonschema"
+ "strings"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
)
+var kubeExec = util.KubectlExec
+var helmExec = util.HelmExec
+
type CM struct{}
func NewCM() *CM {
c := models.XAppConfig{
Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
- Config: activeConfig,
+ Config: activeConfig,
}
configList = append(configList, &c)
}
}
func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
- fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
+ fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
if validationErrors, err := cm.Validate(r); err != nil {
return validationErrors, err
}
return v, err
}
-
func (cm *CM) GenerateJSONFile(jsonString string) error {
cmJson, err := json.RawMessage(jsonString).MarshalJSON()
if err != nil {
func (cm *CM) ReadConfigmap(name string, ns string) (string, error) {
args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
- out, err := util.KubectlExec(args)
+ out, err := kubeExec(args)
return string(out), err
}
-func (cm *CM) ReplaceConfigMap(name, ns string) (error) {
+func (cm *CM) ReplaceConfigMap(name, ns string) error {
cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -"
args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig"))
- _, err := util.KubectlExec(args)
+ _, err := kubeExec(args)
return err
}
repo := viper.GetString("helm.repo-name")
fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
- _, err = util.HelmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
+ _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
return
}
ns := cm.GetNamespace("")
args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
- out, err := util.KubectlExec(args)
+ out, err := kubeExec(args)
if err != nil {
return
}
rname := viper.GetString("helm.repo-name")
cmdArgs := strings.Join([]string{"search ", rname}, "")
- out, err := util.HelmExec(cmdArgs)
+ out, err := helmExec(cmdArgs)
if err != nil {
return
}
"errors"
"os"
"reflect"
+ "strings"
"testing"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
)
+const (
+ expectedHelmSearchCmd = "search helm-repo"
+ expectedHelmFetchCmd = `fetch --untar --untardir /tmp helm-repo/dummy-xapp`
+)
+
+var caughtKubeExecArgs []string
+var kubeExecRetOut string
+var kubeExecRetErr error
+var caughtHelmExecArgs string
+var helmExecRetOut string
+var helmExecRetErr error
+
+var expectedKubectlGetCmd []string = []string{
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-anr-appconfig`,
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-appmgr-appconfig`,
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dualco-appconfig`,
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-reporter-appconfig`,
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-uemgr-appconfig`,
+}
+
var helmSearchOutput = `
helm-repo/anr 0.0.1 1.0 Helm Chart for Nokia ANR (Automatic Neighbour Relation) xAPP
helm-repo/appmgr 0.0.2 1.0 Helm Chart for xAppManager
}
}
`
+var cfgData = `{
+ "active":true,
+ "interfaceId": {
+ "globalENBId":{
+ "plmnId": "1234",
+ "eNBId":"55"
+ }
+ }
+}`
type ConfigSample struct {
Level int
os.Exit(code)
}
-func TestGetRtmData(t *testing.T) {
+func TestUploadConfigAllSuccess(t *testing.T) {
+ var cfg interface{}
+ var expectedResult models.AllXappConfig
+ ns := "ricxapp"
+ xapps := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
+
+ if ret := json.Unmarshal([]byte(cfgData), &cfg); ret != nil {
+ t.Errorf("UploadConfigAll Json unmarshal failed: %v", ret)
+ }
+
+ for i, _ := range xapps {
+ expectedResult = append(expectedResult,
+ &models.XAppConfig{
+ Config: cfg,
+ Metadata: &models.ConfigMetadata{
+ Namespace: &ns,
+ XappName: &xapps[i],
+ },
+ },
+ )
+ }
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ //Fake helm search success
+ helmExecRetOut = helmSearchOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' success
+ kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
+
+ result := NewCM().UploadConfigAll()
+ if !reflect.DeepEqual(result, expectedResult) {
+ t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedResult, result)
+ }
+ if caughtHelmExecArgs != expectedHelmSearchCmd {
+ t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
+ }
+ if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubectlGetCmd) {
+ t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedKubectlGetCmd, caughtKubeExecArgs)
+ }
+}
+
+func TestUploadConfigAllReturnsEmptyMapIfAllConfigMapReadsFail(t *testing.T) {
+ var expectedResult models.AllXappConfig
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ //Fake helm search success
+ helmExecRetOut = helmSearchOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' failure
+ kubeExecRetErr = errors.New("some error")
+
+ result := NewCM().UploadConfigAll()
+ if !reflect.DeepEqual(result, expectedResult) {
+ t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedResult, result)
+ }
+}
+
+func TestUploadConfigElementSuccess(t *testing.T) {
+ var cfg interface{}
+ var expectedResult models.AllXappConfig
+ ns := "ricxapp"
+ xapps := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
+
+ if ret := json.Unmarshal([]byte(cfgData), &cfg); ret != nil {
+ t.Errorf("UploadConfigElement Json unmarshal failed: %v", ret)
+ }
+
+ for i, _ := range xapps {
+ expectedResult = append(expectedResult,
+ &models.XAppConfig{
+ Config: cfg.(map[string]interface{})["active"],
+ Metadata: &models.ConfigMetadata{
+ Namespace: &ns,
+ XappName: &xapps[i],
+ },
+ },
+ )
+ }
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ //Fake helm search success
+ helmExecRetOut = helmSearchOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' success
+ kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
+
+ result := NewCM().UploadConfigElement("active")
+ if !reflect.DeepEqual(result, expectedResult) {
+ t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedResult, result)
+ }
+ if caughtHelmExecArgs != expectedHelmSearchCmd {
+ t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
+ }
+ if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubectlGetCmd) {
+ t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedKubectlGetCmd, caughtKubeExecArgs)
+ }
+}
+
+func TestUploadConfigElementReturnsEmptyMapIfElementMissingFromConfigMap(t *testing.T) {
+ var expectedResult models.AllXappConfig
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ //Fake helm search success
+ helmExecRetOut = helmSearchOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' success
+ kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
+
+ //Try to upload non-existing configuration element
+ result := NewCM().UploadConfigElement("some-not-existing-element")
+ if !reflect.DeepEqual(result, expectedResult) {
+ t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedResult, result)
+ }
+}
+
+func TestGetRtmDataSuccess(t *testing.T) {
+ expectedKubeCmd := []string{
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
+ }
expectedMsgs := appmgr.RtmData{
TxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
RxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
Policies: []int64{11, 22, 33},
}
- util.KubectlExec = func(args string) (out []byte, err error) {
- return []byte(kubectlConfigmapOutput), nil
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' success
+ kubeExecRetOut = kubectlConfigmapOutput
+
+ result := NewCM().GetRtmData("dummy-xapp")
+ if !reflect.DeepEqual(result, expectedMsgs) {
+ t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
+ }
+ if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
+ t.Errorf("GetRtmData failed: expected: '%v', got: '%v'", expectedKubeCmd, caughtKubeExecArgs)
+ }
+}
+
+func TestGetRtmDataReturnsNoDataIfConfigmapGetFails(t *testing.T) {
+ var expectedMsgs appmgr.RtmData
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' failure
+ kubeExecRetErr = errors.New("some error")
+
+ result := NewCM().GetRtmData("dummy-xapp")
+ if !reflect.DeepEqual(result, expectedMsgs) {
+ t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
}
+}
+
+func TestGetRtmDataReturnsNoDataIfJsonParseFails(t *testing.T) {
+ var expectedMsgs appmgr.RtmData
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' to return nothing what will cause JSON parse failure
result := NewCM().GetRtmData("dummy-xapp")
if !reflect.DeepEqual(result, expectedMsgs) {
- t.Errorf("TestGetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
+ t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
}
}
}
func TestFetchChartSuccess(t *testing.T) {
- util.HelmExec = func(args string) (out []byte, err error) {
- return
- }
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
if NewCM().FetchChart("dummy-xapp") != nil {
t.Errorf("TestFetchChart failed!")
}
}
+func TestGetNamespaceSuccess(t *testing.T) {
+ if ns := NewCM().GetNamespace("my-ns"); ns != "my-ns" {
+ t.Errorf("GetNamespace failed: expected: my-ns, got: %s", ns)
+ }
+}
+
+func TestGetNamespaceReturnsConfiguredNamespaceName(t *testing.T) {
+ if ns := NewCM().GetNamespace(""); ns != "ricxapp" {
+ t.Errorf("GetNamespace failed: expected: ricxapp, got: %s", ns)
+ }
+}
+
func TestGetNamesFromHelmRepoSuccess(t *testing.T) {
expectedResult := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
- util.HelmExec = func(args string) (out []byte, err error) {
- return []byte(helmSearchOutput), nil
- }
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ //Fake helm search success
+ helmExecRetOut = helmSearchOutput
names := NewCM().GetNamesFromHelmRepo()
if !reflect.DeepEqual(names, expectedResult) {
t.Errorf("GetNamesFromHelmRepo failed: expected %v, got %v", expectedResult, names)
}
+ if caughtHelmExecArgs != expectedHelmSearchCmd {
+ t.Errorf("GetNamesFromHelmRepo failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
+ }
}
func TestGetNamesFromHelmRepoFailure(t *testing.T) {
expectedResult := []string{}
- util.HelmExec = func(args string) (out []byte, err error) {
- return []byte(helmSearchOutput), errors.New("Command failed!")
- }
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helmSearchOutput
+ helmExecRetErr = errors.New("Command failed!")
names := NewCM().GetNamesFromHelmRepo()
if names != nil {
}
func TestBuildConfigMapSuccess(t *testing.T) {
+ expectedKubeCmd := []string{
+ `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
+ }
name := "dummy-xapp"
namespace := "ricxapp"
m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
- s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, "Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
+ s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
+ `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
- util.KubectlExec = func(args string) (out []byte, err error) {
- return []byte(`{"logger": {"level": 2}}`), nil
- }
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' success
+ kubeExecRetOut = `{"logger": {"level": 2}}`
cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
if err != nil {
t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
}
+ if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
+ t.Errorf("BuildConfigMap failed: expected: %v, got: %v", expectedKubeCmd, caughtKubeExecArgs)
+ }
}
-func TestUpdateConfigMapFails(t *testing.T) {
+func TestBuildConfigMapReturnErrorIfJsonMarshalFails(t *testing.T) {
+ name := "dummy-xapp"
+ namespace := "ricxapp"
+ m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
+ //Give channel as a configuration input, this will fail JSON marshal
+ cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: make(chan int)})
+ if err == nil {
+ t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
+ }
+}
+
+func TestBuildConfigMapReturnErrorIfKubectlGetConfigmapFails(t *testing.T) {
+ name := "dummy-xapp"
+ namespace := "ricxapp"
+ m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
+ s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
+ `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl get configmap' failure
+ kubeExecRetErr = errors.New("some error")
+
+ cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
+ if err == nil {
+ t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
+ } else if err.Error() != "some error" {
+ t.Errorf("BuildConfigMap failed: expected: 'some error', got: '%s'", err.Error())
+ }
+}
+
+func TestBuildConfigMapReturnErrorIfJsonParserFails(t *testing.T) {
+ name := "dummy-xapp"
+ namespace := "ricxapp"
+ m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
+ s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
+ `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Return empty json that causes JSON parser to fail
+ kubeExecRetOut = ``
+
+ cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
+ if err == nil {
+ t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
+ }
+}
+
+func TestGenerateJSONFileSuccess(t *testing.T) {
+ err := NewCM().GenerateJSONFile("{}")
+ if err != nil {
+ t.Errorf("GenerateJSONFile failed: %v", err)
+ }
+}
+
+func TestReplaceConfigMapSuccess(t *testing.T) {
+ name := "dummy-xapp"
+ namespace := "ricxapp"
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ //Fake 'kubectl create configmap' success
+ kubeExecRetOut = ""
+
+ err := NewCM().ReplaceConfigMap(name, namespace)
+ if err != nil {
+ t.Errorf("ReplaceConfigMap failed: %v", err)
+ }
+}
+
+func TestUpdateConfigMapReturnsErrorIfSchemaFileIsMissing(t *testing.T) {
+ name := "dummy-xapp"
+ namespace := "ricxapp"
+ config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = `{}`
+
+ //Will fail at schema reading, because schema file is mission
+ validationErrors, err := NewCM().UpdateConfigMap(config)
+ if err == nil {
+ t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
+ }
+ if caughtHelmExecArgs != expectedHelmFetchCmd {
+ t.Errorf("UpdateConfigMap failed: expected: %v, got: %v", expectedHelmFetchCmd, caughtHelmExecArgs)
+ }
+}
+
+func TestUpdateConfigMapReturnsErrorIfHelmFetchChartFails(t *testing.T) {
name := "dummy-xapp"
namespace := "ricxapp"
config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some error")
+
validationErrors, err := NewCM().UpdateConfigMap(config)
if err == nil {
t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
func TestValidationFails(t *testing.T) {
var d interface{}
var cfg map[string]interface{}
+
err := json.Unmarshal([]byte(`{"active": "INVALID", "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg)
err = NewCM().ReadFile("../../test/schema.json", &d)
feedback, err := NewCM().doValidate(d, cfg)
if err == nil {
- t.Errorf("doValidate should faile but didn't: %v -> %v", err, feedback)
+ t.Errorf("doValidate should fail but didn't: %v -> %v", err, feedback)
}
appmgr.Logger.Debug("Feedbacks: %v", feedback)
}
+
+func TestReadFileReturnsErrorIfFileReadFails(t *testing.T) {
+ var d interface{}
+
+ if err := NewCM().ReadFile("not/existing/test/schema.json", &d); err == nil {
+ t.Errorf("ReadFile should fail but it didn't")
+ }
+}
+
+func TestReadFileReturnsErrorIfJsonUnmarshalFails(t *testing.T) {
+ var d interface{}
+
+ if err := NewCM().ReadFile("../../test/faulty_schema.json", &d); err == nil {
+ t.Errorf("ReadFile should fail but it didn't")
+ }
+}
+
+func mockedKubeExec(args string) (out []byte, err error) {
+ caughtKubeExecArgs = append(caughtKubeExecArgs, args)
+ return []byte(kubeExecRetOut), kubeExecRetErr
+}
+
+func resetKubeExecMock() {
+ kubeExec = util.KubectlExec
+ caughtKubeExecArgs = nil
+ kubeExecRetOut = ""
+ kubeExecRetErr = nil
+}
+
+func mockedHelmExec(args string) (out []byte, err error) {
+ caughtHelmExecArgs = args
+ return []byte(helmExecRetOut), helmExecRetErr
+}
+
+func resetHelmExecMock() {
+ helmExec = util.HelmExec
+ caughtHelmExecArgs = ""
+ helmExecRetOut = ""
+ helmExecRetErr = nil
+}
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
)
+var kubeExec = util.KubectlExec
+var helmExec = util.HelmExec
+
type Helm struct {
initDone bool
cm *cm.CM
}
func (h *Helm) Run(args string) (out []byte, err error) {
- return util.HelmExec(args)
+ return helmExec(args)
}
// API functions
return out, err
}
- return util.HelmExec(strings.Join([]string{"init -c --skip-refresh"}, ""))
+ return helmExec(strings.Join([]string{"init -c --skip-refresh"}, ""))
}
func (h *Helm) AddRepo() (out []byte, err error) {
repoArgs := fmt.Sprintf(" %s %s ", viper.GetString("helm.repo-name"), viper.GetString("helm.repo"))
credentials := fmt.Sprintf(" --username %s --password %s", string(username), string(password))
- return util.HelmExec(strings.Join([]string{"repo add ", repoArgs, credentials}, ""))
+ return helmExec(strings.Join([]string{"repo add ", repoArgs, credentials}, ""))
}
func (h *Helm) Install(m models.XappDescriptor) (xapp models.Xapp, err error) {
appmgr.Logger.Info("Getting xapps status: %v", err.Error())
return
}
-
return h.ParseStatus(name, string(out))
}
port = 4560 // Default
ns := h.cm.GetNamespace("")
args := fmt.Sprintf(" get service -n %s service-%s-%s-rmr -o json", ns, ns, name)
- out, err := util.KubectlExec(args)
+ out, err := kubeExec(args)
if err != nil {
return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), 4560
}
}
if cmOverride == true {
- args = fmt.Sprintf("%s ---set ricapp.appconfig.override=%s-appconfig", args, *x.XappName)
+ args = fmt.Sprintf("%s --set ricapp.appconfig.override=%s-appconfig", args, *x.XappName)
}
if x.OverrideFile != nil {
package helm
import (
+ "errors"
+ "github.com/spf13/viper"
"os"
"reflect"
"strconv"
+ "strings"
"testing"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
)
+var caughtKubeExecArgs string
+var kubeExecRetOut string
+var kubeExecRetErr error
+var caughtHelmExecArgs string
+var helmExecRetOut string
+var helmExecRetErr error
var helmStatusOutput = `
LAST DEPLOYED: Sat Mar 9 06:50:45 2019
NAMESPACE: default
dummy-xapp 3/3 3 3 55m
`
-var helListOutput = `Next: ""
+var helListAllOutput = `Next: ""
Releases:
- AppVersion: "1.0"
Chart: dummy-xapp-chart-0.1.0
Updated: Sun Mar 24 07:17:00 2019
`
-var helmServiceOutput = `{
+var helListOutput = `Next: ""
+Releases:
+- AppVersion: "1.0"
+ Chart: dummy-xapp-chart-0.1.0
+ Name: dummy-xapp
+ Namespace: default
+ Revision: 1
+ Status: DEPLOYED
+ Updated: Mon Mar 11 06:55:05 2019
+ `
+
+var kubeServiceOutput = `{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
os.Exit(code)
}
-func TestHelmStatus(t *testing.T) {
- util.KubectlExec = func(args string) (out []byte, err error) {
- return []byte(helmServiceOutput), nil
+func TestInit(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+
+ NewHelm().Init()
+
+ expectedHelmCommand := "init -c --skip-refresh"
+ if caughtHelmExecArgs != expectedHelmCommand {
+ t.Errorf("Init failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
+ }
+}
+
+func TestAddRepoSuccess(t *testing.T) {
+ defer func() {
+ resetHelmExecMock()
+ removeTestUsernameFile()
+ removeTestPasswordFile()
+ }()
+ helmExec = mockedHelmExec
+
+ if err := writeTestUsernameFile(); err != nil {
+ t.Errorf("AddRepo username file create failed: %s", err)
+ return
+ }
+ if err := writeTestPasswordFile(); err != nil {
+ t.Errorf("AddRepo password file create failed: %s", err)
+ return
+ }
+
+ if _, err := NewHelm().AddRepo(); err != nil {
+ t.Errorf("AddRepo failed: %v", err)
+ }
+
+ if !strings.Contains(caughtHelmExecArgs, "repo add") {
+ t.Errorf("AddRepo failed: expected %v, got %v", "repo add", caughtHelmExecArgs)
+ }
+}
+
+func TestAddRepoReturnsErrorIfNoUsernameFile(t *testing.T) {
+ if _, err := NewHelm().AddRepo(); err == nil {
+ t.Errorf("AddRepo expected to fail but it didn't")
}
+}
+
+func TestAddRepoReturnsErrorIfNoPasswordFile(t *testing.T) {
+ defer func() { resetHelmExecMock(); removeTestUsernameFile() }()
+ helmExec = mockedHelmExec
+
+ if err := writeTestUsernameFile(); err != nil {
+ t.Errorf("AddRepo username file create failed: %s", err)
+ return
+ }
+ if _, err := NewHelm().AddRepo(); err == nil {
+ t.Errorf("AddRepo expected to fail but it didn't")
+ }
+}
+
+func TestInstallSuccess(t *testing.T) {
+ name := "dummy-xapp"
+ xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"}
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helmStatusOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetOut = kubeServiceOutput
+
+ xapp, err := NewHelm().Install(xappDesc)
+ if err != nil {
+ t.Errorf("Install failed: %v", err)
+ }
+ validateXappModel(t, xapp)
+}
+
+func TestInstallReturnsErrorIfHelmRepoUpdateFails(t *testing.T) {
+ name := "dummy-xapp"
+ xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"}
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
+
+ if _, err := NewHelm().Install(xappDesc); err == nil {
+ t.Errorf("Install expected to fail but it didn't")
+ }
+}
+
+func TestStatusSuccess(t *testing.T) {
+ name := "dummy-xapp"
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helmStatusOutput
+
+ xapp, err := NewHelm().Status(name)
+ if err != nil {
+ t.Errorf("Status failed: %v", err)
+ }
+ validateXappModel(t, xapp)
+}
+
+func TestStatusReturnsErrorIfHelmStatusFails(t *testing.T) {
+ name := "dummy-xapp"
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
+
+ if _, err := NewHelm().Status(name); err == nil {
+ t.Errorf("Status expected to fail but it didn't")
+ }
+}
+
+func TestParseStatusSuccess(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helListOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetOut = kubeServiceOutput
+
xapp, err := NewHelm().ParseStatus("dummy-xapp", helmStatusOutput)
if err != nil {
- t.Errorf("Helm install failed: %v", err)
+ t.Errorf("ParseStatus failed: %v", err)
}
- x := getXappData()
- xapp.Version = "1.0"
- if *x.Name != *xapp.Name || x.Status != xapp.Status || x.Version != xapp.Version {
- t.Errorf("\n%v \n%v", *xapp.Name, *x.Name)
+ validateXappModel(t, xapp)
+
+ expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp"
+ if caughtHelmExecArgs != expectedHelmCommand {
+ t.Errorf("ParseStatus failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
+ }
+}
+
+func TestListSuccess(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helListAllOutput
+
+ names, err := NewHelm().List()
+ if err != nil {
+ t.Errorf("List failed: %v", err)
}
- if *x.Instances[0].Name != *xapp.Instances[0].Name || x.Instances[0].Status != xapp.Instances[0].Status {
- t.Errorf("\n1:%v 2:%v", *x.Instances[0].Name, *xapp.Instances[0].Name)
+ if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) {
+ t.Errorf("List failed: %v", err)
+ }
+ expectedHelmCommand := "list --all --deployed --output yaml --namespace=ricxapp"
+ if caughtHelmExecArgs != expectedHelmCommand {
+ t.Errorf("List: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
}
+}
+
+func TestListReturnsErrorIfHelmListFails(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
- if x.Instances[0].IP != xapp.Instances[0].IP || x.Instances[0].Port != xapp.Instances[0].Port {
- t.Errorf("\n%v - %v, %v - %v", x.Instances[0].IP, xapp.Instances[0].IP, x.Instances[0].Port, xapp.Instances[0].Port)
+ if _, err := NewHelm().List(); err == nil {
+ t.Errorf("List expected to fail but it didn't")
}
}
-func TestHelmLists(t *testing.T) {
- names, err := NewHelm().GetNames(helListOutput)
+func TestDeleteSuccess(t *testing.T) {
+ name := "dummy-xapp"
+
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helmStatusOutput
+
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetOut = kubeServiceOutput
+
+ xapp, err := NewHelm().Delete(name)
if err != nil {
- t.Errorf("Helm status failed: %v", err)
+ t.Errorf("Delete failed: %v", err)
+ }
+
+ validateXappModel(t, xapp)
+
+ expectedHelmCommand := "del --purge dummy-xapp"
+ if caughtHelmExecArgs != expectedHelmCommand {
+ t.Errorf("Delete failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
}
+}
+
+func TestDeleteReturnsErrorIfHelmStatusFails(t *testing.T) {
+ name := "dummy-xapp"
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
+
+ if _, err := NewHelm().Delete(name); err == nil {
+ t.Errorf("Delete expected to fail but it didn't")
+ }
+}
+
+func TestFetchSuccessIfCmdArgHasTestSuffix(t *testing.T) {
+ if err := NewHelm().Fetch("kissa", "koira"); err != nil {
+ t.Errorf("Fetch failed: %v", err)
+ }
+}
+
+func TestGetVersionSuccess(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helListOutput
+
+ if version := NewHelm().GetVersion("dummy-xapp"); version != "1.0" {
+ t.Errorf("GetVersion failed: expected 1.0, got %v", version)
+ }
+
+ expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp"
+ if caughtHelmExecArgs != expectedHelmCommand {
+ t.Errorf("GetVersion failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
+ }
+}
+
+func TestGetVersionReturnsEmptyStringIfHelmListFails(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
+
+ if version := NewHelm().GetVersion("dummy-xapp"); version != "" {
+ t.Errorf("GetVersion expected to return empty string, got %v", version)
+ }
+}
+
+func TestGetAddressSuccess(t *testing.T) {
+ ip, port := NewHelm().GetAddress(helmStatusOutput)
+ if ip != "10.102.184.212" {
+ t.Errorf("GetAddress failed: expected 10.102.184.212, got %v", ip)
+ }
+ if port != "80/TCP" {
+ t.Errorf("GetAddress failed: expected 80/TCP, got %v", port)
+ }
+}
+
+func TestGetEndpointInfoSuccess(t *testing.T) {
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetOut = kubeServiceOutput
+
+ svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
+ expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
+ if svc != expectedSvc {
+ t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
+ }
+ if port != 4560 {
+ t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
+ }
+ expectedKubeCommand := " get service -n ricxapp service-ricxapp-dummy-xapp-rmr -o json"
+ if caughtKubeExecArgs != expectedKubeCommand {
+ t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedKubeCommand, caughtKubeExecArgs)
+ }
+}
+
+func TestGetEndpointInfoReturnsDefaultPortIfJsonParseFails(t *testing.T) {
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetOut = "not-json-syntax"
+
+ svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
+ expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
+ if svc != expectedSvc {
+ t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
+ }
+ if port != 4560 {
+ t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
+ }
+}
+
+func TestGetEndpointInfoReturnsDefaultPortIfKubeGetServiceFails(t *testing.T) {
+ defer func() { resetKubeExecMock() }()
+ kubeExec = mockedKubeExec
+ kubeExecRetErr = errors.New("some helm command error")
+
+ svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
+ expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
+ if svc != expectedSvc {
+ t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
+ }
+ if port != 4560 {
+ t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
+ }
+}
+
+func TestHelmStatusAllSuccess(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetOut = helListAllOutput
+
+ if _, err := NewHelm().StatusAll(); err != nil {
+ t.Errorf("StatusAll failed: %v", err)
+ }
+ // Todo: check StatusAll response content
+}
+
+func TestStatusAllReturnsErrorIfHelmListFails(t *testing.T) {
+ defer func() { resetHelmExecMock() }()
+ helmExec = mockedHelmExec
+ helmExecRetErr = errors.New("some helm command error")
+
+ if _, err := NewHelm().StatusAll(); err == nil {
+ t.Errorf("StatusAll expected to fail but it didn't")
+ }
+}
+
+func TestGetNamesSuccess(t *testing.T) {
+ names, err := NewHelm().GetNames(helListAllOutput)
+ if err != nil {
+ t.Errorf("GetNames failed: %v", err)
+ }
if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) {
- t.Errorf("Helm status failed: %v", err)
+ t.Errorf("GetNames failed: %v", err)
}
}
func TestAddTillerEnv(t *testing.T) {
if NewHelm().AddTillerEnv() != nil {
- t.Errorf("TestAddTillerEnv failed!")
+ t.Errorf("AddTillerEnv failed!")
}
}
expectedArgs := "install helm-repo/dummy-xapp --namespace=ricxapp --name=dummy-xapp"
if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
- t.Errorf("TestGetInstallArgs failed: expected %v, got %v", expectedArgs, args)
+ t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
+ }
+
+ expectedArgs += " --set ricapp.appconfig.override=dummy-xapp-appconfig"
+ if args := NewHelm().GetInstallArgs(x, true); args != expectedArgs {
+ t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
}
x.HelmVersion = "1.2.3"
expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=dummy-xapp"
if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
- t.Errorf("TestGetInstallArgs failed: expected %v, got %v", expectedArgs, args)
+ t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
}
x.ReleaseName = "ueec-xapp"
expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=ueec-xapp"
if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
- t.Errorf("TestGetInstallArgs failed: expected %v, got %v", expectedArgs, args)
+ t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
+ }
+
+ x.OverrideFile = "../../test/dummy-xapp_values.json"
+ expectedArgs += " -f=/tmp/appmgr_override.yaml"
+ if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
+ t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
}
}
+func writeTestUsernameFile() error {
+ f, err := os.Create(viper.GetString("helm.helm-username-file"))
+ if err != nil {
+ return err
+ }
+ _, err = f.WriteString("some-username")
+ f.Close()
+ return err
+}
+
+func removeTestUsernameFile() error {
+ return os.Remove(viper.GetString("helm.helm-username-file"))
+}
+
+func writeTestPasswordFile() (err error) {
+ f, err := os.Create(viper.GetString("helm.helm-password-file"))
+ if err != nil {
+ return err
+ }
+
+ _, err = f.WriteString("some-password")
+ f.Close()
+ return err
+}
+
+func removeTestPasswordFile() error {
+ return os.Remove(viper.GetString("helm.helm-password-file"))
+}
+
func getXappData() (x models.Xapp) {
//name1 := "dummy-xapp-8984fc9fd-l6xch"
//name2 := "dummy-xapp-8984fc9fd-pp4hg"
return
}
+
+func mockedKubeExec(args string) (out []byte, err error) {
+ caughtKubeExecArgs = args
+ return []byte(kubeExecRetOut), kubeExecRetErr
+}
+
+func resetKubeExecMock() {
+ kubeExec = util.KubectlExec
+ caughtKubeExecArgs = ""
+ kubeExecRetOut = ""
+ kubeExecRetErr = nil
+}
+
+func mockedHelmExec(args string) (out []byte, err error) {
+ caughtHelmExecArgs = args
+ return []byte(helmExecRetOut), helmExecRetErr
+}
+
+func resetHelmExecMock() {
+ helmExec = util.HelmExec
+ caughtHelmExecArgs = ""
+ helmExecRetOut = ""
+ helmExecRetErr = nil
+}
+
+func validateXappModel(t *testing.T, xapp models.Xapp) {
+ expXapp := getXappData()
+ xapp.Version = "1.0"
+
+ if *expXapp.Name != *xapp.Name || expXapp.Status != xapp.Status || expXapp.Version != xapp.Version {
+ t.Errorf("\n%v \n%v", *xapp.Name, *expXapp.Name)
+ }
+
+ if *expXapp.Instances[0].Name != *xapp.Instances[0].Name || expXapp.Instances[0].Status != xapp.Instances[0].Status {
+ t.Errorf("\n1:%v 2:%v", *expXapp.Instances[0].Name, *xapp.Instances[0].Name)
+ }
+
+ if expXapp.Instances[0].IP != xapp.Instances[0].IP || expXapp.Instances[0].Port != xapp.Instances[0].Port {
+ t.Errorf("\n%v - %v, %v - %v", expXapp.Instances[0].IP, xapp.Instances[0].IP, expXapp.Instances[0].Port, xapp.Instances[0].Port)
+ }
+}
)
func NewResthook(restoreData bool) *Resthook {
+ return createResthook(restoreData, sdl.NewSdlInstance("appmgr", sdl.NewDatabase()))
+}
+
+func createResthook(restoreData bool, sdlInst iSdl) *Resthook {
rh := &Resthook{
client: &http.Client{},
- db: sdl.NewSdlInstance("appmgr", sdl.NewDatabase()),
+ db: sdlInst,
}
if restoreData {
func (rh *Resthook) StoreSubscriptions(m cmap.ConcurrentMap) {
for v := range m.Iter() {
s := v.Val.(SubscriptionInfo)
-
data, err := json.Marshal(s.req)
if err != nil {
appmgr.Logger.Error("json.marshal failed: %v ", err.Error())
package resthooks
import (
+ "encoding/json"
+ "errors"
+ "fmt"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "net"
+ "net/http"
+ "net/http/httptest"
"os"
+ "time"
+
+ "strconv"
"testing"
"gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
var rh *Resthook
var resp models.SubscriptionResponse
+var mockedSdl *SdlMock
// Test cases
func TestMain(m *testing.M) {
appmgr.Init()
appmgr.Logger.SetLevel(0)
- rh = NewResthook(false)
+ mockedSdl = new(SdlMock)
+ rh = createResthook(false, mockedSdl)
code := m.Run()
os.Exit(code)
}
func TestAddSubscriptionSuccess(t *testing.T) {
- resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook"))
+ var mockSdlRetOk error
+ subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+
+ mockedSdl.expectDbSet(t, subsReq, mockSdlRetOk)
+ resp := rh.AddSubscription(subsReq)
assert.Equal(t, resp.Version, int64(0))
assert.Equal(t, resp.EventType, models.EventTypeCreated)
}
func TestAddSubscriptionExists(t *testing.T) {
- resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook"))
+ resp := rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook"))
assert.Equal(t, resp.Version, int64(0))
assert.Equal(t, resp.EventType, models.EventTypeCreated)
}
func TestDeletesubscriptionSuccess(t *testing.T) {
- resp := rh.AddSubscription(CreateSubscription(models.EventTypeDeleted, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
+ var mockSdlRetOk error
+
+ mockedSdl.On("Set", mock.Anything).Return(mockSdlRetOk)
+ resp := rh.AddSubscription(createSubscription(models.EventTypeDeleted, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
assert.Equal(t, resp.Version, int64(0))
assert.Equal(t, resp.EventType, models.EventTypeDeleted)
}
func TestModifySubscriptionSuccess(t *testing.T) {
- resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
+ resp := rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
assert.Equal(t, resp.Version, int64(0))
assert.Equal(t, resp.EventType, models.EventTypeCreated)
- resp, ok := rh.ModifySubscription(resp.ID, CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
+ resp, ok := rh.ModifySubscription(resp.ID, createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
assert.Equal(t, ok, true)
assert.Equal(t, resp.Version, int64(0))
assert.Equal(t, resp.EventType, models.EventTypeModified)
}
-func TestModifysubscriptionInvalid(t *testing.T) {
+func TestModifySubscriptionForNonExistingSubscription(t *testing.T) {
+ resp, ok := rh.ModifySubscription("Non-existent-ID", createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
+ assert.Equal(t, ok, false)
+ assert.Equal(t, *resp, models.SubscriptionResponse{})
+}
+
+func TestDeleteSubscriptionForNonExistingSubscription(t *testing.T) {
resp, ok := rh.DeleteSubscription("Non-existent-ID")
assert.Equal(t, ok, false)
assert.Equal(t, resp.Version, int64(0))
}
func TestGetAllSubscriptionSuccess(t *testing.T) {
- rh.FlushSubscriptions()
+ flushExistingSubscriptions()
subscriptions := rh.GetAllSubscriptions()
assert.Equal(t, len(subscriptions), 0)
- rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook"))
- rh.AddSubscription(CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
+ rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook"))
+ rh.AddSubscription(createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2"))
subscriptions = rh.GetAllSubscriptions()
assert.Equal(t, len(subscriptions), 2)
}
func TestGetSubscriptionByIdSuccess(t *testing.T) {
- rh.FlushSubscriptions()
- sub1 := CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
- sub2 := CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")
+ flushExistingSubscriptions()
+
+ sub1 := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+ sub2 := createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")
r1 := rh.AddSubscription(sub1)
r2 := rh.AddSubscription(sub2)
assert.Equal(t, resp2.Data, sub2.Data)
}
+func TestGetSubscriptionByIdForNonExistingSubscription(t *testing.T) {
+ resp, ok := rh.GetSubscriptionById("Non-existent-ID")
+ assert.Equal(t, ok, false)
+ assert.Equal(t, resp, models.Subscription{})
+}
+
+func TestNotifyClientsNoXapp(t *testing.T) {
+ rh.NotifyClients(models.AllDeployedXapps{}, models.EventTypeUndeployed)
+}
+
+func TestNotifySuccess(t *testing.T) {
+ flushExistingSubscriptions()
+
+ sub := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+ resp := rh.AddSubscription(sub)
+
+ xapp := getDummyXapp()
+ ts := createHTTPServer(t, "POST", "/xapps_hook", 8087, http.StatusOK, nil)
+ defer ts.Close()
+
+ v, ok := rh.subscriptions.Get(resp.ID)
+ assert.True(t, ok)
+ err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1)
+ assert.Nil(t, err)
+}
+
+func TestNotifySuccessIfHttpErrorResponse(t *testing.T) {
+ flushExistingSubscriptions()
+
+ sub := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+ resp := rh.AddSubscription(sub)
+
+ xapp := getDummyXapp()
+ ts := createHTTPServer(t, "POST", "/xapps_hook", 8087, http.StatusInternalServerError, nil)
+ defer ts.Close()
+
+ v, ok := rh.subscriptions.Get(resp.ID)
+ assert.True(t, ok)
+ err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1)
+ assert.Nil(t, err)
+}
+
+func TestNotifyReturnsErrorAfterRetriesIfNoHttpServer(t *testing.T) {
+ flushExistingSubscriptions()
+
+ sub := createSubscription(models.EventTypeCreated, int64(2), int64(1), "http://localhost:8087/xapps_hook")
+ resp := rh.AddSubscription(sub)
+
+ xapp := getDummyXapp()
+
+ v, ok := rh.subscriptions.Get(resp.ID)
+ assert.True(t, ok)
+ err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1)
+ assert.NotNil(t, err)
+ assert.Equal(t, 0, len(rh.subscriptions.Items()))
+}
+
+func TestRestoreSubscriptionsSuccess(t *testing.T) {
+ var mockSdlRetOk error
+ mSdl := new(SdlMock)
+ key := "key-1"
+
+ subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+ serializedSubsReq, err := json.Marshal(subsReq)
+ assert.Nil(t, err)
+
+ mockSdlGetRetVal := make(map[string]interface{})
+ //Cast data to string to act like a real SDL/Redis client
+ mockSdlGetRetVal[key] = string(serializedSubsReq)
+ mSdl.On("GetAll").Return([]string{key}, mockSdlRetOk).Twice()
+ mSdl.On("Get", []string{key}).Return(mockSdlGetRetVal, mockSdlRetOk).Once()
+ restHook := createResthook(true, mSdl)
+
+ val, found := restHook.subscriptions.Get(key)
+ assert.True(t, found)
+ assert.Equal(t, subsReq, val.(SubscriptionInfo).req)
+}
+
+func TestRestoreSubscriptionsFailsIfSdlGetAllFails(t *testing.T) {
+ var mockSdlRetStatus error
+ mSdl := new(SdlMock)
+ getCalled := 0
+ mGetAllCall := mSdl.On("GetAll")
+ mGetAllCall.RunFn = func(args mock.Arguments) {
+ if getCalled > 0 {
+ mockSdlRetStatus = errors.New("some SDL error")
+ }
+ getCalled++
+ mGetAllCall.ReturnArguments = mock.Arguments{[]string{}, mockSdlRetStatus}
+ }
+
+ restHook := createResthook(true, mSdl)
+ assert.Equal(t, 0, len(restHook.subscriptions.Items()))
+}
+
+func TestRestoreSubscriptionsFailsIfSdlGetFails(t *testing.T) {
+ var mockSdlRetOk error
+ mSdl := new(SdlMock)
+ mockSdlRetNok := errors.New("some SDL error")
+ key := "key-1"
+ subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")
+ serializedSubsReq, err := json.Marshal(subsReq)
+ assert.Nil(t, err)
+
+ mockSdlGetRetVal := make(map[string]interface{})
+ mockSdlGetRetVal[key] = serializedSubsReq
+
+ mSdl.On("GetAll").Return([]string{key}, mockSdlRetOk).Twice()
+ mSdl.On("Get", []string{key}).Return(mockSdlGetRetVal, mockSdlRetNok).Once()
+
+ restHook := createResthook(true, mSdl)
+ assert.Equal(t, 0, len(restHook.subscriptions.Items()))
+}
+
func TestTeardown(t *testing.T) {
+ var mockSdlRetOk error
+ mockedSdl.On("RemoveAll").Return(mockSdlRetOk).Once()
+
rh.FlushSubscriptions()
}
-func CreateSubscription(et models.EventType, maxRetries, retryTimer int64, targetUrl string) models.SubscriptionRequest {
+func createSubscription(et models.EventType, maxRetries, retryTimer int64, targetUrl string) models.SubscriptionRequest {
return models.SubscriptionRequest{&models.SubscriptionData{et, &maxRetries, &retryTimer, &targetUrl}}
}
+
+func getDummyXapp() models.Xapp {
+ return generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "service-ricxapp-dummy-xapp-rmr.ricxapp", "4560")
+}
+
+func generateXapp(name, status, ver, iname, istatus, ip, port string) (x models.Xapp) {
+ x.Name = &name
+ x.Status = status
+ x.Version = ver
+ p, _ := strconv.Atoi(port)
+ var msgs appmgr.RtmData
+
+ instance := &models.XappInstance{
+ Name: &iname,
+ Status: istatus,
+ IP: ip,
+ Port: int64(p),
+ TxMessages: msgs.TxMessages,
+ RxMessages: msgs.RxMessages,
+ }
+ x.Instances = append(x.Instances, instance)
+ return
+}
+
+func flushExistingSubscriptions() {
+ var mockSdlRetOk error
+ mockedSdl.On("RemoveAll").Return(mockSdlRetOk).Once()
+ rh.FlushSubscriptions()
+}
+
+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()
+ time.Sleep(time.Duration(1 * time.Second))
+ return ts
+}
+
+func (m *SdlMock) expectDbSet(t *testing.T, subsReq models.SubscriptionRequest, mockRet error) {
+ serializedSubReq, _ := json.Marshal(subsReq)
+ m.On("Set", mock.Anything).Run(
+ func(args mock.Arguments) {
+ sdlKVs := args.Get(0).([]interface{})
+ assert.Equal(t, 2, len(sdlKVs))
+ //Validate that subscription request is set to SDL
+ assert.Equal(t, serializedSubReq, sdlKVs[1])
+ }).Return(mockRet).Once()
+}
+
+type SdlMock struct {
+ mock.Mock
+}
+
+func (m *SdlMock) Set(pairs ...interface{}) error {
+ a := m.Called(pairs)
+ return a.Error(0)
+}
+
+func (m *SdlMock) Get(keys []string) (map[string]interface{}, error) {
+ a := m.Called(keys)
+ return a.Get(0).(map[string]interface{}), a.Error(1)
+}
+
+func (m *SdlMock) GetAll() ([]string, error) {
+ a := m.Called()
+ return a.Get(0).([]string), a.Error(1)
+}
+
+func (m *SdlMock) RemoveAll() error {
+ a := m.Called()
+ return a.Error(0)
+}
package resthooks
import (
- sdl "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
cmap "github.com/orcaman/concurrent-map"
"net/http"
type Resthook struct {
client *http.Client
subscriptions cmap.ConcurrentMap
- db *sdl.SdlInstance
+ db iSdl
Seq int64
}
Version int64 `json:"version,omitempty"`
XApps string `json:"xApps,omitempty"`
}
+
+type iSdl interface {
+ Set(pairs ...interface{}) error
+ Get(keys []string) (map[string]interface{}, error)
+ GetAll() ([]string, error)
+ RemoveAll() error
+}
--- /dev/null
+{
+ "XappName": "dummy-xapp",
+ "ReleaseName": "dummy-xapp",
+ "HelmVersion": "1.0.0",
+ "Namespace": "ricxapp"
+}