2 Copyright 2018 Intel Corporation.
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 http://www.apache.org/licenses/LICENSE-2.0
7 Unless required by applicable law or agreed to in writing, software
8 distributed under the License is distributed on an "AS IS" BASIS,
9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 See the License for the specific language governing permissions and
11 limitations under the License.
28 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30 "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
31 "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
33 "github.com/gorilla/mux"
34 pkgerrors "github.com/pkg/errors"
35 "k8s.io/apimachinery/pkg/runtime/schema"
38 //Creating an embedded interface via anonymous variable
39 //This allows us to make mockDB satisfy the DatabaseConnection
40 //interface even if we are not implementing all the methods in it
41 type mockInstanceClient struct {
43 // Items and err will be used to customize each test
44 // via a localized instantiation of mockInstanceClient
45 items []app.InstanceResponse
46 miniitems []app.InstanceMiniResponse
47 statusItem app.InstanceStatus
51 func (m *mockInstanceClient) Create(inp app.InstanceRequest) (app.InstanceResponse, error) {
53 return app.InstanceResponse{}, m.err
56 return m.items[0], nil
59 func (m *mockInstanceClient) Get(id string) (app.InstanceResponse, error) {
61 return app.InstanceResponse{}, m.err
64 return m.items[0], nil
67 func (m *mockInstanceClient) Query(id, apiVersion, kind, name, labels string) (app.InstanceStatus, error) {
69 return app.InstanceStatus{}, m.err
72 return m.statusItem, nil
75 func (m *mockInstanceClient) Status(id string) (app.InstanceStatus, error) {
77 return app.InstanceStatus{}, m.err
80 return m.statusItem, nil
83 func (m *mockInstanceClient) List(rbname, rbversion, profilename string) ([]app.InstanceMiniResponse, error) {
85 return []app.InstanceMiniResponse{}, m.err
88 return m.miniitems, nil
91 func (m *mockInstanceClient) Find(rbName string, ver string, profile string, labelKeys map[string]string) ([]app.InstanceMiniResponse, error) {
96 return m.miniitems, nil
99 func (m *mockInstanceClient) Delete(id string) error {
103 func executeRequest(request *http.Request, router *mux.Router) *http.Response {
104 recorder := httptest.NewRecorder()
105 router.ServeHTTP(recorder, request)
106 resp := recorder.Result()
110 func TestInstanceCreateHandler(t *testing.T) {
111 testCases := []struct {
114 expected app.InstanceResponse
116 instClient *mockInstanceClient
119 label: "Missing body failure",
120 expectedCode: http.StatusBadRequest,
123 label: "Invalid JSON request format",
124 input: bytes.NewBuffer([]byte("invalid")),
125 expectedCode: http.StatusUnprocessableEntity,
128 label: "Missing parameter failure",
129 input: bytes.NewBuffer([]byte(`{
130 "rb-name": "test-rbdef",
131 "profile-name": "profile1",
132 "cloud-region": "kud"
134 expectedCode: http.StatusUnprocessableEntity,
137 label: "Succesfully create an Instance",
138 input: bytes.NewBuffer([]byte(`{
139 "cloud-region": "region1",
140 "rb-name": "test-rbdef",
142 "profile-name": "profile1"
144 expected: app.InstanceResponse{
146 Request: app.InstanceRequest{
147 RBName: "test-rbdef",
149 ProfileName: "profile1",
150 CloudRegion: "region1",
152 Namespace: "testnamespace",
153 Resources: []helm.KubernetesResource{
155 GVK: schema.GroupVersionKind{
159 Name: "test-deployment",
162 GVK: schema.GroupVersionKind{
166 Name: "test-service",
170 expectedCode: http.StatusCreated,
171 instClient: &mockInstanceClient{
172 items: []app.InstanceResponse{
175 Request: app.InstanceRequest{
176 RBName: "test-rbdef",
178 ProfileName: "profile1",
179 CloudRegion: "region1",
181 Namespace: "testnamespace",
182 Resources: []helm.KubernetesResource{
184 GVK: schema.GroupVersionKind{
188 Name: "test-deployment",
191 GVK: schema.GroupVersionKind{
195 Name: "test-service",
204 for _, testCase := range testCases {
205 t.Run(testCase.label, func(t *testing.T) {
207 request := httptest.NewRequest("POST", "/v1/instance", testCase.input)
208 resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
210 if testCase.expectedCode != resp.StatusCode {
211 body, _ := ioutil.ReadAll(resp.Body)
213 t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v", resp.StatusCode, testCase.expectedCode)
216 if resp.StatusCode == http.StatusCreated {
217 var response app.InstanceResponse
218 err := json.NewDecoder(resp.Body).Decode(&response)
220 t.Fatalf("Parsing the returned response got an error (%s)", err)
227 func TestInstanceGetHandler(t *testing.T) {
228 testCases := []struct {
232 expectedResponse *app.InstanceResponse
233 instClient *mockInstanceClient
236 label: "Fail to retrieve Instance",
238 expectedCode: http.StatusInternalServerError,
239 instClient: &mockInstanceClient{
240 err: pkgerrors.New("Internal error"),
244 label: "Succesful get an Instance",
246 expectedCode: http.StatusOK,
247 expectedResponse: &app.InstanceResponse{
249 Request: app.InstanceRequest{
250 RBName: "test-rbdef",
252 ProfileName: "profile1",
253 CloudRegion: "region1",
255 Namespace: "testnamespace",
256 Resources: []helm.KubernetesResource{
258 GVK: schema.GroupVersionKind{
262 Name: "test-deployment",
265 GVK: schema.GroupVersionKind{
269 Name: "test-service",
273 instClient: &mockInstanceClient{
274 items: []app.InstanceResponse{
277 Request: app.InstanceRequest{
278 RBName: "test-rbdef",
280 ProfileName: "profile1",
281 CloudRegion: "region1",
283 Namespace: "testnamespace",
284 Resources: []helm.KubernetesResource{
286 GVK: schema.GroupVersionKind{
290 Name: "test-deployment",
293 GVK: schema.GroupVersionKind{
297 Name: "test-service",
306 for _, testCase := range testCases {
307 t.Run(testCase.label, func(t *testing.T) {
308 request := httptest.NewRequest("GET", "/v1/instance/"+testCase.input, nil)
309 resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
311 if testCase.expectedCode != resp.StatusCode {
312 t.Fatalf("Request method returned: %v and it was expected: %v",
313 resp.StatusCode, testCase.expectedCode)
315 if resp.StatusCode == http.StatusOK {
316 var response app.InstanceResponse
317 err := json.NewDecoder(resp.Body).Decode(&response)
319 t.Fatalf("Parsing the returned response got an error (%s)", err)
321 if !reflect.DeepEqual(testCase.expectedResponse, &response) {
322 t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
323 &response, testCase.expectedResponse)
330 func TestInstanceListHandler(t *testing.T) {
331 testCases := []struct {
336 queryParamsMap map[string]string
337 expectedResponse []app.InstanceMiniResponse
338 instClient *mockInstanceClient
341 label: "Fail to List Instance",
343 expectedCode: http.StatusInternalServerError,
344 instClient: &mockInstanceClient{
345 err: pkgerrors.New("Internal error"),
349 label: "Succesful List Instances",
350 expectedCode: http.StatusOK,
351 expectedResponse: []app.InstanceMiniResponse{
354 Request: app.InstanceRequest{
355 RBName: "test-rbdef",
357 ProfileName: "profile1",
358 CloudRegion: "region1",
360 Namespace: "testnamespace",
364 Request: app.InstanceRequest{
365 RBName: "test-rbdef-two",
366 RBVersion: "versionsomething",
367 ProfileName: "profile3",
368 CloudRegion: "region1",
370 Namespace: "testnamespace-two",
373 instClient: &mockInstanceClient{
374 miniitems: []app.InstanceMiniResponse{
377 Request: app.InstanceRequest{
378 RBName: "test-rbdef",
380 ProfileName: "profile1",
381 CloudRegion: "region1",
383 Namespace: "testnamespace",
387 Request: app.InstanceRequest{
388 RBName: "test-rbdef-two",
389 RBVersion: "versionsomething",
390 ProfileName: "profile3",
391 CloudRegion: "region1",
393 Namespace: "testnamespace-two",
399 label: "List Instances Based on Query Parameters",
401 queryParamsMap: map[string]string{
402 "rb-name": "test-rbdef1",
404 expectedCode: http.StatusOK,
405 expectedResponse: []app.InstanceMiniResponse{
408 Request: app.InstanceRequest{
409 RBName: "test-rbdef",
411 ProfileName: "profile1",
412 CloudRegion: "region1",
414 Namespace: "testnamespace",
417 instClient: &mockInstanceClient{
418 miniitems: []app.InstanceMiniResponse{
421 Request: app.InstanceRequest{
422 RBName: "test-rbdef",
424 ProfileName: "profile1",
425 CloudRegion: "region1",
427 Namespace: "testnamespace",
434 for _, testCase := range testCases {
435 t.Run(testCase.label, func(t *testing.T) {
436 request := httptest.NewRequest("GET", "/v1/instance", nil)
437 if testCase.queryParams {
438 q := request.URL.Query()
439 for k, v := range testCase.queryParamsMap {
442 request.URL.RawQuery = q.Encode()
444 resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
446 if testCase.expectedCode != resp.StatusCode {
447 t.Fatalf("Request method returned: %v and it was expected: %v",
448 resp.StatusCode, testCase.expectedCode)
450 if resp.StatusCode == http.StatusOK {
451 var response []app.InstanceMiniResponse
452 err := json.NewDecoder(resp.Body).Decode(&response)
454 t.Fatalf("Parsing the returned response got an error (%s)", err)
457 // Since the order of returned slice is not guaranteed
458 // Sort them first and then do deepequal
459 // Check both and return error if both don't match
460 sort.Slice(response, func(i, j int) bool {
461 return response[i].ID < response[j].ID
464 sort.Slice(testCase.expectedResponse, func(i, j int) bool {
465 return testCase.expectedResponse[i].ID < testCase.expectedResponse[j].ID
468 if reflect.DeepEqual(testCase.expectedResponse, response) == false {
469 t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
470 &response, testCase.expectedResponse)
477 func TestDeleteHandler(t *testing.T) {
478 testCases := []struct {
482 instClient *mockInstanceClient
485 label: "Fail to destroy VNF",
487 expectedCode: http.StatusInternalServerError,
488 instClient: &mockInstanceClient{
489 err: pkgerrors.New("Internal error"),
493 label: "Succesful delete a VNF",
495 expectedCode: http.StatusAccepted,
496 instClient: &mockInstanceClient{},
500 for _, testCase := range testCases {
501 t.Run(testCase.label, func(t *testing.T) {
502 request := httptest.NewRequest("DELETE", "/v1/instance/"+testCase.input, nil)
503 resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
505 if testCase.expectedCode != resp.StatusCode {
506 t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
512 func TestInstanceQueryHandler(t *testing.T) {
513 testCases := []struct {
515 input map[string]string
518 expectedResponse *app.InstanceStatus
519 instClient *mockInstanceClient
522 label: "Missing apiVersion mandatory parameter",
524 input: map[string]string{},
525 expectedCode: http.StatusBadRequest,
526 instClient: &mockInstanceClient{
527 err: pkgerrors.New("Missing apiVersion mandatory parameter"),
531 label: "Missing kind mandatory parameter",
533 input: map[string]string{
536 expectedCode: http.StatusBadRequest,
537 instClient: &mockInstanceClient{
538 err: pkgerrors.New("Missing kind mandatory parameter"),
542 label: "Query instance by name",
544 input: map[string]string{
549 expectedCode: http.StatusOK,
550 expectedResponse: &app.InstanceStatus{
551 Request: app.InstanceRequest{
552 RBName: "test-rbdef",
554 ProfileName: "profile1",
555 CloudRegion: "region1",
558 ResourceCount: int32(1),
559 ResourcesStatus: []app.ResourceStatus{
562 GVK: schema.GroupVersionKind{
567 Status: unstructured.Unstructured{
568 Object: map[string]interface{}{
571 "metadata": map[string]interface{}{
572 "name": string("Test"),
579 instClient: &mockInstanceClient{
580 statusItem: app.InstanceStatus{
581 Request: app.InstanceRequest{
582 RBName: "test-rbdef",
584 ProfileName: "profile1",
585 CloudRegion: "region1",
588 ResourceCount: int32(1),
589 ResourcesStatus: []app.ResourceStatus{
592 GVK: schema.GroupVersionKind{
597 Status: unstructured.Unstructured{
598 Object: map[string]interface{}{
601 "metadata": map[string]interface{}{
602 "name": string("Test"),
612 label: "Query instance by label",
614 input: map[string]string{
617 "Labels": "app=test",
619 expectedCode: http.StatusOK,
620 expectedResponse: &app.InstanceStatus{
621 Request: app.InstanceRequest{
622 RBName: "test-rbdef",
624 ProfileName: "profile1",
625 CloudRegion: "region1",
628 ResourceCount: int32(1),
629 ResourcesStatus: []app.ResourceStatus{
632 GVK: schema.GroupVersionKind{
637 Status: unstructured.Unstructured{
638 Object: map[string]interface{}{
641 "metadata": map[string]interface{}{
642 "name": string("Test-1"),
643 "labels": map[string]interface{}{
644 "app": string("test"),
652 GVK: schema.GroupVersionKind{
657 Status: unstructured.Unstructured{
658 Object: map[string]interface{}{
661 "metadata": map[string]interface{}{
662 "name": string("Test-2"),
663 "labels": map[string]interface{}{
664 "app": string("test"),
672 instClient: &mockInstanceClient{
673 statusItem: app.InstanceStatus{
674 Request: app.InstanceRequest{
675 RBName: "test-rbdef",
677 ProfileName: "profile1",
678 CloudRegion: "region1",
681 ResourceCount: int32(1),
682 ResourcesStatus: []app.ResourceStatus{
685 GVK: schema.GroupVersionKind{
690 Status: unstructured.Unstructured{
691 Object: map[string]interface{}{
694 "metadata": map[string]interface{}{
695 "name": string("Test-1"),
696 "labels": map[string]interface{}{
697 "app": string("test"),
705 GVK: schema.GroupVersionKind{
710 Status: unstructured.Unstructured{
711 Object: map[string]interface{}{
714 "metadata": map[string]interface{}{
715 "name": string("Test-2"),
716 "labels": map[string]interface{}{
717 "app": string("test"),
729 for _, testCase := range testCases {
730 t.Run(testCase.label, func(t *testing.T) {
731 params := neturl.Values{}
732 for k, v := range testCase.input {
735 url := "/v1/instance/" + testCase.id + "/query?" + params.Encode()
736 request := httptest.NewRequest("GET", url, nil)
737 resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
739 if testCase.expectedCode != resp.StatusCode {
740 body, _ := ioutil.ReadAll(resp.Body)
741 t.Fatalf("Request method returned: %v and it was expected: %v\nReturned body: %s",
742 resp.StatusCode, testCase.expectedCode, body)
744 if resp.StatusCode == http.StatusOK {
745 var response app.InstanceStatus
746 err := json.NewDecoder(resp.Body).Decode(&response)
748 t.Fatalf("Parsing the returned response got an error (%s)", err)
750 if !reflect.DeepEqual(testCase.expectedResponse, &response) {
751 t.Fatalf("TestQueryHandler returned:\n result=%v\n expected=%v",
752 &response, testCase.expectedResponse)