Create UTs to cover DB calls 90/71090/2
authorVictor Morales <victor.morales@intel.com>
Tue, 23 Oct 2018 18:54:58 +0000 (11:54 -0700)
committerVictor Morales <victor.morales@intel.com>
Wed, 14 Nov 2018 00:20:57 +0000 (16:20 -0800)
This change pretends to increase the code coverage creating Unit
Tests for the interactions with the Databases.

Change-Id: I3b78ebe8ddb131e3c06bcee0065ad5eabeed5677
Signed-off-by: Victor Morales <victor.morales@intel.com>
Issue-ID: MULTICLOUD-301

src/k8splugin/api/api.go
src/k8splugin/api/handler.go
src/k8splugin/api/handler_test.go
src/k8splugin/db/consul.go
src/k8splugin/db/consul_test.go [new file with mode: 0644]
src/k8splugin/db/db_test.go [deleted file]
src/k8splugin/db/store.go [moved from src/k8splugin/db/DB.go with 79% similarity]
src/k8splugin/db/store_test.go [new file with mode: 0644]
src/k8splugin/db/testing.go [new file with mode: 0644]
src/k8splugin/vnfd/vnfd.go
src/k8splugin/vnfd/vnfd_test.go

index e90c994..f05fbb0 100644 (file)
@@ -47,12 +47,7 @@ func CheckDatabaseConnection() error {
                return pkgerrors.Cause(err)
        }
 
-       err = db.DBconn.InitializeDatabase()
-       if err != nil {
-               return pkgerrors.Cause(err)
-       }
-
-       err = db.DBconn.CheckDatabase()
+       err = db.DBconn.HealthCheck()
        if err != nil {
                return pkgerrors.Cause(err)
        }
index b4828b7..53fa231 100644 (file)
@@ -127,7 +127,7 @@ func CreateHandler(w http.ResponseWriter, r *http.Request) {
 
        // key: cloud1-default-uuid
        // value: "{"deployment":<>,"service":<>}"
-       err = db.DBconn.CreateEntry(internalVNFID, serializedResourceNameMap)
+       err = db.DBconn.Create(internalVNFID, serializedResourceNameMap)
        if err != nil {
                werr := pkgerrors.Wrap(err, "Create VNF deployment DB error")
                http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -156,8 +156,8 @@ func ListHandler(w http.ResponseWriter, r *http.Request) {
 
        internalVNFIDs, err := db.DBconn.ReadAll(prefix)
        if err != nil {
-               werr := pkgerrors.Wrap(err, "Get VNF list error")
-               http.Error(w, werr.Error(), http.StatusInternalServerError)
+               http.Error(w, pkgerrors.Wrap(err, "Get VNF list error").Error(),
+                       http.StatusInternalServerError)
                return
        }
 
@@ -202,24 +202,15 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
        // cloud1-default-uuid
        internalVNFID := cloudRegionID + "-" + namespace + "-" + externalVNFID
 
-       // (TODO): Read kubeconfig for specific Cloud Region from local file system
-       // if present or download it from AAI
-       // err := DownloadKubeConfigFromAAI(resource.CloudRegionID, os.Getenv("KUBE_CONFIG_DIR")
-       kubeclient, err := GetVNFClient(os.Getenv("KUBE_CONFIG_DIR") + "/" + cloudRegionID)
-       if err != nil {
-               http.Error(w, err.Error(), http.StatusInternalServerError)
-               return
-       }
-
        // key: cloud1-default-uuid
        // value: "{"deployment":<>,"service":<>}"
-       serializedResourceNameMap, found, err := db.DBconn.ReadEntry(internalVNFID)
+       serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
 
-       if found == false {
+       if serializedResourceNameMap == "" {
                w.WriteHeader(http.StatusNotFound)
                return
        }
@@ -233,11 +224,19 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
        deserializedResourceNameMap := make(map[string][]string)
        err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
        if err != nil {
-               werr := pkgerrors.Wrap(err, "Delete VNF error")
+               werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
                http.Error(w, werr.Error(), http.StatusInternalServerError)
                return
        }
 
+       // (TODO): Read kubeconfig for specific Cloud Region from local file system
+       // if present or download it from AAI
+       // err := DownloadKubeConfigFromAAI(resource.CloudRegionID, os.Getenv("KUBE_CONFIG_DIR")
+       kubeclient, err := GetVNFClient(os.Getenv("KUBE_CONFIG_DIR") + "/" + cloudRegionID)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
        err = csar.DestroyVNF(deserializedResourceNameMap, namespace, &kubeclient)
        if err != nil {
                werr := pkgerrors.Wrap(err, "Delete VNF error")
@@ -245,9 +244,9 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       err = db.DBconn.DeleteEntry(internalVNFID)
+       err = db.DBconn.Delete(internalVNFID)
        if err != nil {
-               werr := pkgerrors.Wrap(err, "Delete VNF error")
+               werr := pkgerrors.Wrap(err, "Delete VNF db record error")
                http.Error(w, werr.Error(), http.StatusInternalServerError)
                return
        }
@@ -338,13 +337,13 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
 
        // key: cloud1-default-uuid
        // value: "{"deployment":<>,"service":<>}"
-       serializedResourceNameMap, found, err := db.DBconn.ReadEntry(internalVNFID)
+       serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
 
-       if found == false {
+       if serializedResourceNameMap == "" {
                w.WriteHeader(http.StatusNotFound)
                return
        }
@@ -358,7 +357,7 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
        deserializedResourceNameMap := make(map[string][]string)
        err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
        if err != nil {
-               werr := pkgerrors.Wrap(err, "Get VNF error")
+               werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
                http.Error(w, werr.Error(), http.StatusInternalServerError)
                return
        }
index ac97d01..3336bbc 100644 (file)
@@ -18,48 +18,34 @@ package api
 import (
        "bytes"
        "encoding/json"
+       "io"
        "net/http"
        "net/http/httptest"
        "reflect"
        "testing"
 
+       "github.com/hashicorp/consul/api"
+       pkgerrors "github.com/pkg/errors"
        "k8s.io/client-go/kubernetes"
 
        "k8splugin/csar"
        "k8splugin/db"
 )
 
-//Creating an embedded interface via anonymous variable
-//This allows us to make mockDB satisfy the DatabaseConnection
-//interface even if we are not implementing all the methods in it
-type mockDB struct {
-       db.DatabaseConnection
+type mockCSAR struct {
+       externalVNFID       string
+       resourceYAMLNameMap map[string][]string
+       err                 error
 }
 
-func (c *mockDB) InitializeDatabase() error {
-       return nil
+func (c *mockCSAR) CreateVNF(id, r, n string,
+       kubeclient *kubernetes.Clientset) (string, map[string][]string, error) {
+       return c.externalVNFID, c.resourceYAMLNameMap, c.err
 }
 
-func (c *mockDB) CheckDatabase() error {
-       return nil
-}
-
-func (c *mockDB) CreateEntry(key string, value string) error {
-       return nil
-}
-
-func (c *mockDB) ReadEntry(key string) (string, bool, error) {
-       str := "{\"deployment\":[\"cloud1-default-uuid-sisedeploy\"],\"service\":[\"cloud1-default-uuid-sisesvc\"]}"
-       return str, true, nil
-}
-
-func (c *mockDB) DeleteEntry(key string) error {
-       return nil
-}
-
-func (c *mockDB) ReadAll(key string) ([]string, error) {
-       returnVal := []string{"cloud1-default-uuid1", "cloud1-default-uuid2"}
-       return returnVal, nil
+func (c *mockCSAR) DestroyVNF(data map[string][]string, namespace string,
+       kubeclient *kubernetes.Clientset) error {
+       return c.err
 }
 
 func executeRequest(req *http.Request) *httptest.ResponseRecorder {
@@ -76,146 +62,308 @@ func checkResponseCode(t *testing.T, expected, actual int) {
        }
 }
 
-func TestVNFInstanceCreation(t *testing.T) {
-       t.Run("Succesful create a VNF", func(t *testing.T) {
-               payload := []byte(`{
-                       "cloud_region_id": "region1",
-                       "namespace": "test",
-                       "csar_id": "UUID-1",
-                       "oof_parameters": [{
-                               "key1": "value1",
-                               "key2": "value2",
-                               "key3": {}
-                       }],
-                       "network_parameters": {
-                               "oam_ip_address": {
-                                       "connection_point": "string",
-                                       "ip_address": "string",
-                                       "workload_name": "string"
-                               }
-                       }
-               }`)
-
-               data := map[string][]string{
-                       "deployment": []string{"cloud1-default-uuid-sisedeploy"},
-                       "service":    []string{"cloud1-default-uuid-sisesvc"},
-               }
-
-               expected := &CreateVnfResponse{
-                       VNFID:         "test_UUID",
-                       CloudRegionID: "region1",
-                       Namespace:     "test",
-                       VNFComponents: data,
-               }
-
-               var result CreateVnfResponse
-
-               req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
-
-               GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
-                       return kubernetes.Clientset{}, nil
-               }
-
-               csar.CreateVNF = func(id string, r string, n string, kubeclient *kubernetes.Clientset) (string, map[string][]string, error) {
-                       return "externaluuid", data, nil
-               }
-
-               db.DBconn = &mockDB{}
+func TestCreateHandler(t *testing.T) {
+       testCases := []struct {
+               label               string
+               input               io.Reader
+               expectedCode        int
+               mockGetVNFClientErr error
+               mockCreateVNF       *mockCSAR
+               mockStore           *db.MockDB
+       }{
+               {
+                       label:        "Missing body failure",
+                       expectedCode: http.StatusBadRequest,
+               },
+               {
+                       label:        "Invalid JSON request format",
+                       input:        bytes.NewBuffer([]byte("invalid")),
+                       expectedCode: http.StatusUnprocessableEntity,
+               },
+               {
+                       label: "Missing parameter failure",
+                       input: bytes.NewBuffer([]byte(`{
+                               "csar_id": "testID",
+                               "oof_parameters": {
+                                       "key_values": {
+                                               "key1": "value1",
+                                               "key2": "value2"
+                                       }
+                               },
+                               "vnf_instance_name": "test",
+                               "vnf_instance_description": "vRouter_test_description"
+                       }`)),
+                       expectedCode: http.StatusUnprocessableEntity,
+               },
+               {
+                       label: "Fail to get the VNF client",
+                       input: bytes.NewBuffer([]byte(`{
+                               "cloud_region_id": "region1",
+                               "namespace": "test",
+                               "csar_id": "UUID-1"
+                       }`)),
+                       expectedCode:        http.StatusInternalServerError,
+                       mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
+               },
+               {
+                       label: "Fail to create the VNF instance",
+                       input: bytes.NewBuffer([]byte(`{
+                               "cloud_region_id": "region1",
+                               "namespace": "test",
+                               "csar_id": "UUID-1"
+                       }`)),
+                       expectedCode: http.StatusInternalServerError,
+                       mockCreateVNF: &mockCSAR{
+                               err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label: "Fail to create a VNF DB record",
+                       input: bytes.NewBuffer([]byte(`{
+                               "cloud_region_id": "region1",
+                               "namespace": "test",
+                               "csar_id": "UUID-1"
+                       }`)),
+                       expectedCode: http.StatusInternalServerError,
+                       mockCreateVNF: &mockCSAR{
+                               resourceYAMLNameMap: map[string][]string{},
+                       },
+                       mockStore: &db.MockDB{
+                               Err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label: "Succesful create a VNF",
+                       input: bytes.NewBuffer([]byte(`{
+                               "cloud_region_id": "region1",
+                               "namespace": "test",
+                               "csar_id": "UUID-1"
+                       }`)),
+                       expectedCode: http.StatusCreated,
+                       mockCreateVNF: &mockCSAR{
+                               resourceYAMLNameMap: map[string][]string{
+                                       "deployment": []string{"cloud1-default-uuid-sisedeploy"},
+                                       "service":    []string{"cloud1-default-uuid-sisesvc"},
+                               },
+                       },
+                       mockStore: &db.MockDB{},
+               },
+       }
 
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusCreated, response.Code)
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
+                               return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
+                       }
+                       if testCase.mockCreateVNF != nil {
+                               csar.CreateVNF = testCase.mockCreateVNF.CreateVNF
+                       }
+                       if testCase.mockStore != nil {
+                               db.DBconn = testCase.mockStore
+                       }
 
-               err := json.NewDecoder(response.Body).Decode(&result)
-               if err != nil {
-                       t.Fatalf("TestVNFInstanceCreation returned:\n result=%v\n expected=%v", err, expected.VNFComponents)
-               }
-       })
-       t.Run("Missing body failure", func(t *testing.T) {
-               req, _ := http.NewRequest("POST", "/v1/vnf_instances/", nil)
-               response := executeRequest(req)
+                       request, _ := http.NewRequest("POST", "/v1/vnf_instances/", testCase.input)
+                       result := executeRequest(request)
 
-               checkResponseCode(t, http.StatusBadRequest, response.Code)
-       })
-       t.Run("Invalid JSON request format", func(t *testing.T) {
-               payload := []byte("invalid")
-               req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusUnprocessableEntity, response.Code)
-       })
-       t.Run("Missing parameter failure", func(t *testing.T) {
-               payload := []byte(`{
-                       "csar_id": "testID",
-                       "oof_parameters": {
-                               "key_values": {
-                                       "key1": "value1",
-                                       "key2": "value2"
+                       if testCase.expectedCode != result.Code {
+                               t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v", result.Code, testCase.expectedCode)
+                       }
+                       if result.Code == http.StatusCreated {
+                               var response CreateVnfResponse
+                               err := json.NewDecoder(result.Body).Decode(&response)
+                               if err != nil {
+                                       t.Fatalf("Parsing the returned response got an error (%s)", err)
                                }
-                       },
-                       "vnf_instance_name": "test",
-                       "vnf_instance_description": "vRouter_test_description"
-               }`)
-               req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusUnprocessableEntity, response.Code)
-       })
+                       }
+               })
+       }
 }
 
-func TestVNFInstancesRetrieval(t *testing.T) {
-       t.Run("Succesful get a list of VNF", func(t *testing.T) {
-               expected := &ListVnfsResponse{
-                       VNFs: []string{"uuid1", "uuid2"},
-               }
-               var result ListVnfsResponse
-
-               req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default", nil)
+func TestListHandler(t *testing.T) {
+       testCases := []struct {
+               label            string
+               expectedCode     int
+               expectedResponse []string
+               mockStore        *db.MockDB
+       }{
+               {
+                       label:        "Fail to retrieve DB records",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label:        "Get result from DB non-records",
+                       expectedCode: http.StatusNotFound,
+                       mockStore:    &db.MockDB{},
+               },
+               {
+                       label:            "Get empty list",
+                       expectedCode:     http.StatusOK,
+                       expectedResponse: []string{""},
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "",
+                                               Value: []byte("{}"),
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:            "Succesful get a list of VNF",
+                       expectedCode:     http.StatusOK,
+                       expectedResponse: []string{"uid1", "uid2"},
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "uuid1",
+                                               Value: []byte("{}"),
+                                       },
+                                       &api.KVPair{
+                                               Key:   "uuid2",
+                                               Value: []byte("{}"),
+                                       },
+                               },
+                       },
+               },
+       }
 
-               db.DBconn = &mockDB{}
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       if testCase.mockStore != nil {
+                               db.DBconn = testCase.mockStore
+                       }
 
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusOK, response.Code)
+                       request, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default", nil)
+                       result := executeRequest(request)
 
-               err := json.NewDecoder(response.Body).Decode(&result)
-               if err != nil {
-                       t.Fatalf("TestVNFInstancesRetrieval returned:\n result=%v\n expected=list", err)
-               }
-               if !reflect.DeepEqual(*expected, result) {
-                       t.Fatalf("TestVNFInstancesRetrieval returned:\n result=%v\n expected=%v", result, *expected)
-               }
-       })
-       t.Run("Get empty list", func(t *testing.T) {
-               req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloudregion1/testnamespace", nil)
-               db.DBconn = &mockDB{}
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusOK, response.Code)
-       })
+                       if testCase.expectedCode != result.Code {
+                               t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v",
+                                       result.Code, testCase.expectedCode)
+                       }
+                       if result.Code == http.StatusOK {
+                               var response ListVnfsResponse
+                               err := json.NewDecoder(result.Body).Decode(&response)
+                               if err != nil {
+                                       t.Fatalf("Parsing the returned response got an error (%s)", err)
+                               }
+                               if !reflect.DeepEqual(testCase.expectedResponse, response.VNFs) {
+                                       t.Fatalf("TestListHandler returned:\n result=%v\n expected=%v",
+                                               response.VNFs, testCase.expectedResponse)
+                               }
+                       }
+               })
+       }
 }
 
-func TestVNFInstanceDeletion(t *testing.T) {
-       t.Run("Succesful delete a VNF", func(t *testing.T) {
-               req, _ := http.NewRequest("DELETE", "/v1/vnf_instances/cloudregion1/testnamespace/1", nil)
-
-               GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
-                       return kubernetes.Clientset{}, nil
-               }
-
-               csar.DestroyVNF = func(d map[string][]string, n string, kubeclient *kubernetes.Clientset) error {
-                       return nil
-               }
+func TestDeleteHandler(t *testing.T) {
+       testCases := []struct {
+               label               string
+               expectedCode        int
+               mockGetVNFClientErr error
+               mockDeleteVNF       *mockCSAR
+               mockStore           *db.MockDB
+       }{
+               {
+                       label:        "Fail to read a VNF DB record",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label:        "Fail to find VNF record be deleted",
+                       expectedCode: http.StatusNotFound,
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{},
+                       },
+               },
+               {
+                       label:        "Fail to unmarshal the DB record",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "cloudregion1-testnamespace-uuid1",
+                                               Value: []byte("{invalid format}"),
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:               "Fail to get the VNF client",
+                       expectedCode:        http.StatusInternalServerError,
+                       mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key: "cloudregion1-testnamespace-uuid1",
+                                               Value: []byte("{" +
+                                                       "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+                                                       "\"service\": [\"svc1\", \"svc2\"]" +
+                                                       "}"),
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Fail to destroy VNF",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key: "cloudregion1-testnamespace-uuid1",
+                                               Value: []byte("{" +
+                                                       "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+                                                       "\"service\": [\"svc1\", \"svc2\"]" +
+                                                       "}"),
+                                       },
+                               },
+                       },
+                       mockDeleteVNF: &mockCSAR{
+                               err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label:        "Succesful delete a VNF",
+                       expectedCode: http.StatusAccepted,
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key: "cloudregion1-testnamespace-uuid1",
+                                               Value: []byte("{" +
+                                                       "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+                                                       "\"service\": [\"svc1\", \"svc2\"]" +
+                                                       "}"),
+                                       },
+                               },
+                       },
+                       mockDeleteVNF: &mockCSAR{},
+               },
+       }
 
-               db.DBconn = &mockDB{}
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
+                               return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
+                       }
+                       if testCase.mockStore != nil {
+                               db.DBconn = testCase.mockStore
+                       }
+                       if testCase.mockDeleteVNF != nil {
+                               csar.DestroyVNF = testCase.mockDeleteVNF.DestroyVNF
+                       }
 
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusAccepted, response.Code)
+                       request, _ := http.NewRequest("DELETE", "/v1/vnf_instances/cloudregion1/testnamespace/uuid1", nil)
+                       result := executeRequest(request)
 
-               if result := response.Body.String(); result != "" {
-                       t.Fatalf("TestVNFInstanceDeletion returned:\n result=%v\n expected=%v", result, "")
-               }
-       })
-       // t.Run("Malformed delete request", func(t *testing.T) {
-       //      req, _ := http.NewRequest("DELETE", "/v1/vnf_instances/foo", nil)
-       //      response := executeRqequest(req)
-       //      checkResponseCode(t, http.StatusBadRequest, response.Code)
-       // })
+                       if testCase.expectedCode != result.Code {
+                               t.Fatalf("Request method returned: %v and it was expected: %v", result.Code, testCase.expectedCode)
+                       }
+               })
+       }
 }
 
 // TODO: Update this test when the UpdateVNF endpoint is fixed.
@@ -276,47 +424,88 @@ func TestVNFInstanceUpdate(t *testing.T) {
 }
 */
 
-func TestVNFInstanceRetrieval(t *testing.T) {
-       t.Run("Succesful get a VNF", func(t *testing.T) {
-
-               data := map[string][]string{
-                       "deployment": []string{"cloud1-default-uuid-sisedeploy"},
-                       "service":    []string{"cloud1-default-uuid-sisesvc"},
-               }
-
-               expected := GetVnfResponse{
-                       VNFID:         "1",
-                       CloudRegionID: "cloud1",
-                       Namespace:     "default",
-                       VNFComponents: data,
-               }
-
-               req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default/1", nil)
-
-               GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
-                       return kubernetes.Clientset{}, nil
-               }
-
-               db.DBconn = &mockDB{}
-
-               response := executeRequest(req)
-               checkResponseCode(t, http.StatusOK, response.Code)
-
-               var result GetVnfResponse
-
-               err := json.NewDecoder(response.Body).Decode(&result)
-               if err != nil {
-                       t.Fatalf("TestVNFInstanceRetrieval returned:\n result=%v\n expected=%v", err, expected)
-               }
+func TestGetHandler(t *testing.T) {
+       testCases := []struct {
+               label            string
+               expectedCode     int
+               expectedResponse *GetVnfResponse
+               mockStore        *db.MockDB
+       }{
+               {
+                       label:        "Fail to retrieve DB record",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Err: pkgerrors.New("Internal error"),
+                       },
+               },
+               {
+                       label:        "Not found DB record",
+                       expectedCode: http.StatusNotFound,
+                       mockStore:    &db.MockDB{},
+               },
+               {
+                       label:        "Fail to unmarshal the DB record",
+                       expectedCode: http.StatusInternalServerError,
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "cloud1-default-1",
+                                               Value: []byte("{invalid-format}"),
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Succesful get a list of VNF",
+                       expectedCode: http.StatusOK,
+                       expectedResponse: &GetVnfResponse{
+                               VNFID:         "1",
+                               CloudRegionID: "cloud1",
+                               Namespace:     "default",
+                               VNFComponents: map[string][]string{
+                                       "deployment": []string{"deploy1", "deploy2"},
+                                       "service":    []string{"svc1", "svc2"},
+                               },
+                       },
+                       mockStore: &db.MockDB{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key: "cloud1-default-1",
+                                               Value: []byte("{" +
+                                                       "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+                                                       "\"service\": [\"svc1\", \"svc2\"]" +
+                                                       "}"),
+                                       },
+                                       &api.KVPair{
+                                               Key:   "cloud1-default-2",
+                                               Value: []byte("{}"),
+                                       },
+                               },
+                       },
+               },
+       }
 
-               if !reflect.DeepEqual(expected, result) {
-                       t.Fatalf("TestVNFInstanceRetrieval returned:\n result=%v\n expected=%v", result, expected)
-               }
-       })
-       t.Run("VNF not found", func(t *testing.T) {
-               req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloudregion1/testnamespace/1", nil)
-               response := executeRequest(req)
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       db.DBconn = testCase.mockStore
+                       request, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default/1", nil)
+                       result := executeRequest(request)
 
-               checkResponseCode(t, http.StatusOK, response.Code)
-       })
+                       if testCase.expectedCode != result.Code {
+                               t.Fatalf("Request method returned: %v and it was expected: %v",
+                                       result.Code, testCase.expectedCode)
+                       }
+                       if result.Code == http.StatusOK {
+                               var response GetVnfResponse
+                               err := json.NewDecoder(result.Body).Decode(&response)
+                               if err != nil {
+                                       t.Fatalf("Parsing the returned response got an error (%s)", err)
+                               }
+                               if !reflect.DeepEqual(testCase.expectedResponse, &response) {
+                                       t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
+                                               &response, testCase.expectedResponse)
+                               }
+                       }
+               })
+       }
 }
index 950eea3..d750724 100644 (file)
@@ -16,95 +16,89 @@ package db
 import (
        "os"
 
-       consulapi "github.com/hashicorp/consul/api"
+       "github.com/hashicorp/consul/api"
        pkgerrors "github.com/pkg/errors"
 )
 
-// ConsulDB is an implementation of the DatabaseConnection interface
-type ConsulDB struct {
-       consulClient *consulapi.Client
+// ConsulKVStore defines the a subset of Consul DB operations
+// Note: This interface is defined mainly for allowing mock testing
+type ConsulKVStore interface {
+       List(prefix string, q *api.QueryOptions) (api.KVPairs, *api.QueryMeta, error)
+       Get(key string, q *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error)
+       Put(p *api.KVPair, q *api.WriteOptions) (*api.WriteMeta, error)
+       Delete(key string, w *api.WriteOptions) (*api.WriteMeta, error)
 }
 
-// InitializeDatabase initialized the initial steps
-func (c *ConsulDB) InitializeDatabase() error {
-       config := consulapi.DefaultConfig()
-       config.Address = os.Getenv("DATABASE_IP") + ":8500"
+// ConsulStore is an implementation of the ConsulKVStore interface
+type ConsulStore struct {
+       client ConsulKVStore
+}
 
-       client, err := consulapi.NewClient(config)
-       if err != nil {
-               return err
+// NewConsulStore initializes a Consul Store instance using the default values
+func NewConsulStore(store ConsulKVStore) (Store, error) {
+       if store == nil {
+               config := api.DefaultConfig()
+               config.Address = os.Getenv("DATABASE_IP") + ":8500"
+
+               consulClient, err := api.NewClient(config)
+               if err != nil {
+                       return nil, err
+               }
+               store = consulClient.KV()
        }
-       c.consulClient = client
-       return nil
+
+       return &ConsulStore{
+               client: store,
+       }, nil
 }
 
-// CheckDatabase checks if the database is running
-func (c *ConsulDB) CheckDatabase() error {
-       kv := c.consulClient.KV()
-       _, _, err := kv.Get("test", nil)
+// HealthCheck verifies if the database is up and running
+func (c *ConsulStore) HealthCheck() error {
+       _, err := c.Read("test")
        if err != nil {
                return pkgerrors.New("[ERROR] Cannot talk to Datastore. Check if it is running/reachable.")
        }
        return nil
 }
 
-// CreateEntry is used to create a DB entry
-func (c *ConsulDB) CreateEntry(key string, value string) error {
-       kv := c.consulClient.KV()
-
-       p := &consulapi.KVPair{Key: key, Value: []byte(value)}
-
-       _, err := kv.Put(p, nil)
-
-       if err != nil {
-               return err
+// Create is used to create a DB entry
+func (c *ConsulStore) Create(key, value string) error {
+       p := &api.KVPair{
+               Key:   key,
+               Value: []byte(value),
        }
-
-       return nil
+       _, err := c.client.Put(p, nil)
+       return err
 }
 
-// ReadEntry returns the internalID for a particular externalID is present in a namespace
-func (c *ConsulDB) ReadEntry(key string) (string, bool, error) {
-
-       kv := c.consulClient.KV()
-
-       pair, _, err := kv.Get(key, nil)
-
+// Read method returns the internalID for a particular externalID
+func (c *ConsulStore) Read(key string) (string, error) {
+       pair, _, err := c.client.Get(key, nil)
+       if err != nil {
+               return "", err
+       }
        if pair == nil {
-               return string("No value found for ID: " + key), false, err
+               return "", nil
        }
-       return string(pair.Value), true, err
+       return string(pair.Value), nil
 }
 
-// DeleteEntry is used to delete an ID
-func (c *ConsulDB) DeleteEntry(key string) error {
-
-       kv := c.consulClient.KV()
-
-       _, err := kv.Delete(key, nil)
-
-       if err != nil {
-               return err
-       }
-
-       return nil
+// Delete method removes an internalID from the Database
+func (c *ConsulStore) Delete(key string) error {
+       _, err := c.client.Delete(key, nil)
+       return err
 }
 
 // ReadAll is used to get all ExternalIDs in a namespace
-func (c *ConsulDB) ReadAll(prefix string) ([]string, error) {
-       kv := c.consulClient.KV()
-
-       pairs, _, err := kv.List(prefix, nil)
-
-       if len(pairs) == 0 {
-               return []string{""}, err
+func (c *ConsulStore) ReadAll(prefix string) ([]string, error) {
+       pairs, _, err := c.client.List(prefix, nil)
+       if err != nil {
+               return nil, err
        }
-
-       var res []string
-
+       var result []string
        for _, keypair := range pairs {
-               res = append(res, keypair.Key)
+               result = append(result, keypair.Key)
        }
 
-       return res, err
+       return result, nil
 }
diff --git a/src/k8splugin/db/consul_test.go b/src/k8splugin/db/consul_test.go
new file mode 100644 (file)
index 0000000..ede1a5e
--- /dev/null
@@ -0,0 +1,298 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+       "reflect"
+       "strings"
+       "testing"
+
+       "github.com/hashicorp/consul/api"
+       pkgerrors "github.com/pkg/errors"
+)
+
+type mockConsulKVStore struct {
+       Items api.KVPairs
+       Err   error
+}
+
+func (c *mockConsulKVStore) Put(p *api.KVPair, q *api.WriteOptions) (*api.WriteMeta, error) {
+       return nil, c.Err
+}
+
+func (c *mockConsulKVStore) Get(key string, q *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error) {
+       if c.Err != nil {
+               return nil, nil, c.Err
+       }
+       for _, kvpair := range c.Items {
+               if kvpair.Key == key {
+                       return kvpair, nil, nil
+               }
+       }
+       return nil, nil, nil
+}
+
+func (c *mockConsulKVStore) Delete(key string, w *api.WriteOptions) (*api.WriteMeta, error) {
+       return nil, c.Err
+}
+
+func (c *mockConsulKVStore) List(prefix string, q *api.QueryOptions) (api.KVPairs, *api.QueryMeta, error) {
+       if c.Err != nil {
+               return nil, nil, c.Err
+       }
+       return c.Items, nil, nil
+}
+
+func TestConsulHealthCheck(t *testing.T) {
+       testCases := []struct {
+               label         string
+               mock          *mockConsulKVStore
+               expectedError string
+       }{
+               {
+                       label: "Sucessful health check Consul Database",
+                       mock: &mockConsulKVStore{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "test-key",
+                                               Value: nil,
+                                       },
+                               },
+                       },
+               },
+               {
+                       label: "Fail connectivity to Consul Database",
+                       mock: &mockConsulKVStore{
+                               Err: pkgerrors.New("Timeout"),
+                       },
+                       expectedError: "Cannot talk to Datastore. Check if it is running/reachable.",
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       client, _ := NewConsulStore(testCase.mock)
+                       err := client.HealthCheck()
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("HealthCheck method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.expectedError) {
+                                       t.Fatalf("HealthCheck method returned an error (%s)", err)
+                               }
+                       }
+               })
+       }
+}
+
+func TestConsulCreate(t *testing.T) {
+       testCases := []struct {
+               label         string
+               input         map[string]string
+               mock          *mockConsulKVStore
+               expectedError string
+       }{
+               {
+                       label: "Sucessful register a record to Consul Database",
+                       input: map[string]string{"key": "test-key", "value": "test-value"},
+                       mock:  &mockConsulKVStore{},
+               },
+               {
+                       label: "Fail to create a new record in Consul Database",
+                       input: map[string]string{"key": "test-key", "value": "test-value"},
+                       mock: &mockConsulKVStore{
+                               Err: pkgerrors.New("DB error"),
+                       },
+                       expectedError: "DB error",
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       client, _ := NewConsulStore(testCase.mock)
+                       err := client.Create(testCase.input["key"], testCase.input["value"])
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("Create method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.expectedError) {
+                                       t.Fatalf("Create method returned an error (%s)", err)
+                               }
+                       }
+               })
+       }
+}
+
+func TestConsulRead(t *testing.T) {
+       testCases := []struct {
+               label          string
+               input          string
+               mock           *mockConsulKVStore
+               expectedError  string
+               expectedResult string
+       }{
+               {
+                       label: "Sucessful retrieve a record from Consul Database",
+                       input: "test",
+                       mock: &mockConsulKVStore{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "test",
+                                               Value: []byte("test-value"),
+                                       },
+                               },
+                       },
+                       expectedResult: "test-value",
+               },
+               {
+                       label: "Fail retrieve a non-existing record from Consul Database",
+                       input: "test",
+                       mock:  &mockConsulKVStore{},
+               },
+               {
+                       label: "Fail retrieve a record from Consul Database",
+                       input: "test",
+                       mock: &mockConsulKVStore{
+                               Err: pkgerrors.New("DB error"),
+                       },
+                       expectedError: "DB error",
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       client, _ := NewConsulStore(testCase.mock)
+                       result, err := client.Read(testCase.input)
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("Read method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.expectedError) {
+                                       t.Fatalf("Read method returned an error (%s)", err)
+                               }
+                       } else {
+                               if testCase.expectedError != "" && testCase.expectedResult == "" {
+                                       t.Fatalf("Read method was expecting \"%s\" error message", testCase.expectedError)
+                               }
+                               if !reflect.DeepEqual(testCase.expectedResult, result) {
+
+                                       t.Fatalf("Read method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
+                               }
+                       }
+               })
+       }
+}
+
+func TestConsulDelete(t *testing.T) {
+       testCases := []struct {
+               label         string
+               input         string
+               mock          *mockConsulKVStore
+               expectedError string
+       }{
+               {
+                       label: "Sucessful delete a record to Consul Database",
+                       input: "test",
+                       mock:  &mockConsulKVStore{},
+               },
+               {
+                       label: "Fail to delete a record in Consul Database",
+                       mock: &mockConsulKVStore{
+                               Err: pkgerrors.New("DB error"),
+                       },
+                       expectedError: "DB error",
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       client, _ := NewConsulStore(testCase.mock)
+                       err := client.Delete(testCase.input)
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("Delete method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.expectedError) {
+                                       t.Fatalf("Delete method returned an error (%s)", err)
+                               }
+                       }
+               })
+       }
+}
+
+func TestConsulReadAll(t *testing.T) {
+       testCases := []struct {
+               label          string
+               input          string
+               mock           *mockConsulKVStore
+               expectedError  string
+               expectedResult []string
+       }{
+               {
+                       label: "Sucessful retrieve a list from Consul Database",
+                       input: "test",
+                       mock: &mockConsulKVStore{
+                               Items: api.KVPairs{
+                                       &api.KVPair{
+                                               Key:   "test",
+                                               Value: []byte("test-value"),
+                                       },
+                                       &api.KVPair{
+                                               Key:   "test2",
+                                               Value: []byte("test-value2"),
+                                       },
+                               },
+                       },
+                       expectedResult: []string{"test", "test2"},
+               },
+               {
+                       label: "Sucessful retrieve an empty list from Consul Database",
+                       input: "test",
+                       mock:  &mockConsulKVStore{},
+               },
+               {
+                       label: "Fail retrieve a record from Consul Database",
+                       input: "test",
+                       mock: &mockConsulKVStore{
+                               Err: pkgerrors.New("DB error"),
+                       },
+                       expectedError: "DB error",
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       client, _ := NewConsulStore(testCase.mock)
+                       result, err := client.ReadAll(testCase.input)
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("ReadAll method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.expectedError) {
+                                       t.Fatalf("ReadAll method returned an error (%s)", err)
+                               }
+                       } else {
+                               if testCase.expectedError != "" && testCase.expectedResult == nil {
+                                       t.Fatalf("ReadAll method was expecting \"%s\" error message", testCase.expectedError)
+                               }
+                               if !reflect.DeepEqual(testCase.expectedResult, result) {
+
+                                       t.Fatalf("ReadAll method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
+                               }
+                       }
+               })
+       }
+}
diff --git a/src/k8splugin/db/db_test.go b/src/k8splugin/db/db_test.go
deleted file mode 100644 (file)
index d37cd7a..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-// +build unit
-
-/*
-Copyright 2018 Intel Corporation.
-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 db
-
-import (
-       "reflect"
-       "testing"
-)
-
-func TestCreateDBClient(t *testing.T) {
-       oldDBconn := DBconn
-
-       defer func() {
-               DBconn = oldDBconn
-       }()
-
-       t.Run("Successfully create DB client", func(t *testing.T) {
-               expectedDB := ConsulDB{}
-
-               err := CreateDBClient("consul")
-               if err != nil {
-                       t.Fatalf("TestCreateDBClient returned an error (%s)", err)
-               }
-
-               if !reflect.DeepEqual(DBconn, &expectedDB) {
-                       t.Fatalf("TestCreateDBClient set DBconn as:\n result=%v\n expected=%v", DBconn, expectedDB)
-               }
-       })
-}
-
-func TestSerialize(t *testing.T) {
-
-       inp := map[string]interface{}{
-               "UUID":   "123e4567-e89b-12d3-a456-426655440000",
-               "Data":   "sdaijsdiodalkfjsdlagf",
-               "Number": 23,
-               "Float":  34.4,
-               "Map": map[string]interface{}{
-                       "m1": "m1",
-                       "m2": 2,
-                       "m3": 3.0,
-               },
-       }
-
-       got, err := Serialize(inp)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       expected := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
-               "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m2\":2,\"m3\":3}," +
-               "\"Number\":23,\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
-
-       if expected != got {
-               t.Errorf("Serialize returned unexpected string: %s;"+
-                       " expected %sv", got, expected)
-       }
-}
-
-func TestDeSerialize(t *testing.T) {
-
-       inp := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
-               "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m3\":3}," +
-               "\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
-
-       got := make(map[string]interface{})
-       err := DeSerialize(inp, &got)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       expected := map[string]interface{}{
-               "UUID":  "123e4567-e89b-12d3-a456-426655440000",
-               "Data":  "sdaijsdiodalkfjsdlagf",
-               "Float": 34.4,
-               "Map": map[string]interface{}{
-                       "m1": "m1",
-                       "m3": 3.0,
-               },
-       }
-
-       if reflect.DeepEqual(expected, got) == false {
-               t.Errorf("Serialize returned unexpected : %s;"+
-                       " expected %s", got, expected)
-       }
-}
similarity index 79%
rename from src/k8splugin/db/DB.go
rename to src/k8splugin/db/store.go
index d92b595..c1a8b31 100644 (file)
@@ -21,27 +21,30 @@ import (
 )
 
 // DBconn interface used to talk a concrete Database connection
-var DBconn DatabaseConnection
-
-// DatabaseConnection is an interface for accessing a database
-type DatabaseConnection interface {
-       InitializeDatabase() error
-       CheckDatabase() error
-       CreateEntry(string, string) error
-       ReadEntry(string) (string, bool, error)
-       DeleteEntry(string) error
+var DBconn Store
+
+// Store is an interface for accessing a database
+type Store interface {
+       HealthCheck() error
+
+       Create(string, string) error
+       Read(string) (string, error)
+       // Update(string) (string, error)
+       Delete(string) error
+
        ReadAll(string) ([]string, error)
 }
 
 // CreateDBClient creates the DB client
-var CreateDBClient = func(dbType string) error {
+func CreateDBClient(dbType string) error {
+       var err error
        switch dbType {
        case "consul":
-               DBconn = &ConsulDB{}
-               return nil
+               DBconn, err = NewConsulStore(nil)
        default:
                return pkgerrors.New(dbType + "DB not supported")
        }
+       return err
 }
 
 // Serialize converts given data into a JSON string
diff --git a/src/k8splugin/db/store_test.go b/src/k8splugin/db/store_test.go
new file mode 100644 (file)
index 0000000..9bbe4a9
--- /dev/null
@@ -0,0 +1,123 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+       "reflect"
+       "strings"
+       "testing"
+)
+
+func TestCreateDBClient(t *testing.T) {
+       t.Run("Successfully create DB client", func(t *testing.T) {
+               expected := &ConsulStore{}
+
+               err := CreateDBClient("consul")
+               if err != nil {
+                       t.Fatalf("CreateDBClient returned an error (%s)", err)
+               }
+               if reflect.TypeOf(DBconn) != reflect.TypeOf(expected) {
+                       t.Fatalf("CreateDBClient set DBconn as:\n result=%T\n expected=%T", DBconn, expected)
+               }
+       })
+       t.Run("Fail to create client for unsupported DB", func(t *testing.T) {
+               err := CreateDBClient("fakeDB")
+               if err == nil {
+                       t.Fatal("CreateDBClient didn't return an error")
+               }
+               if !strings.Contains(string(err.Error()), "DB not supported") {
+                       t.Fatalf("CreateDBClient method returned an error (%s)", err)
+               }
+       })
+}
+
+func TestSerialize(t *testing.T) {
+
+       inp := map[string]interface{}{
+               "UUID":   "123e4567-e89b-12d3-a456-426655440000",
+               "Data":   "sdaijsdiodalkfjsdlagf",
+               "Number": 23,
+               "Float":  34.4,
+               "Map": map[string]interface{}{
+                       "m1": "m1",
+                       "m2": 2,
+                       "m3": 3.0,
+               },
+       }
+
+       got, err := Serialize(inp)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       expected := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+               "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m2\":2,\"m3\":3}," +
+               "\"Number\":23,\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
+
+       if expected != got {
+               t.Errorf("Serialize returned unexpected string: %s;"+
+                       " expected %sv", got, expected)
+       }
+}
+
+func TestDeSerialize(t *testing.T) {
+       testCases := []struct {
+               label    string
+               input    string
+               expected map[string]interface{}
+               errMsg   string
+       }{
+               {
+                       label: "Sucessful deserialize entry",
+                       input: "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+                               "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m3\":3}," +
+                               "\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}",
+                       expected: map[string]interface{}{
+                               "UUID":  "123e4567-e89b-12d3-a456-426655440000",
+                               "Data":  "sdaijsdiodalkfjsdlagf",
+                               "Float": 34.4,
+                               "Map": map[string]interface{}{
+                                       "m1": "m1",
+                                       "m3": 3.0,
+                               },
+                       },
+               },
+               {
+                       label:  "Fail to deserialize invalid entry",
+                       input:  "{invalid}",
+                       errMsg: "Error deSerializing {invalid}: invalid character 'i' looking for beginning of object key string",
+               },
+       }
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       got := make(map[string]interface{})
+                       err := DeSerialize(testCase.input, &got)
+                       if err != nil {
+                               if testCase.errMsg == "" {
+                                       t.Fatalf("DeSerialize method return an un-expected (%s)", err)
+                               }
+                               if !strings.Contains(string(err.Error()), testCase.errMsg) {
+                                       t.Fatalf("DeSerialize method returned an error (%s)", err)
+                               }
+                       } else {
+                               if !reflect.DeepEqual(testCase.expected, got) {
+                                       t.Errorf("Serialize returned unexpected : %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
diff --git a/src/k8splugin/db/testing.go b/src/k8splugin/db/testing.go
new file mode 100644 (file)
index 0000000..672fcbf
--- /dev/null
@@ -0,0 +1,65 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+       "github.com/hashicorp/consul/api"
+)
+
+//Creating an embedded interface via anonymous variable
+//This allows us to make mockDB satisfy the DatabaseConnection
+//interface even if we are not implementing all the methods in it
+type MockDB struct {
+       Store
+       Items api.KVPairs
+       Err   error
+}
+
+func (m *MockDB) Create(key string, value string) error {
+       return m.Err
+}
+
+func (m *MockDB) Read(key string) (string, error) {
+       if m.Err != nil {
+               return "", m.Err
+       }
+
+       for _, kvpair := range m.Items {
+               if kvpair.Key == key {
+                       return string(kvpair.Value), nil
+               }
+       }
+
+       return "", nil
+}
+
+func (m *MockDB) Delete(key string) error {
+       return m.Err
+}
+
+func (m *MockDB) ReadAll(prefix string) ([]string, error) {
+       if m.Err != nil {
+               return []string{}, m.Err
+       }
+
+       var res []string
+
+       for _, keypair := range m.Items {
+               res = append(res, keypair.Key)
+       }
+
+       return res, nil
+}
index 322b2d7..0fb81db 100644 (file)
@@ -67,7 +67,7 @@ func (v *VNFDefinitionClient) Create(vnfd VNFDefinition) (VNFDefinition, error)
                return VNFDefinition{}, pkgerrors.Wrap(err, "Serialize VNF Definition")
        }
 
-       err = db.DBconn.CreateEntry(key, serData)
+       err = db.DBconn.Create(key, serData)
        if err != nil {
                return VNFDefinition{}, pkgerrors.Wrap(err, "Creating DB Entry")
        }
@@ -85,12 +85,12 @@ func (v *VNFDefinitionClient) List() ([]VNFDefinition, error) {
        var retData []VNFDefinition
 
        for _, key := range strArray {
-               value, ok, err := db.DBconn.ReadEntry(key)
+               value, err := db.DBconn.Read(key)
                if err != nil {
                        log.Printf("Error Reading Key: %s", key)
                        continue
                }
-               if ok {
+               if value != "" {
                        vnfd := VNFDefinition{}
                        err = db.DeSerialize(value, &vnfd)
                        if err != nil {
@@ -106,12 +106,12 @@ func (v *VNFDefinitionClient) List() ([]VNFDefinition, error) {
 
 // Get returns the VNF Definition for corresponding ID
 func (v *VNFDefinitionClient) Get(vnfID string) (VNFDefinition, error) {
-       value, ok, err := db.DBconn.ReadEntry(v.keyPrefix + vnfID)
+       value, err := db.DBconn.Read(v.keyPrefix + vnfID)
        if err != nil {
                return VNFDefinition{}, pkgerrors.Wrap(err, "Get VNF Definitions")
        }
 
-       if ok {
+       if value != "" {
                vnfd := VNFDefinition{}
                err = db.DeSerialize(value, &vnfd)
                if err != nil {
@@ -125,7 +125,7 @@ func (v *VNFDefinitionClient) Get(vnfID string) (VNFDefinition, error) {
 
 // Delete deletes the VNF Definition from database
 func (v *VNFDefinitionClient) Delete(vnfID string) error {
-       err := db.DBconn.DeleteEntry(v.keyPrefix + vnfID)
+       err := db.DBconn.Delete(v.keyPrefix + vnfID)
        if err != nil {
                return pkgerrors.Wrap(err, "Delete VNF Definitions")
        }
index 54ab5f4..3230d3e 100644 (file)
@@ -1,3 +1,5 @@
+// +build unit
+
 /*
  * Copyright 2018 Intel Corporation, Inc
  *
@@ -26,57 +28,12 @@ import (
        pkgerrors "github.com/pkg/errors"
 )
 
-//Creating an embedded interface via anonymous variable
-//This allows us to make mockDB satisfy the DatabaseConnection
-//interface even if we are not implementing all the methods in it
-type mockDB struct {
-       db.DatabaseConnection
-       Items api.KVPairs
-       Err   error
-}
-
-func (m *mockDB) CreateEntry(key string, value string) error {
-       return m.Err
-}
-
-func (m *mockDB) ReadEntry(key string) (string, bool, error) {
-       if m.Err != nil {
-               return "", false, m.Err
-       }
-
-       for _, kvpair := range m.Items {
-               if kvpair.Key == key {
-                       return string(kvpair.Value), true, nil
-               }
-       }
-
-       return "", false, nil
-}
-
-func (m *mockDB) DeleteEntry(key string) error {
-       return m.Err
-}
-
-func (m *mockDB) ReadAll(prefix string) ([]string, error) {
-       if m.Err != nil {
-               return []string{}, m.Err
-       }
-
-       var res []string
-
-       for _, keypair := range m.Items {
-               res = append(res, keypair.Key)
-       }
-
-       return res, nil
-}
-
 func TestCreate(t *testing.T) {
        testCases := []struct {
                label         string
                inp           VNFDefinition
                expectedError string
-               mockdb        *mockDB
+               mockdb        *db.MockDB
                expected      VNFDefinition
        }{
                {
@@ -94,12 +51,12 @@ func TestCreate(t *testing.T) {
                                ServiceType: "firewall",
                        },
                        expectedError: "",
-                       mockdb:        &mockDB{},
+                       mockdb:        &db.MockDB{},
                },
                {
                        label:         "Failed Create VNF Definition",
                        expectedError: "Error Creating Definition",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Err: pkgerrors.New("Error Creating Definition"),
                        },
                },
@@ -132,7 +89,7 @@ func TestList(t *testing.T) {
        testCases := []struct {
                label         string
                expectedError string
-               mockdb        *mockDB
+               mockdb        *db.MockDB
                expected      []VNFDefinition
        }{
                {
@@ -152,7 +109,7 @@ func TestList(t *testing.T) {
                                },
                        },
                        expectedError: "",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Items: api.KVPairs{
                                        &api.KVPair{
                                                Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
@@ -174,7 +131,7 @@ func TestList(t *testing.T) {
                {
                        label:         "List Error",
                        expectedError: "DB Error",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Err: pkgerrors.New("DB Error"),
                        },
                },
@@ -207,7 +164,7 @@ func TestGet(t *testing.T) {
        testCases := []struct {
                label         string
                expectedError string
-               mockdb        *mockDB
+               mockdb        *db.MockDB
                inp           string
                expected      VNFDefinition
        }{
@@ -221,7 +178,7 @@ func TestGet(t *testing.T) {
                                ServiceType: "firewall",
                        },
                        expectedError: "",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Items: api.KVPairs{
                                        &api.KVPair{
                                                Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
@@ -236,7 +193,7 @@ func TestGet(t *testing.T) {
                {
                        label:         "Get Error",
                        expectedError: "DB Error",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Err: pkgerrors.New("DB Error"),
                        },
                },
@@ -270,18 +227,18 @@ func TestDelete(t *testing.T) {
                label         string
                inp           string
                expectedError string
-               mockdb        *mockDB
+               mockdb        *db.MockDB
                expected      []VNFDefinition
        }{
                {
                        label:  "Delete VNF Definition",
                        inp:    "123e4567-e89b-12d3-a456-426655440000",
-                       mockdb: &mockDB{},
+                       mockdb: &db.MockDB{},
                },
                {
                        label:         "Delete Error",
                        expectedError: "DB Error",
-                       mockdb: &mockDB{
+                       mockdb: &db.MockDB{
                                Err: pkgerrors.New("DB Error"),
                        },
                },