Merge "Use httptest instead of http in unit tests"
[multicloud/k8s.git] / src / k8splugin / api / handler_test.go
1 // +build unit
2
3 /*
4 Copyright 2018 Intel Corporation.
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8     http://www.apache.org/licenses/LICENSE-2.0
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 */
15
16 package api
17
18 import (
19         "bytes"
20         "encoding/json"
21         "io"
22         "net/http"
23         "net/http/httptest"
24         "reflect"
25         "testing"
26
27         pkgerrors "github.com/pkg/errors"
28         "k8s.io/client-go/kubernetes"
29
30         helper "k8splugin/internal/app"
31         "k8splugin/internal/db"
32 )
33
34 type mockCSAR struct {
35         externalVNFID       string
36         resourceYAMLNameMap map[string][]string
37         err                 error
38 }
39
40 func (c *mockCSAR) CreateVNF(id, r, n string,
41         kubeclient *kubernetes.Clientset) (string, map[string][]string, error) {
42         return c.externalVNFID, c.resourceYAMLNameMap, c.err
43 }
44
45 func (c *mockCSAR) DestroyVNF(data map[string][]string, namespace string,
46         kubeclient *kubernetes.Clientset) error {
47         return c.err
48 }
49
50 func executeRequest(req *http.Request) *http.Response {
51         router := NewRouter("", nil, nil)
52         recorder := httptest.NewRecorder()
53         router.ServeHTTP(recorder, req)
54
55         return recorder.Result()
56 }
57
58 func checkResponseCode(t *testing.T, expected, actual int) {
59         if expected != actual {
60                 t.Errorf("Expected response code %d. Got %d\n", expected, actual)
61         }
62 }
63
64 func TestCreateHandler(t *testing.T) {
65         testCases := []struct {
66                 label               string
67                 input               io.Reader
68                 expectedCode        int
69                 mockGetVNFClientErr error
70                 mockCreateVNF       *mockCSAR
71                 mockStore           *db.MockDB
72         }{
73                 {
74                         label:        "Missing body failure",
75                         expectedCode: http.StatusBadRequest,
76                 },
77                 {
78                         label:        "Invalid JSON request format",
79                         input:        bytes.NewBuffer([]byte("invalid")),
80                         expectedCode: http.StatusUnprocessableEntity,
81                 },
82                 {
83                         label: "Missing parameter failure",
84                         input: bytes.NewBuffer([]byte(`{
85                                 "csar_id": "testID",
86                                 "oof_parameters": {
87                                         "key_values": {
88                                                 "key1": "value1",
89                                                 "key2": "value2"
90                                         }
91                                 },
92                                 "vnf_instance_name": "test",
93                                 "vnf_instance_description": "vRouter_test_description"
94                         }`)),
95                         expectedCode: http.StatusUnprocessableEntity,
96                 },
97                 {
98                         label: "Fail to get the VNF client",
99                         input: bytes.NewBuffer([]byte(`{
100                                 "cloud_region_id": "region1",
101                                 "rb_profile_id": "123e4567-e89b-12d3-a456-426655440000",
102                                 "csar_id": "UUID-1"
103                         }`)),
104                         expectedCode:        http.StatusInternalServerError,
105                         mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
106                 },
107                 {
108                         label: "Fail to create the VNF instance",
109                         input: bytes.NewBuffer([]byte(`{
110                                 "cloud_region_id": "region1",
111                                 "rb_profile_id": "123e4567-e89b-12d3-a456-426655440000",
112                                 "csar_id": "UUID-1"
113                         }`)),
114                         expectedCode: http.StatusInternalServerError,
115                         mockCreateVNF: &mockCSAR{
116                                 err: pkgerrors.New("Internal error"),
117                         },
118                 },
119                 {
120                         label: "Fail to create a VNF DB record",
121                         input: bytes.NewBuffer([]byte(`{
122                                 "cloud_region_id": "region1",
123                                 "rb_profile_id": "123e4567-e89b-12d3-a456-426655440000",
124                                 "csar_id": "UUID-1"
125                         }`)),
126                         expectedCode: http.StatusInternalServerError,
127                         mockCreateVNF: &mockCSAR{
128                                 resourceYAMLNameMap: map[string][]string{},
129                         },
130                         mockStore: &db.MockDB{
131                                 Err: pkgerrors.New("Internal error"),
132                         },
133                 },
134                 {
135                         label: "Succesful create a VNF",
136                         input: bytes.NewBuffer([]byte(`{
137                                 "cloud_region_id": "region1",
138                                 "rb_profile_id": "123e4567-e89b-12d3-a456-426655440000",
139                                 "csar_id": "UUID-1"
140                         }`)),
141                         expectedCode: http.StatusCreated,
142                         mockCreateVNF: &mockCSAR{
143                                 resourceYAMLNameMap: map[string][]string{
144                                         "deployment": []string{"cloud1-default-uuid-sisedeploy"},
145                                         "service":    []string{"cloud1-default-uuid-sisesvc"},
146                                 },
147                         },
148                         mockStore: &db.MockDB{},
149                 },
150         }
151
152         for _, testCase := range testCases {
153                 t.Run(testCase.label, func(t *testing.T) {
154                         GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
155                                 return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
156                         }
157                         if testCase.mockCreateVNF != nil {
158                                 helper.CreateVNF = testCase.mockCreateVNF.CreateVNF
159                         }
160                         if testCase.mockStore != nil {
161                                 db.DBconn = testCase.mockStore
162                         }
163
164                         request := httptest.NewRequest("POST", "/v1/vnf_instances/", testCase.input)
165                         result := executeRequest(request)
166
167                         if testCase.expectedCode != result.StatusCode {
168                                 t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v", result.StatusCode, testCase.expectedCode)
169                         }
170                         if result.StatusCode == http.StatusCreated {
171                                 var response CreateVnfResponse
172                                 err := json.NewDecoder(result.Body).Decode(&response)
173                                 if err != nil {
174                                         t.Fatalf("Parsing the returned response got an error (%s)", err)
175                                 }
176                         }
177                 })
178         }
179 }
180
181 func TestListHandler(t *testing.T) {
182         testCases := []struct {
183                 label            string
184                 expectedCode     int
185                 expectedResponse []string
186                 mockStore        *db.MockDB
187         }{
188                 {
189                         label:        "Fail to retrieve DB records",
190                         expectedCode: http.StatusInternalServerError,
191                         mockStore: &db.MockDB{
192                                 Err: pkgerrors.New("Internal error"),
193                         },
194                 },
195                 {
196                         label:            "Get empty list",
197                         expectedCode:     http.StatusOK,
198                         expectedResponse: []string{""},
199                         mockStore:        &db.MockDB{},
200                 },
201                 {
202                         label:            "Succesful get a list of VNF",
203                         expectedCode:     http.StatusOK,
204                         expectedResponse: []string{"uid1"},
205                         mockStore: &db.MockDB{
206                                 Items: map[string]map[string][]byte{
207                                         "uuid1": {
208                                                 "data": []byte("{}"),
209                                         },
210                                 },
211                         },
212                 },
213         }
214
215         for _, testCase := range testCases {
216                 t.Run(testCase.label, func(t *testing.T) {
217                         if testCase.mockStore != nil {
218                                 db.DBconn = testCase.mockStore
219                         }
220
221                         request := httptest.NewRequest("GET", "/v1/vnf_instances/cloud1/default", nil)
222                         result := executeRequest(request)
223
224                         if testCase.expectedCode != result.StatusCode {
225                                 t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v",
226                                         result.StatusCode, testCase.expectedCode)
227                         }
228                         if result.StatusCode == http.StatusOK {
229                                 var response ListVnfsResponse
230                                 err := json.NewDecoder(result.Body).Decode(&response)
231                                 if err != nil {
232                                         t.Fatalf("Parsing the returned response got an error (%s)", err)
233                                 }
234                                 if !reflect.DeepEqual(testCase.expectedResponse, response.VNFs) {
235                                         t.Fatalf("TestListHandler returned:\n result=%v\n expected=%v",
236                                                 response.VNFs, testCase.expectedResponse)
237                                 }
238                         }
239                 })
240         }
241 }
242
243 func TestDeleteHandler(t *testing.T) {
244         testCases := []struct {
245                 label               string
246                 expectedCode        int
247                 mockGetVNFClientErr error
248                 mockDeleteVNF       *mockCSAR
249                 mockStore           *db.MockDB
250         }{
251                 {
252                         label:        "Fail to read a VNF DB record",
253                         expectedCode: http.StatusInternalServerError,
254                         mockStore: &db.MockDB{
255                                 Err: pkgerrors.New("Internal error"),
256                         },
257                 },
258                 {
259                         label:        "Fail to find VNF record be deleted",
260                         expectedCode: http.StatusInternalServerError,
261                         mockStore: &db.MockDB{
262                                 Items: map[string]map[string][]byte{},
263                         },
264                 },
265                 {
266                         label:        "Fail to unmarshal the DB record",
267                         expectedCode: http.StatusInternalServerError,
268                         mockStore: &db.MockDB{
269                                 Items: map[string]map[string][]byte{
270                                         "cloudregion1-testnamespace-uuid1": {
271                                                 "data": []byte("{invalid format}"),
272                                         },
273                                 },
274                         },
275                 },
276                 {
277                         label:               "Fail to get the VNF client",
278                         expectedCode:        http.StatusInternalServerError,
279                         mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
280                         mockStore: &db.MockDB{
281                                 Items: map[string]map[string][]byte{
282                                         "cloudregion1-testnamespace-uuid1": {
283                                                 "data": []byte(
284                                                         "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
285                                                                 "\"service\": [\"svc1\", \"svc2\"]}"),
286                                         },
287                                 },
288                         },
289                 },
290                 {
291                         label:        "Fail to destroy VNF",
292                         expectedCode: http.StatusInternalServerError,
293                         mockStore: &db.MockDB{
294                                 Items: map[string]map[string][]byte{
295                                         "cloudregion1-testnamespace-uuid1": {
296                                                 "data": []byte(
297                                                         "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
298                                                                 "\"service\": [\"svc1\", \"svc2\"]}"),
299                                         },
300                                 },
301                         },
302                         mockDeleteVNF: &mockCSAR{
303                                 err: pkgerrors.New("Internal error"),
304                         },
305                 },
306                 {
307                         label:        "Succesful delete a VNF",
308                         expectedCode: http.StatusAccepted,
309                         mockStore: &db.MockDB{
310                                 Items: map[string]map[string][]byte{
311                                         "cloudregion1-testnamespace-uuid1": {
312                                                 "data": []byte(
313                                                         "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
314                                                                 "\"service\": [\"svc1\", \"svc2\"]}"),
315                                         },
316                                 },
317                         },
318                         mockDeleteVNF: &mockCSAR{},
319                 },
320         }
321
322         for _, testCase := range testCases {
323                 t.Run(testCase.label, func(t *testing.T) {
324                         GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
325                                 return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
326                         }
327                         if testCase.mockStore != nil {
328                                 db.DBconn = testCase.mockStore
329                         }
330                         if testCase.mockDeleteVNF != nil {
331                                 helper.DestroyVNF = testCase.mockDeleteVNF.DestroyVNF
332                         }
333
334                         request := httptest.NewRequest("DELETE", "/v1/vnf_instances/cloudregion1/testnamespace/uuid1", nil)
335                         result := executeRequest(request)
336
337                         if testCase.expectedCode != result.StatusCode {
338                                 t.Fatalf("Request method returned: %v and it was expected: %v", result.StatusCode, testCase.expectedCode)
339                         }
340                 })
341         }
342 }
343
344 // TODO: Update this test when the UpdateVNF endpoint is fixed.
345 /*
346 func TestVNFInstanceUpdate(t *testing.T) {
347         t.Run("Succesful update a VNF", func(t *testing.T) {
348                 payload := []byte(`{
349                         "cloud_region_id": "region1",
350                         "csar_id": "UUID-1",
351                         "oof_parameters": [{
352                                 "key1": "value1",
353                                 "key2": "value2",
354                                 "key3": {}
355                         }],
356                         "network_parameters": {
357                                 "oam_ip_address": {
358                                         "connection_point": "string",
359                                         "ip_address": "string",
360                                         "workload_name": "string"
361                                 }
362                         }
363                 }`)
364                 expected := &UpdateVnfResponse{
365                         DeploymentID: "1",
366                 }
367
368                 var result UpdateVnfResponse
369
370                 req := httptest.NewRequest("PUT", "/v1/vnf_instances/1", bytes.NewBuffer(payload))
371
372                 GetVNFClient = func(configPath string) (krd.VNFInstanceClientInterface, error) {
373                         return &mockClient{
374                                 update: func() error {
375                                         return nil
376                                 },
377                         }, nil
378                 }
379                 utils.ReadCSARFromFileSystem = func(csarID string) (*krd.KubernetesData, error) {
380                         kubeData := &krd.KubernetesData{
381                                 Deployment: &appsV1.Deployment{},
382                                 Service:    &coreV1.Service{},
383                         }
384                         return kubeData, nil
385                 }
386
387                 response := executeRequest(req)
388                 checkResponseCode(t, http.StatusCreated, response.Code)
389
390                 err := json.NewDecoder(response.Body).Decode(&result)
391                 if err != nil {
392                         t.Fatalf("TestVNFInstanceUpdate returned:\n result=%v\n expected=%v", err, expected.DeploymentID)
393                 }
394
395                 if result.DeploymentID != expected.DeploymentID {
396                         t.Fatalf("TestVNFInstanceUpdate returned:\n result=%v\n expected=%v", result.DeploymentID, expected.DeploymentID)
397                 }
398         })
399 }
400 */
401
402 func TestGetHandler(t *testing.T) {
403         testCases := []struct {
404                 label            string
405                 expectedCode     int
406                 expectedResponse *GetVnfResponse
407                 mockStore        *db.MockDB
408         }{
409                 {
410                         label:        "Fail to retrieve DB record",
411                         expectedCode: http.StatusInternalServerError,
412                         mockStore: &db.MockDB{
413                                 Err: pkgerrors.New("Internal error"),
414                         },
415                 },
416                 {
417                         label:        "Not found DB record",
418                         expectedCode: http.StatusInternalServerError,
419                         mockStore:    &db.MockDB{},
420                 },
421                 {
422                         label:        "Fail to unmarshal the DB record",
423                         expectedCode: http.StatusInternalServerError,
424                         mockStore: &db.MockDB{
425                                 Items: map[string]map[string][]byte{
426                                         "cloud1-default-1": {
427                                                 "data": []byte("{invalid-format}"),
428                                         },
429                                 },
430                         },
431                 },
432                 {
433                         label:        "Succesful get a list of VNF",
434                         expectedCode: http.StatusOK,
435                         expectedResponse: &GetVnfResponse{
436                                 VNFID:         "1",
437                                 CloudRegionID: "cloud1",
438                                 Namespace:     "default",
439                                 VNFComponents: map[string][]string{
440                                         "deployment": []string{"deploy1", "deploy2"},
441                                         "service":    []string{"svc1", "svc2"},
442                                 },
443                         },
444                         mockStore: &db.MockDB{
445                                 Items: map[string]map[string][]byte{
446                                         "cloud1-default-1": {
447                                                 "data": []byte(
448                                                         "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
449                                                                 "\"service\": [\"svc1\", \"svc2\"]}"),
450                                                 "cloud1-default-2": []byte("{}"),
451                                         },
452                                 },
453                         },
454                 },
455         }
456
457         for _, testCase := range testCases {
458                 t.Run(testCase.label, func(t *testing.T) {
459                         db.DBconn = testCase.mockStore
460                         request := httptest.NewRequest("GET", "/v1/vnf_instances/cloud1/default/1", nil)
461                         result := executeRequest(request)
462
463                         if testCase.expectedCode != result.StatusCode {
464                                 t.Fatalf("Request method returned: %v and it was expected: %v",
465                                         result.StatusCode, testCase.expectedCode)
466                         }
467                         if result.StatusCode == http.StatusOK {
468                                 var response GetVnfResponse
469                                 err := json.NewDecoder(result.Body).Decode(&response)
470                                 if err != nil {
471                                         t.Fatalf("Parsing the returned response got an error (%s)", err)
472                                 }
473                                 if !reflect.DeepEqual(testCase.expectedResponse, &response) {
474                                         t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
475                                                 &response, testCase.expectedResponse)
476                                 }
477                         }
478                 })
479         }
480 }