From cc05d4af8f082d8174bde5c43fc45b1acc61339f Mon Sep 17 00:00:00 2001 From: Victor Morales Date: Tue, 23 Oct 2018 11:54:58 -0700 Subject: Create UTs to cover DB calls 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 Issue-ID: MULTICLOUD-301 --- src/k8splugin/api/api.go | 7 +- src/k8splugin/api/handler.go | 39 ++- src/k8splugin/api/handler_test.go | 577 +++++++++++++++++++++++++------------- src/k8splugin/db/DB.go | 63 ----- src/k8splugin/db/consul.go | 118 ++++---- src/k8splugin/db/consul_test.go | 298 ++++++++++++++++++++ src/k8splugin/db/db_test.go | 99 ------- src/k8splugin/db/store.go | 66 +++++ src/k8splugin/db/store_test.go | 123 ++++++++ src/k8splugin/db/testing.go | 65 +++++ src/k8splugin/vnfd/vnfd.go | 12 +- src/k8splugin/vnfd/vnfd_test.go | 71 +---- 12 files changed, 1031 insertions(+), 507 deletions(-) delete mode 100644 src/k8splugin/db/DB.go create mode 100644 src/k8splugin/db/consul_test.go delete mode 100644 src/k8splugin/db/db_test.go create mode 100644 src/k8splugin/db/store.go create mode 100644 src/k8splugin/db/store_test.go create mode 100644 src/k8splugin/db/testing.go diff --git a/src/k8splugin/api/api.go b/src/k8splugin/api/api.go index e90c994e..f05fbb0b 100644 --- a/src/k8splugin/api/api.go +++ b/src/k8splugin/api/api.go @@ -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) } diff --git a/src/k8splugin/api/handler.go b/src/k8splugin/api/handler.go index b4828b76..53fa2317 100644 --- a/src/k8splugin/api/handler.go +++ b/src/k8splugin/api/handler.go @@ -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 } diff --git a/src/k8splugin/api/handler_test.go b/src/k8splugin/api/handler_test.go index ac97d011..3336bbc2 100644 --- a/src/k8splugin/api/handler_test.go +++ b/src/k8splugin/api/handler_test.go @@ -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) + } + } + }) + } } diff --git a/src/k8splugin/db/DB.go b/src/k8splugin/db/DB.go deleted file mode 100644 index d92b5953..00000000 --- a/src/k8splugin/db/DB.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -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 ( - "encoding/json" - "reflect" - - pkgerrors "github.com/pkg/errors" -) - -// 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 - ReadAll(string) ([]string, error) -} - -// CreateDBClient creates the DB client -var CreateDBClient = func(dbType string) error { - switch dbType { - case "consul": - DBconn = &ConsulDB{} - return nil - default: - return pkgerrors.New(dbType + "DB not supported") - } -} - -// Serialize converts given data into a JSON string -func Serialize(v interface{}) (string, error) { - out, err := json.Marshal(v) - if err != nil { - return "", pkgerrors.Wrap(err, "Error serializing "+reflect.TypeOf(v).String()) - } - return string(out), nil -} - -// DeSerialize converts string to a json object specified by type -func DeSerialize(str string, v interface{}) error { - err := json.Unmarshal([]byte(str), &v) - if err != nil { - return pkgerrors.Wrap(err, "Error deSerializing "+str) - } - return nil -} diff --git a/src/k8splugin/db/consul.go b/src/k8splugin/db/consul.go index 950eea34..d7507242 100644 --- a/src/k8splugin/db/consul.go +++ b/src/k8splugin/db/consul.go @@ -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 index 00000000..ede1a5e9 --- /dev/null +++ b/src/k8splugin/db/consul_test.go @@ -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 index d37cd7ae..00000000 --- a/src/k8splugin/db/db_test.go +++ /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) - } -} diff --git a/src/k8splugin/db/store.go b/src/k8splugin/db/store.go new file mode 100644 index 00000000..c1a8b31f --- /dev/null +++ b/src/k8splugin/db/store.go @@ -0,0 +1,66 @@ +/* +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 ( + "encoding/json" + "reflect" + + pkgerrors "github.com/pkg/errors" +) + +// DBconn interface used to talk a concrete Database connection +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 +func CreateDBClient(dbType string) error { + var err error + switch dbType { + case "consul": + DBconn, err = NewConsulStore(nil) + default: + return pkgerrors.New(dbType + "DB not supported") + } + return err +} + +// Serialize converts given data into a JSON string +func Serialize(v interface{}) (string, error) { + out, err := json.Marshal(v) + if err != nil { + return "", pkgerrors.Wrap(err, "Error serializing "+reflect.TypeOf(v).String()) + } + return string(out), nil +} + +// DeSerialize converts string to a json object specified by type +func DeSerialize(str string, v interface{}) error { + err := json.Unmarshal([]byte(str), &v) + if err != nil { + return pkgerrors.Wrap(err, "Error deSerializing "+str) + } + return nil +} diff --git a/src/k8splugin/db/store_test.go b/src/k8splugin/db/store_test.go new file mode 100644 index 00000000..9bbe4a92 --- /dev/null +++ b/src/k8splugin/db/store_test.go @@ -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 index 00000000..672fcbfb --- /dev/null +++ b/src/k8splugin/db/testing.go @@ -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 +} diff --git a/src/k8splugin/vnfd/vnfd.go b/src/k8splugin/vnfd/vnfd.go index 322b2d78..0fb81dbd 100644 --- a/src/k8splugin/vnfd/vnfd.go +++ b/src/k8splugin/vnfd/vnfd.go @@ -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") } diff --git a/src/k8splugin/vnfd/vnfd_test.go b/src/k8splugin/vnfd/vnfd_test.go index 54ab5f49..3230d3ef 100644 --- a/src/k8splugin/vnfd/vnfd_test.go +++ b/src/k8splugin/vnfd/vnfd_test.go @@ -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"), }, }, -- cgit 1.2.3-korg